2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1994, 1995 Janne Kukonlehto
6 1994, 1995 Fred Leeflang
7 1994, 1995, 1996 Miguel de Icaza
8 1995, 1996 Jakub Jelinek
12 The copy code was based in GNU's cp, and was written by:
13 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
15 The move code was based in GNU's mv, and was written by:
16 Mike Parker and David MacKenzie.
18 Janne Kukonlehto added much error recovery to them for being used
19 in an interactive program.
21 This program is free software; you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation; either version 2 of the License, or
24 (at your option) any later version.
26 This program is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
31 You should have received a copy of the GNU General Public License
32 along with this program; if not, write to the Free Software
33 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
36 * Please note that all dialogs used here must be safe for background
41 * \brief Source: file management
44 /* {{{ Include files */
53 #include <sys/types.h>
58 #include "lib/global.h"
59 #include "lib/tty/tty.h"
60 #include "lib/tty/key.h"
61 #include "lib/search.h"
62 #include "lib/strescape.h"
63 #include "lib/strutil.h"
65 #include "lib/vfs/mc-vfs/vfs.h"
66 #include "lib/widget.h"
68 #include "src/setup.h"
69 #include "src/background.h" /* we_are_background */
71 #include "layout.h" /* rotate_dash() */
73 /* Needed for current_panel, other_panel and WTree */
77 #include "midnight.h" /* current_panel */
83 /*** global variables ****************************************************************************/
85 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
86 const char *op_names
[3] = {
87 N_("DialogTitle|Copy"),
88 N_("DialogTitle|Move"),
89 N_("DialogTitle|Delete")
92 /*** file scope macro definitions ****************************************************************/
94 /* Hack: the vfs code should not rely on this */
95 #define WITH_FULL_PATHS 1
97 #define FILEOP_UPDATE_INTERVAL 2
98 #define FILEOP_STALLING_INTERVAL 4
100 /*** file scope type declarations ****************************************************************/
102 /* This is a hard link cache */
106 struct vfs_class
*vfs
;
114 /* Status of the destination file */
117 DEST_NONE
= 0, /* Not created */
118 DEST_SHORT
= 1, /* Created, not fully copied */
119 DEST_FULL
= 2 /* Created, fully copied */
123 * This array introduced to avoid translation problems. The former (op_names)
124 * is assumed to be nouns, suitable in dialog box titles; this one should
125 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
126 * (I don't use spaces around the words, because someday they could be
127 * dropped, when widgets get smarter)
130 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
131 static const char *op_names1
[] = {
132 N_("FileOperation|Copy"),
133 N_("FileOperation|Move"),
134 N_("FileOperation|Delete")
138 * These are formats for building a prompt. Parts encoded as follows:
139 * %o - operation from op_names1
140 * %f - file/files or files/directories, as appropriate
141 * %m - "with source mask" or question mark for delete
142 * %s - source name (truncated)
143 * %d - number of marked files
144 * %e - "to:" or question mark for delete
146 * xgettext:no-c-format */
147 static const char *one_format
= N_("%o %f \"%s\"%m");
148 /* xgettext:no-c-format */
149 static const char *many_format
= N_("%o %d %f%m");
151 static const char *prompt_parts
[] = {
156 N_("files/directories"),
157 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
158 N_(" with source mask:"),
162 static const char *question_format
= N_("%s?");
164 /*** file scope variables ************************************************************************/
166 /* the hard link cache */
167 static struct link
*linklist
= NULL
;
169 /* the files-to-be-erased list */
170 static struct link
*erase_list
;
173 * In copy_dir_dir we use two additional single linked lists: The first -
174 * variable name `parent_dirs' - holds information about already copied
175 * directories and is used to detect cyclic symbolic links.
176 * The second (`dest_dirs' below) holds information about just created
177 * target directories and is used to detect when an directory is copied
178 * into itself (we don't want to copy infinitly).
179 * Both lists don't use the linkcount and name structure members of struct
182 static struct link
*dest_dirs
= NULL
;
184 static FileProgressStatus transform_error
= FILE_CONT
;
186 /*** file scope functions ************************************************************************/
187 /* --------------------------------------------------------------------------------------------- */
190 transform_source (FileOpContext
* ctx
, const char *source
)
195 s
= g_strdup (source
);
197 /* We remove \n from the filename since regex routines would use \n as an anchor */
198 /* this is just to be allowed to maniupulate file names with \n on it */
199 for (q
= s
; *q
!= '\0'; q
++)
203 fnsource
= (char *) x_basename (s
);
205 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
206 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
210 transform_error
= FILE_SKIP
;
217 /* --------------------------------------------------------------------------------------------- */
220 free_linklist (struct link
**lc_linklist
)
222 struct link
*lp
, *lp2
;
224 for (lp
= *lc_linklist
; lp
!= NULL
; lp
= lp2
)
232 /* --------------------------------------------------------------------------------------------- */
235 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
237 ino_t ino
= sb
->st_ino
;
238 dev_t dev
= sb
->st_dev
;
239 struct vfs_class
*vfs
= vfs_get_class (path
);
244 if (lp
->ino
== ino
&& lp
->dev
== dev
)
251 /* --------------------------------------------------------------------------------------------- */
253 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
254 * and a hardlink was succesfully made
258 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
261 struct vfs_class
*my_vfs
= vfs_get_class (src_name
);
262 ino_t ino
= pstat
->st_ino
;
263 dev_t dev
= pstat
->st_dev
;
264 struct stat link_stat
;
267 if ((vfs_file_class_flags (src_name
) & VFSF_NOLINKS
) != 0)
270 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
271 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
)
273 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
274 && link_stat
.st_dev
== dev
&& vfs_get_class (lp
->name
) == my_vfs
)
276 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
278 if (vfs_get_class (dst_name
) == vfs_get_class (p
))
280 if (!mc_stat (p
, &link_stat
))
282 if (!mc_link (p
, dst_name
))
287 message (D_ERROR
, MSG_ERROR
, _("Cannot make the hardlink"));
290 lp
= (struct link
*) g_try_malloc (sizeof (struct link
) + strlen (src_name
)
291 + strlen (dst_name
) + 1);
298 strcpy (lp
->name
, src_name
);
299 lpdstname
= lp
->name
+ strlen (lp
->name
) + 1;
300 strcpy (lpdstname
, dst_name
);
307 /* --------------------------------------------------------------------------------------------- */
309 * Duplicate the contents of the symbolic link src_path in dst_path.
310 * Try to make a stable symlink if the option "stable symlink" was
311 * set in the file mask dialog.
312 * If dst_path is an existing symlink it will be deleted silently
313 * (upper levels take already care of existing files at dst_path).
316 static FileProgressStatus
317 make_symlink (FileOpContext
* ctx
, const char *src_path
, const char *dst_path
)
319 char link_target
[MC_MAXPATHLEN
];
321 FileProgressStatus return_status
;
323 gboolean dst_is_symlink
;
325 dst_is_symlink
= (mc_lstat (dst_path
, &sb
) == 0) && S_ISLNK (sb
.st_mode
);
328 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
- 1);
331 return_status
= file_error (_("Cannot read source link \"%s\"\n%s"), src_path
);
332 if (return_status
== FILE_RETRY
)
333 goto retry_src_readlink
;
334 return return_status
;
336 link_target
[len
] = 0;
338 if (ctx
->stable_symlinks
)
339 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
))
341 message (D_ERROR
, MSG_ERROR
,
342 _("Cannot make stable symlinks across"
343 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
344 ctx
->stable_symlinks
= FALSE
;
347 if (ctx
->stable_symlinks
&& !g_path_is_absolute (link_target
))
351 const char *r
= strrchr (src_path
, PATH_SEP
);
355 p
= g_strndup (src_path
, r
- src_path
+ 1);
356 if (g_path_is_absolute (dst_path
))
357 q
= g_strdup (dst_path
);
359 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
360 s
= strrchr (q
, PATH_SEP
);
364 s
= g_strconcat (p
, link_target
, (char *) NULL
);
366 g_strlcpy (link_target
, s
, sizeof (link_target
));
368 s
= diff_two_paths (q
, link_target
);
371 g_strlcpy (link_target
, s
, sizeof (link_target
));
381 if (mc_symlink (link_target
, dst_path
) == 0)
385 * if dst_exists, it is obvious that this had failed.
386 * We can delete the old symlink and try again...
390 if (!mc_unlink (dst_path
))
391 if (mc_symlink (link_target
, dst_path
) == 0)
395 return_status
= file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path
);
396 if (return_status
== FILE_RETRY
)
397 goto retry_dst_symlink
;
398 return return_status
;
401 /* --------------------------------------------------------------------------------------------- */
403 static FileProgressStatus
404 progress_update_one (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, off_t add
,
405 gboolean is_toplevel_file
)
407 struct timeval tv_current
;
408 static struct timeval tv_start
= { };
410 if (is_toplevel_file
|| ctx
->progress_totals_computed
)
412 tctx
->progress_count
++;
413 tctx
->progress_bytes
+= (uintmax_t) add
;
415 if (tv_start
.tv_sec
== 0)
417 gettimeofday (&tv_start
, (struct timezone
*) NULL
);
419 gettimeofday (&tv_current
, (struct timezone
*) NULL
);
420 if ((tv_current
.tv_sec
- tv_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
)
422 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
424 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
425 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, TRUE
);
427 tv_start
.tv_sec
= tv_current
.tv_sec
;
430 return check_progress_buttons (ctx
);
433 /* --------------------------------------------------------------------------------------------- */
435 static FileProgressStatus
436 real_warn_same_file (enum OperationMode mode
, const char *fmt
, const char *a
, const char *b
)
440 const char *head_msg
;
442 head_msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
444 msg
= g_strdup_printf (fmt
, a
, b
);
445 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
449 return (result
== 1) ? FILE_ABORT
: FILE_SKIP
;
452 /* --------------------------------------------------------------------------------------------- */
454 static FileProgressStatus
455 warn_same_file (const char *fmt
, const char *a
, const char *b
)
457 #ifdef WITH_BACKGROUND
461 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
462 const char *a
, const char *b
);
465 pntr
.f
= real_warn_same_file
;
467 if (we_are_background
)
468 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
), fmt
, strlen (a
), a
, strlen (b
), b
);
470 return real_warn_same_file (Foreground
, fmt
, a
, b
);
473 /* --------------------------------------------------------------------------------------------- */
474 /* {{{ Query/status report routines */
476 static FileProgressStatus
477 real_do_file_error (enum OperationMode mode
, const char *error
)
482 msg
= mode
== Foreground
? MSG_ERROR
: _("Background process error");
483 result
= query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"), _("&Abort"));
501 /* --------------------------------------------------------------------------------------------- */
503 static FileProgressStatus
504 real_query_recursive (FileOpContext
* ctx
, enum OperationMode mode
, const char *s
)
508 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
)
510 const char *msg
= mode
== Foreground
511 ? _("\nDirectory not empty.\nDelete it recursively?")
512 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
513 text
= g_strconcat (_("Delete:"), " ", path_trunc (s
, 30), (char *) NULL
);
518 ctx
->recursive_result
=
519 (FileCopyMode
) query_dialog (text
, msg
, D_ERROR
, 5,
520 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
522 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
527 switch (ctx
->recursive_result
)
530 case RECURSIVE_ALWAYS
:
534 case RECURSIVE_NEVER
:
537 case RECURSIVE_ABORT
:
543 /* --------------------------------------------------------------------------------------------- */
545 #ifdef WITH_BACKGROUND
546 static FileProgressStatus
547 do_file_error (const char *str
)
552 FileProgressStatus (*f
) (enum OperationMode
, const char *);
554 pntr
.f
= real_do_file_error
;
556 if (we_are_background
)
557 return parent_call (pntr
.p
, NULL
, 1, strlen (str
), str
);
559 return real_do_file_error (Foreground
, str
);
562 /* --------------------------------------------------------------------------------------------- */
564 static FileProgressStatus
565 query_recursive (FileOpContext
* ctx
, const char *s
)
570 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
572 pntr
.f
= real_query_recursive
;
574 if (we_are_background
)
575 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
577 return real_query_recursive (ctx
, Foreground
, s
);
580 /* --------------------------------------------------------------------------------------------- */
582 static FileProgressStatus
583 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
584 struct stat
*_d_stat
)
589 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
590 struct stat
*, struct stat
*);
592 pntr
.f
= file_progress_real_query_replace
;
594 if (we_are_background
)
595 return parent_call (pntr
.p
, ctx
, 3, strlen (destname
), destname
,
596 sizeof (struct stat
), _s_stat
, sizeof (struct stat
), _d_stat
);
598 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
602 /* --------------------------------------------------------------------------------------------- */
604 static FileProgressStatus
605 do_file_error (const char *str
)
607 return real_do_file_error (Foreground
, str
);
610 /* --------------------------------------------------------------------------------------------- */
612 static FileProgressStatus
613 query_recursive (FileOpContext
* ctx
, const char *s
)
615 return real_query_recursive (ctx
, Foreground
, s
);
618 /* --------------------------------------------------------------------------------------------- */
620 static FileProgressStatus
621 query_replace (FileOpContext
* ctx
, const char *destname
, struct stat
*_s_stat
,
622 struct stat
*_d_stat
)
624 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
627 #endif /* !WITH_BACKGROUND */
629 /* --------------------------------------------------------------------------------------------- */
630 /** Report error with two files */
632 static FileProgressStatus
633 files_error (const char *format
, const char *file1
, const char *file2
)
635 char buf
[BUF_MEDIUM
];
636 char *nfile1
= g_strdup (path_trunc (file1
, 15));
637 char *nfile2
= g_strdup (path_trunc (file2
, 15));
639 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
, unix_error_string (errno
));
644 return do_file_error (buf
);
647 /* --------------------------------------------------------------------------------------------- */
650 copy_file_file_display_progress (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
651 struct timeval tv_current
, struct timeval tv_transfer_start
,
652 off_t file_size
, off_t n_read_total
)
656 /* 1. Update rotating dash after some time */
660 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
664 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
665 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
670 /* 4. Compute BPS rate */
671 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
672 if (ctx
->bps_time
< 1)
674 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
676 /* 5. Compute total ETA and BPS */
677 if (ctx
->progress_bytes
!= 0)
679 uintmax_t remain_bytes
;
680 tctx
->copyed_bytes
= tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
;
681 remain_bytes
= ctx
->progress_bytes
- tctx
->copyed_bytes
;
684 int total_secs
= tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
;
689 tctx
->bps
= tctx
->copyed_bytes
/ total_secs
;
690 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
693 /* broken on lot of little files */
695 tctx
->bps
= (tctx
->bps
* (tctx
->bps_count
- 1) + ctx
->bps
) / tctx
->bps_count
;
696 tctx
->eta_secs
= (tctx
->bps
!= 0) ? remain_bytes
/ tctx
->bps
: 0;
701 /* --------------------------------------------------------------------------------------------- */
703 /* {{{ Move routines */
704 static FileProgressStatus
705 move_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
707 struct stat src_stats
, dst_stats
;
708 FileProgressStatus return_status
= FILE_CONT
;
709 gboolean copy_done
= FALSE
;
710 gboolean old_ask_overwrite
;
712 file_progress_show_source (ctx
, s
);
713 file_progress_show_target (ctx
, d
);
714 if (check_progress_buttons (ctx
) == FILE_ABORT
)
719 while (mc_lstat (s
, &src_stats
) != 0)
721 /* Source doesn't exist */
722 return_status
= file_error (_("Cannot stat file \"%s\"\n%s"), s
);
723 if (return_status
!= FILE_RETRY
)
724 return return_status
;
727 if (mc_lstat (d
, &dst_stats
) == 0)
729 if (src_stats
.st_dev
== dst_stats
.st_dev
&& src_stats
.st_ino
== dst_stats
.st_ino
)
730 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s
, d
);
732 if (S_ISDIR (dst_stats
.st_mode
))
734 message (D_ERROR
, MSG_ERROR
, _("Cannot overwrite directory \"%s\""), d
);
739 if (confirm_overwrite
)
741 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
742 if (return_status
!= FILE_CONT
)
743 return return_status
;
745 /* Ok to overwrite */
750 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
)
752 return_status
= make_symlink (ctx
, s
, d
);
753 if (return_status
== FILE_CONT
)
754 goto retry_src_remove
;
756 return return_status
;
759 if (mc_rename (s
, d
) == 0)
760 return progress_update_one (tctx
, ctx
, src_stats
.st_size
, TRUE
);
763 /* Comparison to EXDEV seems not to work in nfs if you're moving from
764 one nfs to the same, but on the server it is on two different
765 filesystems. Then nfs returns EIO instead of EXDEV.
766 Hope it will not hurt if we always in case of error try to copy/delete. */
768 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
772 return_status
= files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s
, d
);
773 if (return_status
== FILE_RETRY
)
775 return return_status
;
779 /* Failed because filesystem boundary -> copy the file instead */
780 old_ask_overwrite
= tctx
->ask_overwrite
;
781 tctx
->ask_overwrite
= FALSE
;
782 return_status
= copy_file_file (tctx
, ctx
, s
, d
);
783 tctx
->ask_overwrite
= old_ask_overwrite
;
784 if (return_status
!= FILE_CONT
)
785 return return_status
;
789 file_progress_show_source (ctx
, NULL
);
790 file_progress_show (ctx
, 0, 0, "", FALSE
);
792 return_status
= check_progress_buttons (ctx
);
793 if (return_status
!= FILE_CONT
)
794 return return_status
;
801 return_status
= file_error (_("Cannot remove file \"%s\"\n%s"), s
);
802 if (return_status
== FILE_RETRY
)
803 goto retry_src_remove
;
804 return return_status
;
808 return_status
= progress_update_one (tctx
, ctx
, src_stats
.st_size
, TRUE
);
810 return return_status
;
815 /* --------------------------------------------------------------------------------------------- */
816 /* {{{ Erase routines */
817 /** Don't update progress status if progress_count==NULL */
819 static FileProgressStatus
820 erase_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
,
821 gboolean is_toplevel_file
)
826 file_progress_show_deleting (ctx
, s
);
827 if (check_progress_buttons (ctx
) == FILE_ABORT
)
831 if (tctx
->progress_count
!= 0 && mc_lstat (s
, &buf
) != 0)
833 /* ignore, most likely the mc_unlink fails, too */
837 while (mc_unlink (s
) != 0)
839 return_status
= file_error (_("Cannot delete file \"%s\"\n%s"), s
);
840 if (return_status
!= FILE_RETRY
)
841 return return_status
;
844 if (tctx
->progress_count
== 0)
846 return progress_update_one (tctx
, ctx
, buf
.st_size
, is_toplevel_file
);
849 /* --------------------------------------------------------------------------------------------- */
851 static FileProgressStatus
852 recursive_erase (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
858 FileProgressStatus return_status
= FILE_CONT
;
860 if (!strcmp (s
, ".."))
863 reading
= mc_opendir (s
);
868 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
)
870 if (!strcmp (next
->d_name
, "."))
872 if (!strcmp (next
->d_name
, ".."))
874 path
= concat_dir_and_file (s
, next
->d_name
);
875 if (mc_lstat (path
, &buf
))
878 mc_closedir (reading
);
881 if (S_ISDIR (buf
.st_mode
))
883 (recursive_erase (tctx
, ctx
, path
) != FILE_CONT
) ? FILE_RETRY
: FILE_CONT
;
885 return_status
= erase_file (tctx
, ctx
, path
, 0);
888 mc_closedir (reading
);
889 if (return_status
!= FILE_CONT
)
890 return return_status
;
891 file_progress_show_deleting (ctx
, s
);
892 if (check_progress_buttons (ctx
) == FILE_ABORT
)
898 return_status
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
899 if (return_status
!= FILE_RETRY
)
900 return return_status
;
906 /* --------------------------------------------------------------------------------------------- */
907 /** Return -1 on error, 1 if there are no entries besides "." and ".."
908 in the directory path points to, 0 else. */
911 check_dir_is_empty (const char *path
)
917 dir
= mc_opendir (path
);
921 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
))
923 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
924 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
925 continue; /* "." or ".." */
934 /* --------------------------------------------------------------------------------------------- */
936 static FileProgressStatus
937 erase_dir_iff_empty (FileOpContext
* ctx
, const char *s
)
939 FileProgressStatus error
;
941 if (strcmp (s
, "..") == 0)
944 if (strcmp (s
, ".") == 0)
947 file_progress_show_deleting (ctx
, s
);
948 if (check_progress_buttons (ctx
) == FILE_ABORT
)
952 if (1 != check_dir_is_empty (s
)) /* not empty or error */
957 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
958 if (error
!= FILE_RETRY
)
967 /* --------------------------------------------------------------------------------------------- */
968 /* {{{ Panel operate routines */
971 * Return currently selected entry name or the name of the first marked
972 * entry if there is one.
976 panel_get_file (WPanel
* panel
, struct stat
*stat_buf
)
980 if (get_current_type () == view_tree
)
982 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
983 char *tree_name
= tree_selected_name (tree
);
985 mc_stat (tree_name
, stat_buf
);
991 for (i
= 0; i
< panel
->count
; i
++)
992 if (panel
->dir
.list
[i
].f
.marked
)
994 *stat_buf
= panel
->dir
.list
[i
].st
;
995 return panel
->dir
.list
[i
].fname
;
1000 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1001 return panel
->dir
.list
[panel
->selected
].fname
;
1003 g_assert_not_reached ();
1007 /* --------------------------------------------------------------------------------------------- */
1009 * panel_compute_totals:
1011 * compute the number of files and the number of bytes
1012 * used up by the whole selection, recursing directories
1013 * as required. In addition, it checks to see if it will
1014 * overwrite any files by doing the copy.
1017 static FileProgressStatus
1018 panel_compute_totals (const WPanel
* panel
, const void *ui
,
1019 compute_dir_size_callback cback
,
1020 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
1027 for (i
= 0; i
< panel
->count
; i
++)
1031 if (!panel
->dir
.list
[i
].f
.marked
)
1034 s
= &panel
->dir
.list
[i
].st
;
1036 if (S_ISDIR (s
->st_mode
))
1039 size_t subdir_count
= 0;
1040 uintmax_t subdir_bytes
= 0;
1041 FileProgressStatus status
;
1043 dir_name
= concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1045 status
= compute_dir_size (dir_name
, ui
, cback
,
1046 &subdir_count
, &subdir_bytes
, compute_symlinks
);
1049 if (status
!= FILE_CONT
)
1052 *ret_marked
+= subdir_count
;
1053 *ret_total
+= subdir_bytes
;
1058 *ret_total
+= (uintmax_t) s
->st_size
;
1065 /* --------------------------------------------------------------------------------------------- */
1066 /** Initialize variables for progress bars */
1067 static FileProgressStatus
1068 panel_operate_init_totals (FileOperation operation
,
1069 const WPanel
* panel
, const char *source
, FileOpContext
* ctx
)
1071 FileProgressStatus status
;
1073 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
)
1075 ComputeDirSizeUI
*ui
;
1077 ui
= compute_dir_size_create_ui ();
1080 status
= compute_dir_size (source
, ui
, compute_dir_size_update_ui
,
1081 &ctx
->progress_count
, &ctx
->progress_bytes
,
1084 status
= panel_compute_totals (panel
, ui
, compute_dir_size_update_ui
,
1085 &ctx
->progress_count
, &ctx
->progress_bytes
,
1088 compute_dir_size_destroy_ui (ui
);
1090 ctx
->progress_totals_computed
= (status
== FILE_CONT
);
1095 ctx
->progress_count
= panel
->marked
;
1096 ctx
->progress_bytes
= panel
->total
;
1097 ctx
->progress_totals_computed
= FALSE
;
1103 /* --------------------------------------------------------------------------------------------- */
1105 * Generate user prompt for panel operation.
1106 * single_source is the name if the source entry or NULL for multiple
1108 * src_stat is only used when single_source is not NULL.
1112 panel_operate_generate_prompt (const WPanel
* panel
, FileOperation operation
,
1113 gboolean single_source
, const struct stat
*src_stat
)
1115 const char *sp
, *cp
;
1116 char format_string
[BUF_MEDIUM
];
1117 char *dp
= format_string
;
1118 gboolean build_question
= FALSE
;
1120 static gboolean i18n_flag
= FALSE
;
1125 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1126 op_names1
[i
] = Q_ (op_names1
[i
]);
1129 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1130 prompt_parts
[i
] = _(prompt_parts
[i
]);
1132 one_format
= _(one_format
);
1133 many_format
= _(many_format
);
1134 question_format
= _(question_format
);
1135 #endif /* ENABLE_NLS */
1139 sp
= single_source
? one_format
: many_format
;
1150 cp
= op_names1
[operation
];
1153 if (operation
== OP_DELETE
)
1156 build_question
= TRUE
;
1159 cp
= prompt_parts
[5];
1162 if (operation
== OP_DELETE
)
1165 build_question
= TRUE
;
1168 cp
= prompt_parts
[6];
1172 cp
= S_ISDIR (src_stat
->st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1174 cp
= (panel
->marked
== panel
->dirs_marked
)
1176 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1197 char tmp
[BUF_MEDIUM
];
1199 memmove (tmp
, format_string
, sizeof (tmp
));
1200 g_snprintf (format_string
, sizeof (format_string
), question_format
, tmp
);
1203 return g_strdup (format_string
);
1206 /* --------------------------------------------------------------------------------------------- */
1208 #ifdef WITH_BACKGROUND
1210 end_bg_process (FileOpContext
* ctx
, enum OperationMode mode
)
1217 unregister_task_with_pid (pid
);
1218 /* file_op_context_destroy(ctx); */
1223 /* --------------------------------------------------------------------------------------------- */
1224 /*** public functions ****************************************************************************/
1225 /* --------------------------------------------------------------------------------------------- */
1228 copy_file_file (FileOpTotalContext
* tctx
, FileOpContext
* ctx
,
1229 const char *src_path
, const char *dst_path
)
1231 uid_t src_uid
= (uid_t
) - 1;
1232 gid_t src_gid
= (gid_t
) - 1;
1234 int src_desc
, dest_desc
= -1;
1235 int n_read
, n_written
;
1236 mode_t src_mode
= 0; /* The mode of the source file */
1237 struct stat sb
, sb2
;
1239 gboolean dst_exists
= FALSE
, appending
= FALSE
;
1240 off_t n_read_total
= 0, file_size
= -1;
1241 FileProgressStatus return_status
, temp_status
;
1242 struct timeval tv_transfer_start
;
1243 dest_status_t dst_status
= DEST_NONE
;
1245 gboolean is_first_time
= TRUE
;
1247 /* FIXME: We should not be using global variables! */
1249 return_status
= FILE_RETRY
;
1251 file_progress_show_source (ctx
, src_path
);
1252 file_progress_show_target (ctx
, dst_path
);
1253 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1258 while (mc_stat (dst_path
, &sb2
) == 0)
1260 if (S_ISDIR (sb2
.st_mode
))
1262 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path
);
1263 if (return_status
== FILE_RETRY
)
1265 return return_status
;
1271 while ((*ctx
->stat_func
) (src_path
, &sb
))
1273 return_status
= file_error (_("Cannot stat source file \"%s\"\n%s"), src_path
);
1274 if (return_status
!= FILE_RETRY
)
1275 return return_status
;
1280 /* Destination already exists */
1281 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
1282 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), src_path
, dst_path
);
1283 /* Should we replace destination? */
1284 if (tctx
->ask_overwrite
)
1287 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
1288 if (return_status
!= FILE_CONT
)
1289 return return_status
;
1293 if (!ctx
->do_append
)
1295 /* Check the hardlinks */
1296 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 && check_hardlinks (src_path
, dst_path
, &sb
) == 1)
1298 /* We have made a hardlink - no more processing is necessary */
1302 if (S_ISLNK (sb
.st_mode
))
1303 return make_symlink (ctx
, src_path
, dst_path
);
1305 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
1306 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) || S_ISSOCK (sb
.st_mode
))
1308 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0)
1310 return_status
= file_error (_("Cannot create special file \"%s\"\n%s"), dst_path
);
1311 if (return_status
== FILE_RETRY
)
1313 return return_status
;
1317 while (ctx
->preserve_uidgid
&& mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
))
1319 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1320 if (temp_status
== FILE_RETRY
)
1324 while (ctx
->preserve
&& mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
))
1326 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1327 if (temp_status
== FILE_RETRY
)
1335 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
1337 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0)
1339 return_status
= file_error (_("Cannot open source file \"%s\"\n%s"), src_path
);
1340 if (return_status
== FILE_RETRY
)
1343 return return_status
;
1346 if (ctx
->do_reget
!= 0)
1348 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
)
1350 message (D_ERROR
, _("Warning"), _("Reget failed, about to overwrite file"));
1352 ctx
->do_append
= FALSE
;
1356 while (mc_fstat (src_desc
, &sb
))
1358 return_status
= file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path
);
1359 if (return_status
== FILE_RETRY
)
1361 ctx
->do_append
= FALSE
;
1364 src_mode
= sb
.st_mode
;
1365 src_uid
= sb
.st_uid
;
1366 src_gid
= sb
.st_gid
;
1367 utb
.actime
= sb
.st_atime
;
1368 utb
.modtime
= sb
.st_mtime
;
1369 file_size
= sb
.st_size
;
1371 open_flags
= O_WRONLY
;
1374 if (ctx
->do_append
!= 0)
1375 open_flags
|= O_APPEND
;
1377 open_flags
|= O_CREAT
| O_TRUNC
;
1381 open_flags
|= O_CREAT
| O_EXCL
;
1384 while ((dest_desc
= mc_open (dst_path
, open_flags
, src_mode
)) < 0)
1386 if (errno
== EEXIST
)
1389 return_status
= file_error (_("Cannot create target file \"%s\"\n%s"), dst_path
);
1390 if (return_status
== FILE_RETRY
)
1392 ctx
->do_append
= FALSE
;
1395 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
1397 appending
= ctx
->do_append
;
1398 ctx
->do_append
= FALSE
;
1400 /* Find out the optimal buffer size. */
1401 while (mc_fstat (dest_desc
, &sb
))
1403 return_status
= file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path
);
1404 if (return_status
== FILE_RETRY
)
1409 ctx
->eta_secs
= 0.0;
1412 if (tctx
->bps
== 0 || (file_size
/ (tctx
->bps
)) > FILEOP_UPDATE_INTERVAL
)
1413 file_progress_show (ctx
, 0, file_size
, "", TRUE
);
1415 file_progress_show (ctx
, 1, 1, "", TRUE
);
1416 return_status
= check_progress_buttons (ctx
);
1419 if (return_status
!= FILE_CONT
)
1423 struct timeval tv_current
, tv_last_update
, tv_last_input
;
1424 int secs
, update_secs
;
1425 const char *stalled_msg
= "";
1427 tv_last_update
= tv_transfer_start
;
1434 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
1437 while ((n_read
= mc_read (src_desc
, buf
, sizeof (buf
))) < 0)
1439 return_status
= file_error (_("Cannot read source file\"%s\"\n%s"), src_path
);
1440 if (return_status
== FILE_RETRY
)
1447 gettimeofday (&tv_current
, NULL
);
1452 n_read_total
+= n_read
;
1454 /* Windows NT ftp servers report that files have no
1455 * permissions: -------, so if we happen to have actually
1456 * read something, we should fix the permissions.
1458 if ((src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)) == 0)
1459 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1460 gettimeofday (&tv_last_input
, NULL
);
1463 while ((n_written
= mc_write (dest_desc
, t
, n_read
)) < n_read
)
1467 n_read
-= n_written
;
1471 return_status
= file_error (_("Cannot write target file \"%s\"\n%s"), dst_path
);
1472 if (return_status
!= FILE_RETRY
)
1476 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
1477 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
1479 if (is_first_time
|| secs
> FILEOP_UPDATE_INTERVAL
)
1481 copy_file_file_display_progress (tctx
, ctx
,
1483 tv_transfer_start
, file_size
, n_read_total
);
1484 tv_last_update
= tv_current
;
1486 is_first_time
= FALSE
;
1488 if (update_secs
> FILEOP_STALLING_INTERVAL
)
1490 stalled_msg
= _("(stalled)");
1494 gboolean force_update
;
1497 (tv_current
.tv_sec
- tctx
->transfer_start
.tv_sec
) > FILEOP_UPDATE_INTERVAL
;
1499 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
1501 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
1502 file_progress_show_total (tctx
, ctx
,
1503 tctx
->progress_bytes
+ n_read_total
+ ctx
->do_reget
,
1507 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
, stalled_msg
,
1512 return_status
= check_progress_buttons (ctx
);
1514 if (return_status
!= FILE_CONT
)
1522 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
1525 while (src_desc
!= -1 && mc_close (src_desc
) < 0)
1527 temp_status
= file_error (_("Cannot close source file \"%s\"\n%s"), src_path
);
1528 if (temp_status
== FILE_RETRY
)
1530 if (temp_status
== FILE_ABORT
)
1531 return_status
= temp_status
;
1535 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0)
1537 temp_status
= file_error (_("Cannot close target file \"%s\"\n%s"), dst_path
);
1538 if (temp_status
== FILE_RETRY
)
1540 return_status
= temp_status
;
1544 if (dst_status
== DEST_SHORT
)
1546 /* Remove short file */
1548 result
= query_dialog (Q_ ("DialogTitle|Copy"),
1549 _("Incomplete file was retrieved. Keep it?"),
1550 D_ERROR
, 2, _("&Delete"), _("&Keep"));
1552 mc_unlink (dst_path
);
1554 else if (dst_status
== DEST_FULL
)
1556 /* Copy has succeeded */
1557 if (!appending
&& ctx
->preserve_uidgid
)
1559 while (mc_chown (dst_path
, src_uid
, src_gid
))
1561 temp_status
= file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path
);
1562 if (temp_status
== FILE_RETRY
)
1564 return_status
= temp_status
;
1573 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
)))
1575 temp_status
= file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path
);
1576 if (temp_status
!= FILE_RETRY
)
1578 return_status
= temp_status
;
1585 src_mode
= umask (-1);
1587 src_mode
= 0100666 & ~src_mode
;
1588 mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
));
1590 mc_utime (dst_path
, &utb
);
1594 if (return_status
== FILE_CONT
)
1595 return_status
= progress_update_one (tctx
, ctx
, file_size
, tctx
->is_toplevel_file
);
1597 return return_status
;
1600 /* --------------------------------------------------------------------------------------------- */
1602 * I think these copy_*_* functions should have a return type.
1603 * anyway, this function *must* have two directories as arguments.
1605 /* FIXME: This function needs to check the return values of the
1609 copy_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *_d
,
1610 gboolean toplevel
, gboolean move_over
, gboolean do_delete
, struct link
* parent_dirs
)
1612 struct dirent
*next
;
1613 struct stat buf
, cbuf
;
1615 char *dest_dir
= NULL
;
1616 FileProgressStatus return_status
= FILE_CONT
;
1623 /* First get the mode of the source dir */
1625 if ((*ctx
->stat_func
) (s
, &cbuf
))
1627 return_status
= file_error (_("Cannot stat source directory \"%s\"\n%s"), s
);
1628 if (return_status
== FILE_RETRY
)
1629 goto retry_src_stat
;
1633 if (is_in_linklist (dest_dirs
, s
, &cbuf
))
1635 /* Don't copy a directory we created before (we don't want to copy
1636 infinitely if a directory is copied into itself) */
1637 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1638 return_status
= FILE_CONT
;
1642 /* Hmm, hardlink to directory??? - Norbert */
1643 /* FIXME: In this step we should do something
1644 in case the destination already exist */
1645 /* Check the hardlinks */
1646 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (s
, d
, &cbuf
) == 1)
1648 /* We have made a hardlink - no more processing is necessary */
1652 if (!S_ISDIR (cbuf
.st_mode
))
1654 return_status
= file_error (_("Source \"%s\" is not a directory\n%s"), s
);
1655 if (return_status
== FILE_RETRY
)
1656 goto retry_src_stat
;
1660 if (is_in_linklist (parent_dirs
, s
, &cbuf
))
1662 /* we found a cyclic symbolic link */
1663 message (D_ERROR
, MSG_ERROR
, _("Cannot copy cyclic symbolic link\n\"%s\""), s
);
1664 return_status
= FILE_SKIP
;
1668 lp
= g_new (struct link
, 1);
1669 lp
->vfs
= vfs_get_class (s
);
1670 lp
->ino
= cbuf
.st_ino
;
1671 lp
->dev
= cbuf
.st_dev
;
1672 lp
->next
= parent_dirs
;
1676 /* Now, check if the dest dir exists, if not, create it. */
1677 if (mc_stat (d
, &buf
))
1679 /* Here the dir doesn't exist : make it ! */
1682 if (mc_rename (s
, d
) == 0)
1684 return_status
= FILE_CONT
;
1694 * If the destination directory exists, we want to copy the whole
1695 * directory, but we only want this to happen once.
1697 * Escape sequences added to the * to compiler warnings.
1698 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
1699 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
1701 if (!S_ISDIR (buf
.st_mode
))
1703 return_status
= file_error (_("Destination \"%s\" must be a directory\n%s"), d
);
1704 if (return_status
== FILE_RETRY
)
1705 goto retry_dst_stat
;
1708 /* Dive into subdir if exists */
1709 if (toplevel
&& ctx
->dive_into_subdirs
)
1711 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
1720 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
))
1722 return_status
= file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir
);
1723 if (return_status
!= FILE_RETRY
)
1727 lp
= g_new (struct link
, 1);
1728 mc_stat (dest_dir
, &buf
);
1729 lp
->vfs
= vfs_get_class (dest_dir
);
1730 lp
->ino
= buf
.st_ino
;
1731 lp
->dev
= buf
.st_dev
;
1732 lp
->next
= dest_dirs
;
1735 if (ctx
->preserve_uidgid
)
1737 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
))
1739 return_status
= file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir
);
1740 if (return_status
!= FILE_RETRY
)
1746 /* open the source dir for reading */
1747 reading
= mc_opendir (s
);
1748 if (reading
== NULL
)
1751 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
)
1755 * Now, we don't want '.' and '..' to be created / copied at any time
1757 if (!strcmp (next
->d_name
, "."))
1759 if (!strcmp (next
->d_name
, ".."))
1762 /* get the filename and add it to the src directory */
1763 path
= concat_dir_and_file (s
, next
->d_name
);
1765 (*ctx
->stat_func
) (path
, &buf
);
1766 if (S_ISDIR (buf
.st_mode
))
1770 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
1772 * From here, we just intend to recursively copy subdirs, not
1773 * the double functionality of copying different when the target
1774 * dir already exists. So, we give the recursive call the flag 0
1775 * meaning no toplevel.
1778 copy_dir_dir (tctx
, ctx
, path
, mdpath
, FALSE
, FALSE
, do_delete
, parent_dirs
);
1785 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
1786 return_status
= copy_file_file (tctx
, ctx
, path
, dest_file
);
1789 if (do_delete
&& return_status
== FILE_CONT
)
1791 if (ctx
->erase_at_end
)
1793 static struct link
*tail
;
1794 size_t len
= strlen (path
);
1795 lp
= g_malloc (sizeof (struct link
) + len
);
1796 strncpy (lp
->name
, path
, len
+ 1);
1797 lp
->st_mode
= buf
.st_mode
;
1799 if (erase_list
!= NULL
)
1805 erase_list
= tail
= lp
;
1809 if (S_ISDIR (buf
.st_mode
))
1811 return_status
= erase_dir_iff_empty (ctx
, path
);
1814 return_status
= erase_file (tctx
, ctx
, path
, FALSE
);
1819 mc_closedir (reading
);
1823 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1824 utb
.actime
= cbuf
.st_atime
;
1825 utb
.modtime
= cbuf
.st_mtime
;
1826 mc_utime (dest_dir
, &utb
);
1830 cbuf
.st_mode
= umask (-1);
1831 umask (cbuf
.st_mode
);
1832 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
1833 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1838 g_free (parent_dirs
);
1841 return return_status
;
1846 /* --------------------------------------------------------------------------------------------- */
1847 /* {{{ Move routines */
1850 move_dir_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
, const char *d
)
1852 struct stat sbuf
, dbuf
, destbuf
;
1855 FileProgressStatus return_status
;
1856 gboolean move_over
= FALSE
;
1859 file_progress_show_source (ctx
, s
);
1860 file_progress_show_target (ctx
, d
);
1861 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1867 dstat_ok
= (mc_stat (d
, &dbuf
) == 0);
1869 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
1870 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s
, d
);
1873 destdir
= g_strdup (d
); /* destination doesn't exist */
1874 else if (!ctx
->dive_into_subdirs
)
1876 destdir
= g_strdup (d
);
1880 destdir
= concat_dir_and_file (d
, x_basename (s
));
1882 /* Check if the user inputted an existing dir */
1884 if (!mc_stat (destdir
, &destbuf
))
1888 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, TRUE
, TRUE
, NULL
);
1890 if (return_status
!= FILE_CONT
)
1896 if (S_ISDIR (destbuf
.st_mode
))
1897 return_status
= file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir
);
1899 return_status
= file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir
);
1900 if (return_status
== FILE_RETRY
)
1901 goto retry_dst_stat
;
1904 return return_status
;
1908 if (mc_rename (s
, destdir
) == 0)
1910 return_status
= FILE_CONT
;
1916 return_status
= files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s
, d
);
1917 if (return_status
== FILE_RETRY
)
1921 /* Failed because of filesystem boundary -> copy dir instead */
1922 return_status
= copy_dir_dir (tctx
, ctx
, s
, destdir
, FALSE
, FALSE
, TRUE
, NULL
);
1924 if (return_status
!= FILE_CONT
)
1927 file_progress_show_source (ctx
, NULL
);
1928 file_progress_show (ctx
, 0, 0, "", FALSE
);
1930 return_status
= check_progress_buttons (ctx
);
1931 if (return_status
!= FILE_CONT
)
1935 if (ctx
->erase_at_end
)
1937 for (; erase_list
&& return_status
!= FILE_ABORT
;)
1939 if (S_ISDIR (erase_list
->st_mode
))
1941 return_status
= erase_dir_iff_empty (ctx
, erase_list
->name
);
1944 return_status
= erase_file (tctx
, ctx
, erase_list
->name
, FALSE
);
1946 erase_list
= erase_list
->next
;
1950 erase_dir_iff_empty (ctx
, s
);
1957 erase_list
= erase_list
->next
;
1960 return return_status
;
1965 /* --------------------------------------------------------------------------------------------- */
1966 /* {{{ Erase routines */
1969 erase_dir (FileOpTotalContext
* tctx
, FileOpContext
* ctx
, const char *s
)
1971 FileProgressStatus error
;
1973 if (strcmp (s
, "..") == 0)
1976 if (strcmp (s
, ".") == 0)
1979 file_progress_show_deleting (ctx
, s
);
1980 if (check_progress_buttons (ctx
) == FILE_ABORT
)
1984 /* The old way to detect a non empty directory was:
1985 error = my_rmdir (s);
1986 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1987 For the linux user space nfs server (nfs-server-2.2beta29-2)
1988 we would have to check also for EIO. I hope the new way is
1989 fool proof. (Norbert)
1991 error
= check_dir_is_empty (s
);
1994 error
= query_recursive (ctx
, s
);
1995 if (error
== FILE_CONT
)
1996 return recursive_erase (tctx
, ctx
, s
);
2001 while (my_rmdir (s
) == -1)
2003 error
= file_error (_("Cannot remove directory \"%s\"\n%s"), s
);
2004 if (error
!= FILE_RETRY
)
2013 /* --------------------------------------------------------------------------------------------- */
2014 /* {{{ Panel operate routines */
2017 compute_dir_size_create_ui (void)
2019 ComputeDirSizeUI
*ui
;
2021 const char *b_name
= N_("&Abort");
2027 ui
= g_new (ComputeDirSizeUI
, 1);
2029 ui
->dlg
= create_dlg (TRUE
, 0, 0, 8, COLS
/ 2, dialog_colors
, NULL
,
2030 NULL
, _("Directory scanning"), DLG_CENTER
);
2031 ui
->dirname
= label_new (3, 3, "");
2032 add_widget (ui
->dlg
, ui
->dirname
);
2034 add_widget (ui
->dlg
,
2035 button_new (5, (ui
->dlg
->cols
- strlen (b_name
)) / 2,
2036 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
2038 /* We will manage the dialog without any help,
2039 that's why we have to call init_dlg */
2045 /* --------------------------------------------------------------------------------------------- */
2048 compute_dir_size_destroy_ui (ComputeDirSizeUI
* ui
)
2052 /* schedule to update passive panel */
2053 other_panel
->dirty
= 1;
2055 /* close and destroy dialog */
2056 dlg_run_done (ui
->dlg
);
2057 destroy_dlg (ui
->dlg
);
2062 /* --------------------------------------------------------------------------------------------- */
2065 compute_dir_size_update_ui (const void *ui
, const char *dirname
)
2067 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
2074 label_set_text (this->dirname
, str_trunc (dirname
, this->dlg
->cols
- 6));
2076 event
.x
= -1; /* Don't show the GPM cursor */
2077 c
= tty_get_event (&event
, FALSE
, FALSE
);
2081 /* Reinitialize to avoid old values after events other than
2082 selecting a button */
2083 this->dlg
->ret_value
= FILE_CONT
;
2085 dlg_process_event (this->dlg
, c
, &event
);
2087 switch (this->dlg
->ret_value
)
2097 /* --------------------------------------------------------------------------------------------- */
2101 * Computes the number of bytes used by the files in a directory
2105 compute_dir_size (const char *dirname
, const void *ui
,
2106 compute_dir_size_callback cback
,
2107 size_t * ret_marked
, uintmax_t * ret_total
, gboolean compute_symlinks
)
2112 struct dirent
*dirent
;
2113 FileProgressStatus ret
= FILE_CONT
;
2115 if (!compute_symlinks
)
2117 res
= mc_lstat (dirname
, &s
);
2121 /* don't scan symlink to directory */
2122 if (S_ISLNK (s
.st_mode
))
2125 *ret_total
+= (uintmax_t) s
.st_size
;
2130 dir
= mc_opendir (dirname
);
2135 while ((dirent
= mc_readdir (dir
)) != NULL
)
2139 ret
= (cback
!= NULL
) ? cback (ui
, dirname
) : FILE_CONT
;
2141 if (ret
!= FILE_CONT
)
2144 if (strcmp (dirent
->d_name
, ".") == 0)
2146 if (strcmp (dirent
->d_name
, "..") == 0)
2149 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
2150 res
= mc_lstat (fullname
, &s
);
2158 if (S_ISDIR (s
.st_mode
))
2160 size_t subdir_count
= 0;
2161 uintmax_t subdir_bytes
= 0;
2164 compute_dir_size (fullname
, ui
, cback
, &subdir_count
, &subdir_bytes
,
2167 if (ret
!= FILE_CONT
)
2173 *ret_marked
+= subdir_count
;
2174 *ret_total
+= subdir_bytes
;
2179 *ret_total
+= (uintmax_t) s
.st_size
;
2190 /* --------------------------------------------------------------------------------------------- */
2194 * Performs one of the operations on the selection on the source_panel
2195 * (copy, delete, move).
2197 * Returns TRUE if did change the directory
2198 * structure, Returns FALSE if user aborted
2200 * force_single forces operation on the current entry and affects
2201 * default destination. Current filename is used as default.
2205 panel_operate (void *source_panel
, FileOperation operation
, gboolean force_single
)
2207 WPanel
*panel
= (WPanel
*) source_panel
;
2208 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
2209 || (get_current_type () == view_tree
);
2211 char *source
= NULL
;
2212 #ifdef WITH_FULL_PATHS
2213 char *source_with_path
= NULL
;
2215 #define source_with_path source
2216 #endif /* !WITH_FULL_PATHS */
2219 char *save_cwd
= NULL
, *save_dest
= NULL
;
2220 struct stat src_stat
;
2221 gboolean ret_val
= TRUE
;
2223 FileProgressStatus value
;
2225 FileOpTotalContext
*tctx
;
2227 gboolean do_bg
= FALSE
; /* do background operation? */
2229 static gboolean i18n_flag
= FALSE
;
2232 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
2233 op_names
[i
] = Q_ (op_names
[i
]);
2237 free_linklist (&linklist
);
2238 free_linklist (&dest_dirs
);
2240 /* Update panel contents to avoid actions on deleted files */
2241 if (!panel
->is_panelized
)
2243 panel_update_flags_t flags
= UP_RELOAD
;
2245 /* don't update panelized panel */
2246 if (get_other_type () == view_listing
&& other_panel
->is_panelized
)
2247 flags
|= UP_ONLY_CURRENT
;
2249 update_panels (flags
, UP_KEEPSEL
);
2257 source
= selection (panel
)->fname
;
2258 src_stat
= selection (panel
)->st
;
2261 source
= panel_get_file (panel
, &src_stat
);
2263 if (!strcmp (source
, ".."))
2265 message (D_ERROR
, MSG_ERROR
, _("Cannot operate on \"..\"!"));
2270 ctx
= file_op_context_new (operation
);
2272 /* Show confirmation dialog */
2273 if (operation
!= OP_DELETE
)
2279 /* Forced single operations default to the original name */
2282 else if (get_other_type () == view_listing
)
2283 dest_dir
= other_panel
->cwd
;
2285 dest_dir
= panel
->cwd
;
2287 * Add trailing backslash only when do non-local ops.
2288 * It saves user from occasional file renames (when destination
2291 if (!force_single
&& dest_dir
[0] != '\0' && dest_dir
[strlen (dest_dir
) - 1] != PATH_SEP
)
2293 /* add trailing separator */
2294 dest_dir_
= g_strconcat (dest_dir
, PATH_SEP_STR
, (char *) NULL
);
2299 dest_dir_
= g_strdup (dest_dir
);
2302 if (dest_dir_
== NULL
)
2308 /* Generate confirmation prompt */
2309 format
= panel_operate_generate_prompt (panel
, operation
, source
!= NULL
, &src_stat
);
2311 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
2312 source
!= NULL
? (void *) source
2313 : (void *) &panel
->marked
, dest_dir_
, &do_bg
);
2318 if (dest
== NULL
|| dest
[0] == '\0')
2325 else if (confirm_delete
)
2328 char fmd_buf
[BUF_MEDIUM
];
2330 /* Generate confirmation prompt */
2331 format
= panel_operate_generate_prompt (panel
, OP_DELETE
, source
!= NULL
, &src_stat
);
2334 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
2337 const int fmd_xlen
= 64;
2338 i
= fmd_xlen
- str_term_width1 (format
) - 4;
2339 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, str_trunc (source
, i
));
2347 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
2356 tctx
= file_op_total_context_new ();
2357 gettimeofday (&tctx
->transfer_start
, (struct timezone
*) NULL
);
2360 filegui_dialog_type_t dialog_type
;
2362 if (operation
== OP_DELETE
)
2363 dialog_type
= FILEGUI_DIALOG_DELETE_ITEM
;
2366 dialog_type
= !((operation
!= OP_COPY
) || (single_entry
) || (force_single
))
2367 ? FILEGUI_DIALOG_MULTI_ITEM
: FILEGUI_DIALOG_ONE_ITEM
;
2369 if ((single_entry
) && (operation
== OP_COPY
) && S_ISDIR (selection (panel
)->st
.st_mode
))
2370 dialog_type
= FILEGUI_DIALOG_MULTI_ITEM
;
2373 /* Background also need ctx->ui, but not full */
2375 file_op_context_create_ui_without_init (ctx
, 1, dialog_type
);
2377 file_op_context_create_ui (ctx
, 1, dialog_type
);
2380 #ifdef WITH_BACKGROUND
2381 /* Did the user select to do a background operation? */
2386 v
= do_background (ctx
, g_strconcat (op_names
[operation
], ": ", panel
->cwd
, (char *) NULL
));
2388 message (D_ERROR
, MSG_ERROR
, _("Sorry, I could not put the job in background"));
2390 /* If we are the parent */
2393 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
2394 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
2395 /* file_op_context_destroy (ctx); */
2399 #endif /* WITH_BACKGROUND */
2401 /* Initialize things */
2402 /* We do not want to trash cache every time file is
2403 created/touched. However, this will make our cache contain
2405 if ((dest
!= NULL
) && (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2406 save_dest
= g_strdup (dest
);
2408 if ((panel
->cwd
[0] != '\0') && (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1)))
2409 save_cwd
= g_strdup (panel
->cwd
);
2411 /* Now, let's do the job */
2413 /* This code is only called by the tree and panel code */
2416 /* We now have ETA in all cases */
2418 /* One file: FIXME mc_chdir will take user out of any vfs */
2419 if ((operation
!= OP_COPY
) && (get_current_type () == view_tree
) &&
2420 (mc_chdir (PATH_SEP_STR
) < 0))
2426 /* The source and src_stat variables have been initialized before */
2427 #ifdef WITH_FULL_PATHS
2428 if (g_path_is_absolute (source
))
2429 source_with_path
= g_strdup (source
);
2431 source_with_path
= g_build_filename (panel
->cwd
, source
, (char *) NULL
);
2432 #endif /* WITH_FULL_PATHS */
2434 if (panel_operate_init_totals (operation
, panel
, source_with_path
, ctx
) == FILE_CONT
)
2436 if (operation
== OP_DELETE
)
2438 if (S_ISDIR (src_stat
.st_mode
))
2439 value
= erase_dir (tctx
, ctx
, source_with_path
);
2441 value
= erase_file (tctx
, ctx
, source_with_path
, 1);
2445 temp
= transform_source (ctx
, source_with_path
);
2447 value
= transform_error
;
2450 char *repl_dest
, *temp2
;
2452 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2453 temp2
= concat_dir_and_file (repl_dest
, temp
);
2462 /* we use file_mask_op_follow_links only with OP_COPY */
2463 ctx
->stat_func (source_with_path
, &src_stat
);
2465 if (S_ISDIR (src_stat
.st_mode
))
2466 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, dest
,
2467 TRUE
, FALSE
, FALSE
, NULL
);
2469 value
= copy_file_file (tctx
, ctx
, source_with_path
, dest
);
2473 if (S_ISDIR (src_stat
.st_mode
))
2474 value
= move_dir_dir (tctx
, ctx
, source_with_path
, dest
);
2476 value
= move_file_file (tctx
, ctx
, source_with_path
, dest
);
2480 /* Unknown file operation */
2484 } /* Copy or move operation */
2486 if ((value
== FILE_CONT
) && !force_single
)
2487 unmark_files (panel
);
2494 /* Check destination for copy or move operation */
2495 while (operation
!= OP_DELETE
)
2498 struct stat dst_stat
;
2500 dst_result
= mc_stat (dest
, &dst_stat
);
2502 if ((dst_result
!= 0) || S_ISDIR (dst_stat
.st_mode
))
2505 if (file_error (_("Destination \"%s\" must be a directory\n%s"), dest
) != FILE_RETRY
)
2509 if (panel_operate_init_totals (operation
, panel
, NULL
, ctx
) == FILE_CONT
)
2511 /* Loop for every file, perform the actual copy operation */
2512 for (i
= 0; i
< panel
->count
; i
++)
2514 if (!panel
->dir
.list
[i
].f
.marked
)
2515 continue; /* Skip the unmarked ones */
2517 source
= panel
->dir
.list
[i
].fname
;
2518 src_stat
= panel
->dir
.list
[i
].st
;
2520 #ifdef WITH_FULL_PATHS
2521 g_free (source_with_path
);
2522 if (g_path_is_absolute (source
))
2523 source_with_path
= g_strdup (source
);
2525 source_with_path
= g_build_filename (panel
->cwd
, source
, (char *) NULL
);
2526 #endif /* WITH_FULL_PATHS */
2528 if (operation
== OP_DELETE
)
2530 if (S_ISDIR (src_stat
.st_mode
))
2531 value
= erase_dir (tctx
, ctx
, source_with_path
);
2533 value
= erase_file (tctx
, ctx
, source_with_path
, 1);
2537 temp
= transform_source (ctx
, source_with_path
);
2540 value
= transform_error
;
2543 char *temp2
, *temp3
, *repl_dest
;
2545 repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2546 temp2
= concat_dir_and_file (repl_dest
, temp
);
2549 temp3
= source_with_path
;
2550 source_with_path
= strutils_shell_unescape (source_with_path
);
2553 temp2
= strutils_shell_unescape (temp2
);
2559 /* we use file_mask_op_follow_links only with OP_COPY */
2560 ctx
->stat_func (source_with_path
, &src_stat
);
2561 if (S_ISDIR (src_stat
.st_mode
))
2562 value
= copy_dir_dir (tctx
, ctx
, source_with_path
, temp2
,
2563 TRUE
, FALSE
, FALSE
, NULL
);
2565 value
= copy_file_file (tctx
, ctx
, source_with_path
, temp2
);
2566 free_linklist (&dest_dirs
);
2570 if (S_ISDIR (src_stat
.st_mode
))
2571 value
= move_dir_dir (tctx
, ctx
, source_with_path
, temp2
);
2573 value
= move_file_file (tctx
, ctx
, source_with_path
, temp2
);
2577 /* Unknown file operation */
2583 } /* Copy or move operation */
2585 if (value
== FILE_ABORT
)
2588 if (value
== FILE_CONT
)
2589 do_file_mark (panel
, i
, 0);
2591 if (verbose
&& ctx
->dialog_type
== FILEGUI_DIALOG_MULTI_ITEM
)
2593 file_progress_show_count (ctx
, tctx
->progress_count
, ctx
->progress_count
);
2594 file_progress_show_total (tctx
, ctx
, tctx
->progress_bytes
, FALSE
);
2597 if (operation
!= OP_DELETE
)
2598 file_progress_show (ctx
, 0, 0, "", FALSE
);
2600 if (check_progress_buttons (ctx
) == FILE_ABORT
)
2604 } /* Loop for every file */
2606 } /* Many entries */
2610 if (save_cwd
!= NULL
)
2612 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2616 if (save_dest
!= NULL
)
2618 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2622 free_linklist (&linklist
);
2623 free_linklist (&dest_dirs
);
2624 #ifdef WITH_FULL_PATHS
2625 g_free (source_with_path
);
2626 #endif /* WITH_FULL_PATHS */
2628 g_free (ctx
->dest_mask
);
2629 ctx
->dest_mask
= NULL
;
2631 #ifdef WITH_BACKGROUND
2632 /* Let our parent know we are saying bye bye */
2633 if (we_are_background
)
2635 int cur_pid
= getpid ();
2636 /* Send pid to parent with child context, it is fork and
2637 don't modify real parent ctx */
2639 parent_call ((void *) end_bg_process
, ctx
, 0);
2644 #endif /* WITH_BACKGROUND */
2646 file_op_total_context_destroy (tctx
);
2648 file_op_context_destroy (ctx
);
2655 /* --------------------------------------------------------------------------------------------- */
2656 /* {{{ Query/status report routines */
2657 /** Report error with one file */
2659 file_error (const char *format
, const char *file
)
2661 char buf
[BUF_MEDIUM
];
2663 g_snprintf (buf
, sizeof (buf
), format
, path_trunc (file
, 30), unix_error_string (errno
));
2665 return do_file_error (buf
);
2668 /* --------------------------------------------------------------------------------------------- */
2671 Cause emacs to enter folding mode for this file: