3 /* File managing. Important notes on this file:
5 About the use of dialogs in this file:
6 If you want to add a new dialog box (or call a routine that pops
7 up a dialog box), you have to provide a wrapper for background
8 operations (ie, background operations have to up-call to the parent
11 For example, instead of using the message() routine, in this
12 file, you should use one of the stubs that call message with the
13 proper number of arguments (ie, message_1s, message_2s and so on).
15 Actually, that is a rule that should be followed by any routines
16 that may be called from this module.
21 Copyright (C) 1994, 1995, 1996 The Free Software Foundation
23 Written by: 1994, 1995 Janne Kukonlehto
24 1994, 1995 Fred Leeflang
25 1994, 1995, 1996 Miguel de Icaza
26 1995, 1996 Jakub Jelinek
30 The copy code was based in GNU's cp, and was written by:
31 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
33 The move code was based in GNU's mv, and was written by:
34 Mike Parker and David MacKenzie.
36 Janne Kukonlehto added much error recovery to them for being used
37 in an interactive program.
39 This program is free software; you can redistribute it and/or modify
40 it under the terms of the GNU General Public License as published by
41 the Free Software Foundation; either version 2 of the License, or
42 (at your option) any later version.
44 This program is distributed in the hope that it will be useful,
45 but WITHOUT ANY WARRANTY; without even the implied warranty of
46 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 GNU General Public License for more details.
49 You should have received a copy of the GNU General Public License
50 along with this program; if not, write to the Free Software
51 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
55 /* {{{ Include files */
58 /* Hack: the vfs code should not rely on this */
59 #define WITH_FULL_PATHS 1
61 #include <sys/types.h>
68 #endif /* HAVE_UNISTD_H */
76 /* Needed by query_replace */
82 #include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
87 /* Needed for current_panel, other_panel and WTree */
94 #include "../vfs/vfs.h"
101 * Whether the Midnight Commander tries to provide more
102 * information about copy/move sizes and bytes transfered
103 * at the expense of some speed
105 int file_op_compute_totals
= 1;
107 /* If on, default for "No" in delete operations */
110 /* This is a hard link cache */
121 /* the hard link cache */
122 struct link
*linklist
= NULL
;
124 /* the files-to-be-erased list */
125 struct link
*erase_list
;
128 * In copy_dir_dir we use two additional single linked lists: The first -
129 * variable name `parent_dirs' - holds information about already copied
130 * directories and is used to detect cyclic symbolic links.
131 * The second (`dest_dirs' below) holds information about just created
132 * target directories and is used to detect when an directory is copied
133 * into itself (we don't want to copy infinitly).
134 * Both lists don't use the linkcount and name structure members of struct
137 struct link
*dest_dirs
= 0;
139 char *op_names
[3] = {
147 static int query_replace (FileOpContext
* ctx
, char *destname
,
148 struct stat
*_s_stat
, struct stat
*_d_stat
);
149 static int query_recursive (FileOpContext
* ctx
, char *s
);
150 static int do_file_error (char *str
);
153 enum CaseConvs
{ NO_CONV
= 0, UP_CHAR
= 1, LOW_CHAR
= 2, UP_SECT
=
157 convert_case (int c
, enum CaseConvs
*conversion
)
159 if (*conversion
& UP_CHAR
) {
160 *conversion
&= ~UP_CHAR
;
162 } else if (*conversion
& LOW_CHAR
) {
163 *conversion
&= ~LOW_CHAR
;
165 } else if (*conversion
& UP_SECT
) {
167 } else if (*conversion
& LOW_SECT
) {
173 static int transform_error
= 0;
175 static unsigned char *
176 do_transform_source (FileOpContext
*ctx
, unsigned char *source
)
179 unsigned char *fnsource
= x_basename (source
);
181 enum CaseConvs case_conv
= NO_CONV
;
182 static unsigned char fntarget
[MC_MAXPATHLEN
];
184 len
= strlen (fnsource
);
185 j
= re_match (&ctx
->rx
, fnsource
, len
, 0, &ctx
->regs
);
187 transform_error
= FILE_SKIP
;
190 for (next_reg
= 1, j
= 0, k
= 0; j
< strlen (ctx
->dest_mask
); j
++) {
191 switch (ctx
->dest_mask
[j
]) {
194 if (!isdigit ((unsigned char) ctx
->dest_mask
[j
])) {
195 /* Backslash followed by non-digit */
196 switch (ctx
->dest_mask
[j
]) {
198 case_conv
|= UP_SECT
;
199 case_conv
&= ~LOW_SECT
;
202 case_conv
|= UP_CHAR
;
205 case_conv
|= LOW_SECT
;
206 case_conv
&= ~UP_SECT
;
209 case_conv
|= LOW_CHAR
;
215 /* Backslash as quote mark */
217 convert_case (ctx
->dest_mask
[j
], &case_conv
);
221 /* Backslash followed by digit */
222 next_reg
= ctx
->dest_mask
[j
] - '0';
227 if (next_reg
< 0 || next_reg
>= RE_NREGS
228 || ctx
->regs
.start
[next_reg
] < 0) {
229 message_1s (1, MSG_ERROR
, _(" Invalid target mask "));
230 transform_error
= FILE_ABORT
;
233 for (l
= ctx
->regs
.start
[next_reg
];
234 l
< ctx
->regs
.end
[next_reg
]; l
++)
235 fntarget
[k
++] = convert_case (fnsource
[l
], &case_conv
);
240 fntarget
[k
++] = convert_case (ctx
->dest_mask
[j
], &case_conv
);
248 static unsigned char *
249 transform_source (FileOpContext
*ctx
, unsigned char *source
)
251 unsigned char *s
= g_strdup (source
);
254 /* We remove \n from the filename since regex routines would use \n as an anchor */
255 /* this is just to be allowed to maniupulate file names with \n on it */
256 for (q
= s
; *q
; q
++) {
260 q
= do_transform_source (ctx
, s
);
266 free_linklist (struct link
**linklist
)
268 struct link
*lp
, *lp2
;
270 for (lp
= *linklist
; lp
!= NULL
; lp
= lp2
) {
278 is_in_linklist (struct link
*lp
, char *path
, struct stat
*sb
)
280 ino_t ino
= sb
->st_ino
;
281 dev_t dev
= sb
->st_dev
;
283 vfs
*vfs
= vfs_type (path
);
290 if (lp
->ino
== ino
&& lp
->dev
== dev
)
298 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
299 * and a hardlink was succesfully made
302 check_hardlinks (char *src_name
, char *dst_name
, struct stat
*pstat
)
305 vfs
*my_vfs
= vfs_type (src_name
);
306 ino_t ino
= pstat
->st_ino
;
307 dev_t dev
= pstat
->st_dev
;
308 struct stat link_stat
;
311 #if 1 /* What will happen if we kill this line? mc_link() will fail on this and it is right behaviour... */
312 if (vfs_file_is_ftp (src_name
) || vfs_file_is_smb (src_name
))
315 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
316 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
) {
317 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
318 && link_stat
.st_dev
== dev
319 && vfs_type (lp
->name
) == my_vfs
) {
320 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
322 if (vfs_type (dst_name
) == vfs_type (p
)) {
323 if (!mc_stat (p
, &link_stat
)) {
324 if (!mc_link (p
, dst_name
))
329 message_1s (1, MSG_ERROR
, _(" Cannot make the hardlink "));
332 lp
= (struct link
*) g_malloc (sizeof (struct link
) + strlen (src_name
)
333 + strlen (dst_name
) + 1);
338 strcpy (lp
->name
, src_name
);
339 p
= strchr (lp
->name
, 0) + 1;
340 strcpy (p
, dst_name
);
348 * Duplicate the contents of the symbolic link src_path in dst_path.
349 * Try to make a stable symlink if the option "stable symlink" was
350 * set in the file mask dialog.
351 * If dst_path is an existing symlink it will be deleted silently
352 * (upper levels take already care of existing files at dst_path).
355 make_symlink (FileOpContext
*ctx
, char *src_path
, char *dst_path
)
357 char link_target
[MC_MAXPATHLEN
];
363 if (mc_lstat (dst_path
, &sb
) == 0 && S_ISLNK (sb
.st_mode
))
369 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
);
372 file_error (_(" Cannot read source link \"%s\" \n %s "),
374 if (return_status
== FILE_RETRY
)
375 goto retry_src_readlink
;
376 return return_status
;
378 link_target
[len
] = 0;
380 if (ctx
->stable_symlinks
)
381 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
)) {
382 message_1s (1, MSG_ERROR
,
383 _(" Cannot make stable symlinks across "
384 "non-local filesystems: \n\n"
385 " Option Stable Symlinks will be disabled "));
386 ctx
->stable_symlinks
= 0;
389 if (ctx
->stable_symlinks
&& *link_target
!= PATH_SEP
) {
392 p
= g_strdup (src_path
);
393 r
= strrchr (p
, PATH_SEP
);
397 if (*dst_path
== PATH_SEP
)
398 q
= g_strdup (dst_path
);
400 q
= g_strconcat (p
, dst_path
, NULL
);
401 r
= strrchr (q
, PATH_SEP
);
404 s
= g_strconcat (p
, link_target
, NULL
);
405 strcpy (link_target
, s
);
407 s
= diff_two_paths (q
, link_target
);
409 strcpy (link_target
, s
);
418 if (mc_symlink (link_target
, dst_path
) == 0)
422 * if dst_exists, it is obvious that this had failed.
423 * We can delete the old symlink and try again...
425 if (dst_is_symlink
) {
426 if (!mc_unlink (dst_path
))
427 if (mc_symlink (link_target
, dst_path
) == 0)
432 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
434 if (return_status
== FILE_RETRY
)
435 goto retry_dst_symlink
;
436 return return_status
;
440 progress_update_one (FileOpContext
*ctx
,
441 off_t
*progress_count
,
442 double *progress_bytes
, int add
, int is_toplevel_file
)
446 if (is_toplevel_file
|| ctx
->progress_totals_computed
) {
448 (*progress_bytes
) += add
;
451 /* Apply some heuristic here to not call the update stuff very often */
453 file_progress_show_count (ctx
, *progress_count
,
454 ctx
->progress_count
);
455 if (ret
!= FILE_CONT
)
458 file_progress_show_bytes (ctx
, *progress_bytes
,
459 ctx
->progress_bytes
);
464 /* Status of the destination file */
466 DEST_NONE
, /* Not created */
467 DEST_SHORT
, /* Created, not fully copied */
468 DEST_FULL
/* Created, fully copied */
472 copy_file_file (FileOpContext
*ctx
, char *src_path
, char *dst_path
,
473 int ask_overwrite
, off_t
*progress_count
,
474 double *progress_bytes
, int is_toplevel_file
)
477 uid_t src_uid
= (uid_t
) - 1;
478 gid_t src_gid
= (gid_t
) - 1;
479 #endif /* !NATIVE_WIN32 */
481 int buf_size
= BUF_8K
;
482 int src_desc
, dest_desc
= -1;
483 int n_read
, n_written
;
484 int src_mode
= 0; /* The mode of the source file */
487 int dst_exists
= 0, appending
= 0;
488 off_t n_read_total
= 0, file_size
= -1;
489 int return_status
, temp_status
;
490 struct timeval tv_transfer_start
;
491 int dst_status
= DEST_NONE
; /* 1 if the file is not fully copied */
493 /* FIXME: We should not be using global variables! */
495 return_status
= FILE_RETRY
;
497 if (file_progress_show_source (ctx
, src_path
) == FILE_ABORT
||
498 file_progress_show_target (ctx
, dst_path
) == FILE_ABORT
)
504 if (mc_stat (dst_path
, &sb2
) == 0) {
505 if (S_ISDIR (sb2
.st_mode
)) {
507 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
509 if (return_status
== FILE_RETRY
)
511 return return_status
;
516 while ((*ctx
->stat_func
) (src_path
, &sb
)) {
518 file_error (_(" Cannot stat source file \"%s\" \n %s "),
520 if (return_status
!= FILE_RETRY
)
521 return return_status
;
525 /* .ado: For Win32: no st_ino exists, it is better to just try to
526 * overwrite the target file
529 /* Destination already exists */
530 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
) {
531 message_3s (1, MSG_ERROR
,
532 _(" `%s' and `%s' are the same file "), src_path
,
537 #endif /* !NATIVE_WIN32 */
539 /* Should we replace destination? */
542 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
543 if (return_status
!= FILE_CONT
)
544 return return_status
;
548 if (!ctx
->do_append
) {
549 /* .ado: OS2 and NT don't have hardlinks */
551 /* Check the hardlinks */
552 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
553 check_hardlinks (src_path
, dst_path
, &sb
) == 1) {
554 /* We have made a hardlink - no more processing is necessary */
555 return return_status
;
558 if (S_ISLNK (sb
.st_mode
)) {
561 retval
= make_symlink (ctx
, src_path
, dst_path
);
564 #endif /* !NATIVE_WIN32 */
566 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
)
567 || S_ISFIFO (sb
.st_mode
)
568 || S_ISSOCK (sb
.st_mode
)) {
570 (dst_path
, sb
.st_mode
& ctx
->umask_kill
,
574 (" Cannot create special file \"%s\" \n %s "),
576 if (return_status
== FILE_RETRY
)
578 return return_status
;
583 while (ctx
->preserve_uidgid
584 && mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)) {
587 (" Cannot chown target file \"%s\" \n %s "),
589 if (temp_status
== FILE_RETRY
)
593 #endif /* !NATIVE_WIN32 */
594 while (ctx
->preserve
&&
595 (mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
) <
599 (" Cannot chmod target file \"%s\" \n %s "),
601 if (temp_status
== FILE_RETRY
)
609 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
611 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0) {
613 file_error (_(" Cannot open source file \"%s\" \n %s "),
615 if (return_status
== FILE_RETRY
)
618 return return_status
;
622 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
) {
623 message_1s (1, _("Warning"),
624 _(" Reget failed, about to overwrite file "));
625 ctx
->do_reget
= ctx
->do_append
= 0;
629 while (mc_fstat (src_desc
, &sb
)) {
631 file_error (_(" Cannot fstat source file \"%s\" \n %s "),
633 if (return_status
== FILE_RETRY
)
638 src_mode
= sb
.st_mode
;
642 #endif /* !NATIVE_WIN32 */
643 utb
.actime
= sb
.st_atime
;
644 utb
.modtime
= sb
.st_mtime
;
645 file_size
= sb
.st_size
;
647 /* Create the new regular file with small permissions initially,
648 do not create a security hole. FIXME: You have security hole
649 here, btw. Imagine copying to /tmp and symlink attack :-( */
651 while ((dest_desc
= mc_open (dst_path
, O_WRONLY
|
653 do_append
? O_APPEND
: (O_CREAT
|
657 file_error (_(" Cannot create target file \"%s\" \n %s "),
659 if (return_status
== FILE_RETRY
)
664 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
666 appending
= ctx
->do_append
;
669 /* Find out the optimal buffer size. */
670 while (mc_fstat (dest_desc
, &sb
)) {
672 file_error (_(" Cannot fstat target file \"%s\" \n %s "),
674 if (return_status
== FILE_RETRY
)
678 buf
= (char *) g_malloc (buf_size
);
683 return_status
= file_progress_show (ctx
, 0, file_size
);
687 if (return_status
!= FILE_CONT
)
691 struct timeval tv_current
, tv_last_update
, tv_last_input
;
692 int secs
, update_secs
;
696 tv_last_update
= tv_transfer_start
;
700 if (mc_ctl (src_desc
, MCCTL_IS_NOTREADY
, 0))
703 while ((n_read
= mc_read (src_desc
, buf
, buf_size
)) < 0) {
706 (" Cannot read source file \"%s\" \n %s "),
708 if (return_status
== FILE_RETRY
)
715 gettimeofday (&tv_current
, NULL
);
719 n_read_total
+= n_read
;
721 /* Windows NT ftp servers report that files have no
722 * permissions: -------, so if we happen to have actually
723 * read something, we should fix the permissions.
725 if (!(src_mode
& ((S_IRUSR
| S_IWUSR
| S_IXUSR
) /* user */
726 |(S_IXOTH
| S_IWOTH
| S_IROTH
) /* other */
727 |(S_IXGRP
| S_IWGRP
| S_IRGRP
)))) /* group */
728 src_mode
= S_IRUSR
| S_IWUSR
| S_IROTH
| S_IRGRP
;
729 gettimeofday (&tv_last_input
, NULL
);
733 mc_write (dest_desc
, t
, n_read
)) < n_read
) {
741 (" Cannot write target file \"%s\" \n %s "),
743 if (return_status
!= FILE_RETRY
)
748 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
749 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
752 tv_last_update
= tv_current
;
755 /* 2. Check for a stalled condition */
756 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
758 if (update_secs
> 4) {
759 stalled_msg
= _("(stalled)");
764 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
768 ((dt
/ (double) n_read_total
) * file_size
) - dt
;
769 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
774 /* 4. Compute BPS rate */
777 (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
778 if (ctx
->bps_time
< 1)
780 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
783 file_progress_set_stalled_label (ctx
, stalled_msg
);
785 file_progress_show_bytes (ctx
,
786 *progress_bytes
+ n_read_total
,
787 ctx
->progress_bytes
);
788 if (return_status
== FILE_CONT
) {
790 file_progress_show (ctx
, n_read_total
, file_size
);
793 if (return_status
!= FILE_CONT
)
798 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
804 while (src_desc
!= -1 && mc_close (src_desc
) < 0) {
806 file_error (_(" Cannot close source file \"%s\" \n %s "),
808 if (temp_status
== FILE_RETRY
)
810 if (temp_status
== FILE_ABORT
)
811 return_status
= temp_status
;
815 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0) {
817 file_error (_(" Cannot close target file \"%s\" \n %s "),
819 if (temp_status
== FILE_RETRY
)
821 return_status
= temp_status
;
825 if (dst_status
== DEST_SHORT
) {
826 /* Remove short file */
829 query_dialog (_("Copy"),
830 _("Incomplete file was retrieved. Keep it?"),
831 D_ERROR
, 2, _("&Delete"), _("&Keep"));
833 mc_unlink (dst_path
);
834 } else if (dst_status
== DEST_FULL
) {
835 /* Copy has succeeded */
837 if (!appending
&& ctx
->preserve_uidgid
) {
838 while (mc_chown (dst_path
, src_uid
, src_gid
)) {
839 temp_status
= file_error
840 (_(" Cannot chown target file \"%s\" \n %s "),
842 if (temp_status
== FILE_RETRY
)
844 return_status
= temp_status
;
848 #endif /* !NATIVE_WIN32 */
851 * .ado: according to the XPG4 standard, the file must be closed before
852 * chmod can be invoked
854 if (!appending
&& ctx
->preserve
) {
855 while (mc_chmod (dst_path
, src_mode
& ctx
->umask_kill
)) {
858 (" Cannot chmod target file \"%s\" \n %s "),
860 if (temp_status
!= FILE_RETRY
) {
861 return_status
= temp_status
;
865 mc_utime (dst_path
, &utb
);
869 if (return_status
== FILE_CONT
)
871 progress_update_one (ctx
, progress_count
, progress_bytes
,
872 file_size
, is_toplevel_file
);
874 return return_status
;
878 * I think these copy_*_* functions should have a return type.
879 * anyway, this function *must* have two directories as arguments.
881 /* FIXME: This function needs to check the return values of the
884 copy_dir_dir (FileOpContext
*ctx
, char *s
, char *d
, int toplevel
,
885 int move_over
, int delete,
886 struct link
*parent_dirs
,
887 off_t
*progress_count
, double *progress_bytes
)
890 struct stat buf
, cbuf
;
892 char *path
, *mdpath
, *dest_file
, *dest_dir
;
893 int return_status
= FILE_CONT
;
897 /* First get the mode of the source dir */
899 if ((*ctx
->stat_func
) (s
, &cbuf
)) {
901 file_error (_(" Cannot stat source directory \"%s\" \n %s "),
903 if (return_status
== FILE_RETRY
)
905 return return_status
;
908 if (is_in_linklist (dest_dirs
, s
, &cbuf
)) {
909 /* Don't copy a directory we created before (we don't want to copy
910 infinitely if a directory is copied into itself) */
911 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
915 /* Hmm, hardlink to directory??? - Norbert */
916 /* FIXME: In this step we should do something
917 in case the destination already exist */
918 /* Check the hardlinks */
919 if (ctx
->preserve
&& cbuf
.st_nlink
> 1
920 && check_hardlinks (s
, d
, &cbuf
) == 1) {
921 /* We have made a hardlink - no more processing is necessary */
922 return return_status
;
925 if (!S_ISDIR (cbuf
.st_mode
)) {
928 (" Source directory \"%s\" is not a directory \n %s "),
930 if (return_status
== FILE_RETRY
)
932 return return_status
;
935 if (is_in_linklist (parent_dirs
, s
, &cbuf
)) {
936 /* we found a cyclic symbolic link */
937 message_2s (1, MSG_ERROR
,
938 _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
942 lp
= g_new (struct link
, 1);
943 lp
->vfs
= vfs_type (s
);
944 lp
->ino
= cbuf
.st_ino
;
945 lp
->dev
= cbuf
.st_dev
;
946 lp
->next
= parent_dirs
;
950 /* Now, check if the dest dir exists, if not, create it. */
951 if (mc_stat (d
, &buf
)) {
952 /* Here the dir doesn't exist : make it ! */
955 if (mc_rename (s
, d
) == 0) {
956 g_free (parent_dirs
);
960 dest_dir
= g_strdup (d
);
963 * If the destination directory exists, we want to copy the whole
964 * directory, but we only want this to happen once.
966 * Escape sequences added to the * to compiler warnings.
967 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
968 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
970 if (!S_ISDIR (buf
.st_mode
)) {
973 (" Destination \"%s\" must be a directory \n %s "),
975 if (return_status
== FILE_RETRY
)
977 g_free (parent_dirs
);
978 return return_status
;
981 /* Again, I'm getting curious. Is not d already what we wanted, incl.
982 * masked source basename? Is not this just a relict of the past versions?
983 * I'm afraid this will lead into a two level deep dive :(
985 * I think this is indeed the problem. I cannot remember any case where
986 * we actually would like that behaviour -miguel
988 * It's a documented feature (option `Dive into subdir if exists' in the
989 * copy/move dialog). -Norbert
991 if (toplevel
&& ctx
->dive_into_subdirs
) {
992 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
996 dest_dir
= g_strdup (d
);
1001 if (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)) {
1003 file_error (_(" Cannot create target directory \"%s\" \n %s "),
1005 if (return_status
== FILE_RETRY
)
1006 goto retry_dst_mkdir
;
1010 lp
= g_new (struct link
, 1);
1011 mc_stat (dest_dir
, &buf
);
1012 lp
->vfs
= vfs_type (dest_dir
);
1013 lp
->ino
= buf
.st_ino
;
1014 lp
->dev
= buf
.st_dev
;
1015 lp
->next
= dest_dirs
;
1018 #ifndef NATIVE_WIN32
1019 if (ctx
->preserve_uidgid
) {
1020 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)) {
1023 (" Cannot chown target directory \"%s\" \n %s "),
1025 if (return_status
!= FILE_RETRY
)
1029 #endif /* !NATIVE_WIN32 */
1032 /* open the source dir for reading */
1033 if ((reading
= mc_opendir (s
)) == 0) {
1037 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
) {
1039 * Now, we don't want '.' and '..' to be created / copied at any time
1041 if (!strcmp (next
->d_name
, "."))
1043 if (!strcmp (next
->d_name
, ".."))
1046 /* get the filename and add it to the src directory */
1047 path
= concat_dir_and_file (s
, next
->d_name
);
1049 (*ctx
->stat_func
) (path
, &buf
);
1050 if (S_ISDIR (buf
.st_mode
)) {
1051 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
1053 * From here, we just intend to recursively copy subdirs, not
1054 * the double functionality of copying different when the target
1055 * dir already exists. So, we give the recursive call the flag 0
1056 * meaning no toplevel.
1058 return_status
= copy_dir_dir (ctx
, path
, mdpath
, 0, 0,
1059 delete, parent_dirs
,
1060 progress_count
, progress_bytes
);
1063 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
1064 return_status
= copy_file_file (ctx
, path
, dest_file
, 1,
1065 progress_count
, progress_bytes
,
1069 if (delete && return_status
== FILE_CONT
) {
1070 if (ctx
->erase_at_end
) {
1071 static struct link
*tail
;
1072 lp
= g_malloc (sizeof (struct link
) + strlen (path
));
1073 strcpy (lp
->name
, path
);
1074 lp
->st_mode
= buf
.st_mode
;
1080 erase_list
= tail
= lp
;
1082 if (S_ISDIR (buf
.st_mode
)) {
1083 return_status
= erase_dir_iff_empty (ctx
, path
);
1085 return_status
= erase_file (ctx
, path
, 0, 0, 0);
1091 mc_closedir (reading
);
1093 if (ctx
->preserve
) {
1094 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1095 utb
.actime
= cbuf
.st_atime
;
1096 utb
.modtime
= cbuf
.st_mtime
;
1097 mc_utime (dest_dir
, &utb
);
1102 g_free (parent_dirs
);
1103 return return_status
;
1108 /* {{{ Move routines */
1111 move_file_file (FileOpContext
*ctx
, char *s
, char *d
,
1112 off_t
*progress_count
, double *progress_bytes
)
1114 struct stat src_stats
, dst_stats
;
1115 int return_status
= FILE_CONT
;
1117 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
1118 || file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1123 while (mc_lstat (s
, &src_stats
) != 0) {
1124 /* Source doesn't exist */
1126 file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1127 if (return_status
!= FILE_RETRY
)
1128 return return_status
;
1131 if (mc_lstat (d
, &dst_stats
) == 0) {
1132 /* Destination already exists */
1133 /* .ado: for Win32, no st_ino exists */
1134 #ifndef NATIVE_WIN32
1135 if (src_stats
.st_dev
== dst_stats
.st_dev
1136 && src_stats
.st_ino
== dst_stats
.st_ino
) {
1137 int msize
= COLS
- 36;
1138 char st
[MC_MAXPATHLEN
];
1139 char dt
[MC_MAXPATHLEN
];
1145 strcpy (st
, name_trunc (s
, msize
));
1146 strcpy (dt
, name_trunc (d
, msize
));
1147 message_3s (1, MSG_ERROR
,
1148 _(" `%s' and `%s' are the same file "), st
, dt
);
1152 #endif /* !NATIVE_WIN32 */
1153 if (S_ISDIR (dst_stats
.st_mode
)) {
1154 message_2s (1, MSG_ERROR
,
1155 _(" Cannot overwrite directory `%s' "), d
);
1160 if (confirm_overwrite
) {
1161 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1162 if (return_status
!= FILE_CONT
)
1163 return return_status
;
1165 /* Ok to overwrite */
1168 if (!ctx
->do_append
) {
1169 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1170 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1171 goto retry_src_remove
;
1173 return return_status
;
1176 if (mc_rename (s
, d
) == 0) {
1181 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1182 one nfs to the same, but on the server it is on two different
1183 filesystems. Then nfs returns EIO instead of EXDEV.
1184 Hope it will not hurt if we always in case of error try to copy/delete. */
1186 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1188 if (errno
!= EXDEV
) {
1190 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
,
1192 if (return_status
== FILE_RETRY
)
1194 return return_status
;
1198 /* Failed because filesystem boundary -> copy the file instead */
1200 copy_file_file (ctx
, s
, d
, 0, progress_count
, progress_bytes
, 1);
1201 if (return_status
!= FILE_CONT
)
1202 return return_status
;
1204 if ((return_status
=
1205 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1206 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1207 return return_status
;
1212 if (mc_unlink (s
)) {
1214 file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1215 if (return_status
== FILE_RETRY
)
1216 goto retry_src_remove
;
1217 return return_status
;
1220 if (return_status
== FILE_CONT
)
1221 return_status
= progress_update_one (ctx
,
1224 src_stats
.st_size
, 1);
1226 return return_status
;
1230 move_dir_dir (FileOpContext
*ctx
, char *s
, char *d
,
1231 off_t
*progress_count
, double *progress_bytes
)
1233 struct stat sbuf
, dbuf
, destbuf
;
1239 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
||
1240 file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1246 if (mc_stat (d
, &dbuf
))
1247 destdir
= g_strdup (d
); /* destination doesn't exist */
1248 else if (!ctx
->dive_into_subdirs
) {
1249 destdir
= g_strdup (d
);
1252 destdir
= concat_dir_and_file (d
, x_basename (s
));
1253 #ifndef NATIVE_WIN32
1254 if (sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
) {
1255 int msize
= COLS
- 36;
1256 char st
[MC_MAXPATHLEN
];
1257 char dt
[MC_MAXPATHLEN
];
1263 strcpy (st
, name_trunc (s
, msize
));
1264 strcpy (dt
, name_trunc (d
, msize
));
1265 message_3s (1, MSG_ERROR
,
1266 _(" `%s' and `%s' are the same directory "), st
, dt
);
1270 #endif /* !NATIVE_WIN32 */
1272 /* Check if the user inputted an existing dir */
1274 if (!mc_stat (destdir
, &destbuf
)) {
1276 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 1, 1, 0,
1277 progress_count
, progress_bytes
);
1279 if (return_status
!= FILE_CONT
)
1283 if (S_ISDIR (destbuf
.st_mode
))
1286 (" Cannot overwrite directory \"%s\" %s "),
1290 file_error (_(" Cannot overwrite file \"%s\" %s "),
1292 if (return_status
== FILE_RETRY
)
1293 goto retry_dst_stat
;
1296 return return_status
;
1300 if (mc_rename (s
, destdir
) == 0) {
1301 return_status
= FILE_CONT
;
1304 /* .ado: Drive, Do we need this anymore? */
1307 /* EXDEV: cross device; does not work everywhere */
1308 if (toupper (s
[0]) != toupper (destdir
[0]))
1313 if (errno
!= EXDEV
) {
1316 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1318 if (return_status
== FILE_RETRY
)
1325 /* Failed because of filesystem boundary -> copy dir instead */
1327 copy_dir_dir (ctx
, s
, destdir
, 0, 0, 1, 0, progress_count
,
1330 if (return_status
!= FILE_CONT
)
1333 if ((return_status
=
1334 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1335 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1339 if (ctx
->erase_at_end
) {
1340 for (; erase_list
&& return_status
!= FILE_ABORT
;) {
1341 if (S_ISDIR (erase_list
->st_mode
)) {
1343 erase_dir_iff_empty (ctx
, erase_list
->name
);
1346 erase_file (ctx
, erase_list
->name
, 0, 0, 0);
1348 erase_list
= erase_list
->next
;
1352 erase_dir_iff_empty (ctx
, s
);
1356 while (erase_list
) {
1358 erase_list
= erase_list
->next
;
1361 return return_status
;
1366 /* {{{ Erase routines */
1367 /* Don't update progress status if progress_count==NULL */
1369 erase_file (FileOpContext
*ctx
, char *s
, off_t
*progress_count
,
1370 double *progress_bytes
, int is_toplevel_file
)
1375 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1379 if (progress_count
&& mc_lstat (s
, &buf
)) {
1380 /* ignore, most likely the mc_unlink fails, too */
1384 while (mc_unlink (s
)) {
1386 file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1387 if (return_status
!= FILE_RETRY
)
1388 return return_status
;
1392 return progress_update_one (ctx
, progress_count
, progress_bytes
,
1393 buf
.st_size
, is_toplevel_file
);
1399 recursive_erase (FileOpContext
*ctx
, char *s
, off_t
*progress_count
,
1400 double *progress_bytes
)
1402 struct dirent
*next
;
1406 int return_status
= FILE_CONT
;
1408 if (!strcmp (s
, ".."))
1411 reading
= mc_opendir (s
);
1416 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
) {
1417 if (!strcmp (next
->d_name
, "."))
1419 if (!strcmp (next
->d_name
, ".."))
1421 path
= concat_dir_and_file (s
, next
->d_name
);
1422 if (mc_lstat (path
, &buf
)) {
1424 mc_closedir (reading
);
1427 if (S_ISDIR (buf
.st_mode
))
1430 (ctx
, path
, progress_count
, progress_bytes
)
1434 erase_file (ctx
, path
, progress_count
, progress_bytes
, 0);
1437 mc_closedir (reading
);
1438 if (return_status
!= FILE_CONT
)
1439 return return_status
;
1440 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1444 while (my_rmdir (s
)) {
1446 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1447 if (return_status
!= FILE_RETRY
)
1448 return return_status
;
1454 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1455 in the directory path points to, 0 else. */
1457 check_dir_is_empty (char *path
)
1463 dir
= mc_opendir (path
);
1467 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)) {
1468 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1469 (d
->d_name
[1] == '.'
1470 && d
->d_name
[2] == '\0')))
1471 continue; /* "." or ".." */
1481 erase_dir (FileOpContext
*ctx
, char *s
, off_t
*progress_count
,
1482 double *progress_bytes
)
1486 if (strcmp (s
, "..") == 0)
1489 if (strcmp (s
, ".") == 0)
1492 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1496 /* The old way to detect a non empty directory was:
1497 error = my_rmdir (s);
1498 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1499 For the linux user space nfs server (nfs-server-2.2beta29-2)
1500 we would have to check also for EIO. I hope the new way is
1501 fool proof. (Norbert)
1503 error
= check_dir_is_empty (s
);
1504 if (error
== 0) { /* not empty */
1505 error
= query_recursive (ctx
, s
);
1506 if (error
== FILE_CONT
)
1507 return recursive_erase (ctx
, s
, progress_count
,
1513 while (my_rmdir (s
) == -1) {
1515 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1516 if (error
!= FILE_RETRY
)
1524 erase_dir_iff_empty (FileOpContext
*ctx
, char *s
)
1528 if (strcmp (s
, "..") == 0)
1531 if (strcmp (s
, ".") == 0)
1534 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1538 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1541 while (my_rmdir (s
)) {
1543 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1544 if (error
!= FILE_RETRY
)
1553 /* {{{ Panel operate routines */
1555 /* Returns currently selected file or the first marked file if there is one */
1557 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1561 if (get_current_type () == view_tree
) {
1562 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1563 char *tree_name
= tree_selected_name (tree
);
1565 mc_stat (tree_name
, stat_buf
);
1569 if (panel
->marked
) {
1570 for (i
= 0; i
< panel
->count
; i
++)
1571 if (panel
->dir
.list
[i
].f
.marked
) {
1572 *stat_buf
= panel
->dir
.list
[i
].buf
;
1573 return panel
->dir
.list
[i
].fname
;
1576 *stat_buf
= panel
->dir
.list
[panel
->selected
].buf
;
1577 return panel
->dir
.list
[panel
->selected
].fname
;
1579 g_assert_not_reached ();
1586 * Computes the number of bytes used by the files in a directory
1589 compute_dir_size (char *dirname
, off_t
*ret_marked
, double *ret_total
)
1592 struct dirent
*dirent
;
1594 dir
= mc_opendir (dirname
);
1599 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1604 if (strcmp (dirent
->d_name
, ".") == 0)
1606 if (strcmp (dirent
->d_name
, "..") == 0)
1609 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1611 res
= mc_lstat (fullname
, &s
);
1618 if (S_ISDIR (s
.st_mode
)) {
1619 off_t subdir_count
= 0;
1620 double subdir_bytes
= 0;
1622 compute_dir_size (fullname
, &subdir_count
, &subdir_bytes
);
1624 *ret_marked
+= subdir_count
;
1625 *ret_total
+= subdir_bytes
;
1628 *ret_total
+= s
.st_size
;
1637 * panel_compute_totals:
1639 * compute the number of files and the number of bytes
1640 * used up by the whole selection, recursing directories
1641 * as required. In addition, it checks to see if it will
1642 * overwrite any files by doing the copy.
1645 panel_compute_totals (WPanel
*panel
, off_t
*ret_marked
, double *ret_total
)
1652 for (i
= 0; i
< panel
->count
; i
++) {
1655 if (!panel
->dir
.list
[i
].f
.marked
)
1658 s
= &panel
->dir
.list
[i
].buf
;
1660 if (S_ISDIR (s
->st_mode
)) {
1662 off_t subdir_count
= 0;
1663 double subdir_bytes
= 0;
1666 concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1667 compute_dir_size (dir_name
, &subdir_count
, &subdir_bytes
);
1669 *ret_marked
+= subdir_count
;
1670 *ret_total
+= subdir_bytes
;
1674 *ret_total
+= s
->st_size
;
1680 * This array introduced to avoid translation problems. The former (op_names)
1681 * is assumed to be nouns, suitable in dialog box titles; this one should
1682 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1683 * Notice first symbol - it is to fool gettext and force these strings to
1684 * be different for it. First symbol is skipped while building a prompt.
1685 * (I don't use spaces around the words, because someday they could be
1686 * dropped, when widgets get smarter)
1688 static char *op_names1
[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1691 int fmd_xlen
= FMD_XLEN
;
1694 * These are formats for building a prompt. Parts encoded as follows:
1695 * %o - operation from op_names1
1696 * %f - file/files or files/directories, as appropriate
1697 * %m - "with source mask" or question mark for delete
1698 * %s - source name (truncated)
1699 * %d - number of marked files
1700 * %e - "to:" or question mark for delete
1702 * xgettext:no-c-format */
1703 static char *one_format
= N_("%o %f \"%s\"%m");
1704 /* xgettext:no-c-format */
1705 static char *many_format
= N_("%o %d %f%m");
1706 static char *prompt_parts
[] = {
1707 N_("file"), N_("files"), N_("directory"), N_("directories"),
1708 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1712 panel_operate_generate_prompt (WPanel
*panel
, int operation
, int only_one
,
1713 struct stat
*src_stat
)
1715 register char *sp
, *cp
;
1717 char format_string
[BUF_MEDIUM
];
1718 char *dp
= format_string
;
1719 char *source
= NULL
;
1722 static int i18n_flag
= 0;
1724 fmd_init_i18n (FALSE
); /* to get proper fmd_xlen */
1726 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1727 op_names1
[i
] = _(op_names1
[i
]);
1729 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1730 prompt_parts
[i
] = _(prompt_parts
[i
]);
1732 one_format
= _(one_format
);
1733 many_format
= _(many_format
);
1736 #endif /* ENABLE_NLS */
1738 sp
= only_one
? one_format
: many_format
;
1741 source
= panel_get_file (panel
, src_stat
);
1749 cp
= op_names1
[operation
] + 1;
1752 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1755 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[6];
1759 cp
= S_ISDIR (src_stat
->st_mode
) ?
1760 prompt_parts
[2] : prompt_parts
[0];
1762 cp
= (panel
->marked
== panel
->dirs_marked
)
1764 : (panel
->dirs_marked
? prompt_parts
[4]
1784 i
= fmd_xlen
- strlen (format_string
) - 4;
1785 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1786 name_trunc (source
, i
));
1788 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
,
1790 i
= strlen (cmd_buf
) + 6 - fmd_xlen
;
1793 fmd_init_i18n (TRUE
); /* to recalculate positions of child widgets */
1803 * Performs one of the operations on the selection on the source_panel
1804 * (copy, delete, move).
1806 * Returns 1 if did change the directory
1807 * structure, Returns 0 if user aborted
1810 panel_operate (void *source_panel
, FileOperation operation
,
1811 char *thedefault
, int ask_user
)
1813 WPanel
*panel
= source_panel
;
1814 #ifdef WITH_FULL_PATHS
1815 char *source_with_path
= NULL
;
1817 # define source_with_path source
1818 #endif /* !WITH_FULL_PATHS */
1819 char *source
= NULL
;
1822 char *save_cwd
= NULL
, *save_dest
= NULL
;
1823 int only_one
= (get_current_type () == view_tree
)
1824 || (panel
->marked
<= 1);
1825 struct stat src_stat
, dst_stat
;
1833 int do_bg
; /* do background operation? */
1835 ctx
= file_op_context_new ();
1838 free_linklist (&linklist
);
1839 free_linklist (&dest_dirs
);
1840 if (get_current_type () == view_listing
)
1841 if (!panel
->marked
&& !strcmp (selection (panel
)->fname
, "..")) {
1842 message (1, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1843 file_op_context_destroy (ctx
);
1847 /* Generate confirmation prompt */
1849 panel_operate_generate_prompt (panel
, operation
, only_one
,
1852 /* Show confirmation dialog */
1853 if (operation
== OP_DELETE
&& confirm_delete
) {
1857 i
= query_dialog (_(op_names
[operation
]), cmd_buf
,
1858 D_ERROR
, 2, _("&Yes"), _("&No"));
1861 file_op_context_destroy (ctx
);
1864 } else if (operation
!= OP_DELETE
) {
1868 if (thedefault
!= NULL
)
1869 dest_dir
= thedefault
;
1870 else if (get_other_type () == view_listing
)
1871 dest_dir
= opanel
->cwd
;
1873 dest_dir
= panel
->cwd
;
1876 file_mask_dialog (ctx
, operation
, cmd_buf
, dest_dir
,
1879 file_op_context_destroy (ctx
);
1883 file_op_context_destroy (ctx
);
1888 char *all
= "^\\(.*\\)$";
1890 re_compile_pattern (all
, strlen (all
), &ctx
->rx
);
1891 ctx
->dest_mask
= g_strdup ("*");
1893 dest
= g_strdup (thedefault
);
1896 #ifdef WITH_BACKGROUND
1897 /* Did the user select to do a background operation? */
1901 v
= do_background (ctx
,
1902 g_strconcat (op_names
[operation
], ": ",
1905 message (1, MSG_ERROR
,
1906 _(" Sorry, I could not put the job in background "));
1909 /* If we are the parent */
1911 mc_setctl (panel
->cwd
, MCCTL_FORGET_ABOUT
, NULL
);
1912 mc_setctl (dest
, MCCTL_FORGET_ABOUT
, NULL
);
1913 /* file_op_context_destroy (ctx); */
1917 #endif /* WITH_BACKGROUND */
1919 /* Initialize things */
1920 /* We do not want to trash cache every time file is
1921 created/touched. However, this will make our cache contain
1924 if (mc_setctl (dest
, MCCTL_WANT_STALE_DATA
, NULL
))
1925 save_dest
= g_strdup (dest
);
1928 if (mc_setctl (panel
->cwd
, MCCTL_WANT_STALE_DATA
, NULL
))
1929 save_cwd
= g_strdup (panel
->cwd
);
1932 /* Now, let's do the job */
1937 file_op_context_create_ui (ctx
, operation
, 1);
1939 /* This code is only called by the tree and panel code */
1941 /* We now have ETA in all cases */
1943 /* One file: FIXME mc_chdir will take user out of any vfs */
1944 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
1945 mc_chdir (PATH_SEP_STR
);
1947 /* The source and src_stat variables have been initialized before */
1948 #ifdef WITH_FULL_PATHS
1949 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1950 #endif /* WITH_FULL_PATHS */
1952 if (operation
== OP_DELETE
) {
1953 if (S_ISDIR (src_stat
.st_mode
))
1954 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1957 erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1959 temp
= transform_source (ctx
, source_with_path
);
1962 value
= transform_error
;
1964 temp
= concat_dir_and_file (dest
, temp
);
1969 switch (operation
) {
1972 * we use file_mask_op_follow_links only with OP_COPY,
1974 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
1976 if (S_ISDIR (src_stat
.st_mode
))
1978 copy_dir_dir (ctx
, source_with_path
, dest
, 1,
1979 0, 0, 0, &count
, &bytes
);
1982 copy_file_file (ctx
, source_with_path
, dest
, 1,
1987 if (S_ISDIR (src_stat
.st_mode
))
1989 move_dir_dir (ctx
, source_with_path
, dest
,
1993 move_file_file (ctx
, source_with_path
, dest
,
1998 /* Unknown file operation */
2002 } /* Copy or move operation */
2004 if (value
== FILE_CONT
)
2005 unmark_files (panel
);
2008 /* Check destination for copy or move operation */
2009 if (operation
!= OP_DELETE
) {
2010 retry_many_dst_stat
:
2011 dst_result
= mc_stat (dest
, &dst_stat
);
2012 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)) {
2014 (_(" Destination \"%s\" must be a directory \n %s "),
2015 dest
) == FILE_RETRY
)
2016 goto retry_many_dst_stat
;
2021 /* Initialize variables for progress bars */
2022 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
2023 panel_compute_totals (panel
, &ctx
->progress_count
,
2024 &ctx
->progress_bytes
);
2025 ctx
->progress_totals_computed
= 1;
2027 ctx
->progress_totals_computed
= 0;
2028 ctx
->progress_count
= panel
->marked
;
2029 ctx
->progress_bytes
= panel
->total
;
2032 /* Loop for every file, perform the actual copy operation */
2033 for (i
= 0; i
< panel
->count
; i
++) {
2034 if (!panel
->dir
.list
[i
].f
.marked
)
2035 continue; /* Skip the unmarked ones */
2037 source
= panel
->dir
.list
[i
].fname
;
2038 src_stat
= panel
->dir
.list
[i
].buf
;
2040 #ifdef WITH_FULL_PATHS
2041 if (source_with_path
)
2042 g_free (source_with_path
);
2043 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2044 #endif /* WITH_FULL_PATHS */
2046 if (operation
== OP_DELETE
) {
2047 if (S_ISDIR (src_stat
.st_mode
))
2049 erase_dir (ctx
, source_with_path
, &count
, &bytes
);
2052 erase_file (ctx
, source_with_path
, &count
, &bytes
,
2058 temp
= transform_source (ctx
, source_with_path
);
2060 value
= transform_error
;
2062 temp
= concat_dir_and_file (dest
, temp
);
2064 switch (operation
) {
2067 * we use file_mask_op_follow_links only with OP_COPY,
2069 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
2070 if (S_ISDIR (src_stat
.st_mode
))
2072 copy_dir_dir (ctx
, source_with_path
, temp
,
2073 1, 0, 0, 0, &count
, &bytes
);
2076 copy_file_file (ctx
, source_with_path
,
2077 temp
, 1, &count
, &bytes
,
2079 free_linklist (&dest_dirs
);
2083 if (S_ISDIR (src_stat
.st_mode
))
2085 move_dir_dir (ctx
, source_with_path
, temp
,
2089 move_file_file (ctx
, source_with_path
,
2090 temp
, &count
, &bytes
);
2094 /* Unknown file operation */
2098 } /* Copy or move operation */
2100 if (value
== FILE_ABORT
)
2103 if (value
== FILE_CONT
)
2104 do_file_mark (panel
, i
, 0);
2106 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
)
2111 && file_progress_show_bytes (ctx
, bytes
,
2112 ctx
->progress_bytes
) ==
2116 if (operation
!= OP_DELETE
&& verbose
2117 && file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2121 } /* Loop for every file */
2127 mc_setctl (save_cwd
, MCCTL_NO_STALE_DATA
, NULL
);
2131 mc_setctl (save_dest
, MCCTL_NO_STALE_DATA
, NULL
);
2135 free_linklist (&linklist
);
2136 free_linklist (&dest_dirs
);
2137 #ifdef WITH_FULL_PATHS
2138 if (source_with_path
)
2139 g_free (source_with_path
);
2140 #endif /* WITH_FULL_PATHS */
2148 if (ctx
->dest_mask
) {
2149 g_free (ctx
->dest_mask
);
2150 ctx
->dest_mask
= NULL
;
2152 #ifdef WITH_BACKGROUND
2153 /* Let our parent know we are saying bye bye */
2154 if (we_are_background
) {
2156 tell_parent (MSG_CHILD_EXITING
);
2159 #endif /* WITH_BACKGROUND */
2161 file_op_context_destroy (ctx
);
2167 /* {{{ Query/status report routines */
2170 real_do_file_error (enum OperationMode mode
, char *error
)
2175 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2177 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2195 /* Report error with one file */
2197 file_error (char *format
, char *file
)
2199 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
,
2200 name_trunc (file
, 30), unix_error_string (errno
));
2202 return do_file_error (cmd_buf
);
2205 /* Report error with two files */
2207 files_error (char *format
, char *file1
, char *file2
)
2212 strcpy (nfile1
, name_trunc (file1
, 15));
2213 strcpy (nfile2
, name_trunc (file2
, 15));
2215 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
, nfile1
, nfile2
,
2216 unix_error_string (errno
));
2218 return do_file_error (cmd_buf
);
2222 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, char *s
)
2226 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2230 _("\n Directory not empty. \n"
2231 " Delete it recursively? ")
2232 : _("\n Background process: Directory not empty \n"
2233 " Delete it recursively? ");
2234 text
= g_strconcat (_(" Delete: "), name_trunc (s
, 30), " ", NULL
);
2238 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2239 _("&Yes"), _("&No"),
2240 _("A&ll"), _("Non&e"),
2243 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2248 switch (ctx
->recursive_result
) {
2250 case RECURSIVE_ALWAYS
:
2254 case RECURSIVE_NEVER
:
2257 case RECURSIVE_ABORT
:
2264 #ifdef WITH_BACKGROUND
2266 do_file_error (char *str
)
2268 if (we_are_background
)
2269 return parent_call (real_do_file_error
, NULL
, 1, strlen (str
),
2272 return real_do_file_error (Foreground
, str
);
2276 query_recursive (FileOpContext
*ctx
, char *s
)
2278 if (we_are_background
)
2279 return parent_call (real_query_recursive
, ctx
, 1, strlen (s
), s
);
2281 return real_query_recursive (ctx
, Foreground
, s
);
2285 query_replace (FileOpContext
*ctx
, char *destname
, struct stat
*_s_stat
,
2286 struct stat
*_d_stat
)
2288 if (we_are_background
)
2289 return parent_call ((void *) file_progress_real_query_replace
,
2292 strlen (destname
), destname
,
2293 sizeof (struct stat
), _s_stat
,
2294 sizeof (struct stat
), _d_stat
);
2296 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2302 do_file_error (char *str
)
2304 return real_do_file_error (Foreground
, str
);
2308 query_recursive (FileOpContext
*ctx
, char *s
)
2310 return real_query_recursive (ctx
, Foreground
, s
);
2314 query_replace (FileOpContext
*ctx
, char *destname
, struct stat
*_s_stat
,
2315 struct stat
*_d_stat
)
2317 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2321 #endif /* !WITH_BACKGROUND */
2324 Cause emacs to enter folding mode for this file: