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"
98 /* rcsid [] = "$Id$" */
103 * Whether the Midnight Commander tries to provide more
104 * information about copy/move sizes and bytes transfered
105 * at the expense of some speed
107 int file_op_compute_totals
= 1;
109 /* If on, it gets a little scrict with dangerous operations */
110 int know_not_what_am_i_doing
= 0;
112 /* This is a hard link cache */
123 /* the hard link cache */
124 struct link
*linklist
= NULL
;
126 /* the files-to-be-erased list */
127 struct link
*erase_list
;
130 * In copy_dir_dir we use two additional single linked lists: The first -
131 * variable name `parent_dirs' - holds information about already copied
132 * directories and is used to detect cyclic symbolic links.
133 * The second (`dest_dirs' below) holds information about just created
134 * target directories and is used to detect when an directory is copied
135 * into itself (we don't want to copy infinitly).
136 * Both lists don't use the linkcount and name structure members of struct
139 struct link
*dest_dirs
= 0;
141 char *op_names
[3] = {
149 static int query_replace (FileOpContext
*ctx
, char *destname
,
150 struct stat
*_s_stat
, struct stat
*_d_stat
);
151 static int query_recursive (FileOpContext
*ctx
, char *s
);
152 static int do_file_error (char *str
);
155 enum CaseConvs
{ NO_CONV
=0, UP_CHAR
=1, LOW_CHAR
=2, UP_SECT
=4, LOW_SECT
=8 };
158 convert_case (int c
, enum CaseConvs
*conversion
)
160 if (*conversion
& UP_CHAR
){
161 *conversion
&= ~UP_CHAR
;
163 } else if (*conversion
& LOW_CHAR
){
164 *conversion
&= ~LOW_CHAR
;
166 } else if (*conversion
& UP_SECT
){
168 } else if (*conversion
& LOW_SECT
){
174 static int transform_error
= 0;
176 static unsigned char *
177 do_transform_source (FileOpContext
*ctx
, unsigned char *source
)
180 unsigned char *fnsource
= x_basename (source
);
182 enum CaseConvs case_conv
= NO_CONV
;
183 static unsigned char fntarget
[MC_MAXPATHLEN
];
185 len
= strlen (fnsource
);
186 j
= re_match (&ctx
->rx
, fnsource
, len
, 0, &ctx
->regs
);
188 transform_error
= FILE_SKIP
;
191 for (next_reg
= 1, j
= 0, k
= 0; j
< strlen (ctx
->dest_mask
); j
++){
192 switch (ctx
->dest_mask
[j
]){
195 if (! isdigit ((unsigned char) ctx
->dest_mask
[j
])){
196 /* Backslash followed by non-digit */
197 switch (ctx
->dest_mask
[j
]){
199 case_conv
|= UP_SECT
;
200 case_conv
&= ~LOW_SECT
;
203 case_conv
|= UP_CHAR
;
206 case_conv
|= LOW_SECT
;
207 case_conv
&= ~UP_SECT
;
210 case_conv
|= LOW_CHAR
;
216 /* Backslash as quote mark */
217 fntarget
[k
++] = 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
]; l
< ctx
->regs
.end
[next_reg
]; l
++)
234 fntarget
[k
++] = convert_case (fnsource
[l
], &case_conv
);
239 fntarget
[k
++] = convert_case (ctx
->dest_mask
[j
], &case_conv
);
247 static unsigned char *
248 transform_source (FileOpContext
*ctx
, unsigned char *source
)
250 unsigned char *s
= g_strdup (source
);
253 /* We remove \n from the filename since regex routines would use \n as an anchor */
254 /* this is just to be allowed to maniupulate file names with \n on it */
255 for (q
= s
; *q
; q
++){
259 q
= do_transform_source (ctx
, s
);
265 free_linklist (struct link
**linklist
)
267 struct link
*lp
, *lp2
;
269 for (lp
= *linklist
; lp
!= NULL
; lp
= lp2
){
277 is_in_linklist (struct link
*lp
, char *path
, struct stat
*sb
)
279 ino_t ino
= sb
->st_ino
;
280 dev_t dev
= sb
->st_dev
;
282 vfs
*vfs
= vfs_type (path
);
289 if (lp
->ino
== ino
&& lp
->dev
== dev
)
297 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
298 * and a hardlink was succesfully made
301 check_hardlinks (char *src_name
, char *dst_name
, struct stat
*pstat
)
304 vfs
*my_vfs
= vfs_type (src_name
);
305 ino_t ino
= pstat
->st_ino
;
306 dev_t dev
= pstat
->st_dev
;
307 struct stat link_stat
;
310 #if 1 /* What will happen if we kill this line? mc_link() will fail on this and it is right behaviour... */
311 if (vfs_file_is_ftp (src_name
) || vfs_file_is_smb (src_name
))
314 for (lp
= linklist
; lp
!= NULL
; lp
= lp
-> next
)
315 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
){
316 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
&&
317 link_stat
.st_dev
== dev
&& vfs_type (lp
->name
) == my_vfs
){
318 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
320 if (vfs_type (dst_name
) == vfs_type (p
)){
321 if (!mc_stat (p
, &link_stat
)){
322 if (!mc_link (p
, dst_name
))
327 message_1s (1, MSG_ERROR
, _(" Could not make the hardlink "));
330 lp
= (struct link
*) g_malloc (sizeof (struct link
) + strlen (src_name
)
331 + strlen (dst_name
) + 1);
336 strcpy (lp
->name
, src_name
);
337 p
= strchr (lp
->name
, 0) + 1;
338 strcpy (p
, dst_name
);
346 * Duplicate the contents of the symbolic link src_path in dst_path.
347 * Try to make a stable symlink if the option "stable symlink" was
348 * set in the file mask dialog.
349 * If dst_path is an existing symlink it will be deleted silently
350 * (upper levels take already care of existing files at dst_path).
353 make_symlink (FileOpContext
*ctx
, char *src_path
, char *dst_path
)
355 char link_target
[MC_MAXPATHLEN
];
361 if (mc_lstat (dst_path
, &sb
) == 0 && S_ISLNK (sb
.st_mode
))
367 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
);
369 return_status
= file_error (_(" Cannot read source link \"%s\" \n %s "), src_path
);
370 if (return_status
== FILE_RETRY
)
371 goto retry_src_readlink
;
372 return return_status
;
374 link_target
[len
] = 0;
376 if (ctx
->stable_symlinks
)
377 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
)) {
378 message_1s (1, MSG_ERROR
,
379 _(" Cannot make stable symlinks across "
380 "non-local filesystems: \n\n"
381 " Option Stable Symlinks will be disabled "));
382 ctx
->stable_symlinks
= 0;
385 if (ctx
->stable_symlinks
&& *link_target
!= PATH_SEP
) {
388 p
= g_strdup (src_path
);
389 r
= strrchr (p
, PATH_SEP
);
393 if (*dst_path
== PATH_SEP
)
394 q
= g_strdup (dst_path
);
396 q
= g_strconcat (p
, dst_path
, NULL
);
397 r
= strrchr (q
, PATH_SEP
);
400 s
= g_strconcat (p
, link_target
, NULL
);
401 strcpy (link_target
, s
);
403 s
= diff_two_paths (q
, link_target
);
405 strcpy (link_target
, s
);
414 if (mc_symlink (link_target
, dst_path
) == 0)
418 * if dst_exists, it is obvious that this had failed.
419 * We can delete the old symlink and try again...
422 if (!mc_unlink (dst_path
))
423 if (mc_symlink (link_target
, dst_path
) == 0)
427 return_status
= file_error (_(" Cannot create target symlink \"%s\" \n %s "), dst_path
);
428 if (return_status
== FILE_RETRY
)
429 goto retry_dst_symlink
;
430 return return_status
;
434 progress_update_one (FileOpContext
*ctx
,
435 off_t
*progress_count
,
436 double *progress_bytes
,
438 int is_toplevel_file
)
442 if (is_toplevel_file
|| ctx
->progress_totals_computed
) {
444 (*progress_bytes
) += add
;
447 /* Apply some heuristic here to not call the update stuff very often */
448 ret
= file_progress_show_count (ctx
, *progress_count
, ctx
->progress_count
);
449 if (ret
!= FILE_CONT
)
451 ret
= file_progress_show_bytes (ctx
, *progress_bytes
, ctx
->progress_bytes
);
457 copy_file_file (FileOpContext
*ctx
, char *src_path
, char *dst_path
, int ask_overwrite
,
458 off_t
*progress_count
, double *progress_bytes
,
459 int is_toplevel_file
)
462 uid_t src_uid
= (uid_t
) -1;
463 gid_t src_gid
= (gid_t
) -1;
464 #endif /* !NATIVE_WIN32 */
466 int buf_size
= BUF_8K
;
467 int src_desc
, dest_desc
= 0;
468 int n_read
, n_written
;
469 int src_mode
= 0; /* The mode of the source file */
472 int dst_exists
= 0, appending
= 0;
473 off_t n_read_total
= 0, file_size
= -1;
474 int return_status
, temp_status
;
475 struct timeval tv_transfer_start
;
477 /* bitmask used to remember which resourses we should release on return
478 A single goto label is much easier to handle than a bunch of gotos ;-). */
479 unsigned resources
= 0;
481 /* FIXME: We should not be using global variables! */
483 return_status
= FILE_RETRY
;
485 if (file_progress_show_source (ctx
, src_path
) == FILE_ABORT
||
486 file_progress_show_target (ctx
, dst_path
) == FILE_ABORT
)
492 if (mc_stat (dst_path
, &sb2
) == 0){
493 if (S_ISDIR (sb2
.st_mode
)){
494 return_status
= file_error (_(" Cannot overwrite directory \"%s\" \n %s "), dst_path
);
495 if (return_status
== FILE_RETRY
)
497 return return_status
;
502 while ((* ctx
->stat_func
) (src_path
, &sb
)) {
503 return_status
= file_error (_(" Cannot stat source file \"%s\" \n %s "), src_path
);
504 if (return_status
!= FILE_RETRY
)
505 return return_status
;
509 /* .ado: For Win32: no st_ino exists, it is better to just try to
510 * overwrite the target file
513 /* Destination already exists */
514 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
){
515 message_3s (1, MSG_ERROR
, _(" `%s' and `%s' are the same file. "), src_path
, dst_path
);
519 #endif /* !NATIVE_WIN32 */
521 /* Should we replace destination? */
524 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
525 if (return_status
!= FILE_CONT
)
526 return return_status
;
530 if (!ctx
->do_append
) {
531 /* .ado: OS2 and NT don't have hardlinks */
533 /* Check the hardlinks */
534 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
535 check_hardlinks (src_path
, dst_path
, &sb
) == 1){
536 /* We have made a hardlink - no more processing is necessary */
537 return return_status
;
540 if (S_ISLNK (sb
.st_mode
)) {
543 retval
= make_symlink (ctx
, src_path
, dst_path
);
547 #endif /* !NATIVE_WIN32 */
549 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) || S_ISFIFO (sb
.st_mode
)
550 || S_ISSOCK (sb
.st_mode
)){
551 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0){
552 return_status
= file_error (_(" Cannot create special file \"%s\" \n %s "), dst_path
);
553 if (return_status
== FILE_RETRY
)
555 return return_status
;
560 while (ctx
->preserve_uidgid
&& mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)){
561 temp_status
= file_error (_(" Cannot chown target file \"%s\" \n %s "), dst_path
);
562 if (temp_status
== FILE_RETRY
)
566 #endif /* !NATIVE_WIN32 */
567 while (ctx
->preserve
&&
568 (mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
) < 0)){
569 temp_status
= file_error (_(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
570 if (temp_status
== FILE_RETRY
)
578 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
580 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0){
581 return_status
= file_error (_(" Cannot open source file \"%s\" \n %s "), src_path
);
582 if (return_status
== FILE_RETRY
)
585 return return_status
;
590 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
){
591 message_1s (1, _(" Warning "), _(" Reget failed, about to overwrite file "));
592 ctx
->do_reget
= ctx
->do_append
= 0;
596 while (mc_fstat (src_desc
, &sb
)){
597 return_status
= file_error (_(" Cannot fstat source file \"%s\" \n %s "), src_path
);
598 if (return_status
== FILE_RETRY
)
603 src_mode
= sb
.st_mode
;
607 #endif /* !NATIVE_WIN32 */
608 utb
.actime
= sb
.st_atime
;
609 utb
.modtime
= sb
.st_mtime
;
610 file_size
= sb
.st_size
;
612 /* Create the new regular file with small permissions initially,
613 do not create a security hole. FIXME: You have security hole
614 here, btw. Imagine copying to /tmp and symlink attack :-( */
616 while ((dest_desc
= mc_open (dst_path
, O_WRONLY
|
617 (ctx
->do_append
? O_APPEND
: (O_CREAT
| O_TRUNC
)), 0600)) < 0){
618 return_status
= file_error (_(" Cannot create target file \"%s\" \n %s "), dst_path
);
619 if (return_status
== FILE_RETRY
)
624 resources
|= 2; /* dst_path exists/dst_path opened */
625 resources
|= 4; /* remove short file */
627 appending
= ctx
->do_append
;
630 /* Find out the optimal buffer size. */
631 while (mc_fstat (dest_desc
, &sb
)){
632 return_status
= file_error (_(" Cannot fstat target file \"%s\" \n %s "), dst_path
);
633 if (return_status
== FILE_RETRY
)
637 buf
= (char *) g_malloc (buf_size
);
642 return_status
= file_progress_show (ctx
, 0, file_size
);
646 if (return_status
!= FILE_CONT
)
650 struct timeval tv_current
, tv_last_update
, tv_last_input
;
651 int secs
, update_secs
;
655 tv_last_update
= tv_transfer_start
;
659 if (mc_ctl (src_desc
, MCCTL_IS_NOTREADY
, 0))
662 while ((n_read
= mc_read (src_desc
, buf
, buf_size
))<0){
663 return_status
= file_error(_(" Cannot read source file \"%s\" \n %s "), src_path
);
664 if (return_status
== FILE_RETRY
)
671 gettimeofday (&tv_current
, NULL
);
674 n_read_total
+= n_read
;
676 /* Windows NT ftp servers report that files have no
677 * permissions: -------, so if we happen to have actually
678 * read something, we should fix the permissions.
681 ((S_IRUSR
|S_IWUSR
|S_IXUSR
) /* user */
682 |(S_IXOTH
|S_IWOTH
|S_IROTH
) /* other */
683 |(S_IXGRP
|S_IWGRP
|S_IRGRP
)))) /* group */
684 src_mode
= S_IRUSR
|S_IWUSR
|S_IROTH
|S_IRGRP
;
685 gettimeofday (&tv_last_input
, NULL
);
688 while ((n_written
= mc_write (dest_desc
, buf
, n_read
)) < n_read
){
693 return_status
= file_error(_(" Cannot write target file \"%s\" \n %s "),
695 if (return_status
== FILE_RETRY
)
701 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
702 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
705 tv_last_update
= tv_current
;
708 /* 2. Check for a stalled condition */
709 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
711 if (update_secs
> 4){
712 stalled_msg
= _("(stalled)");
717 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
720 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
721 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
726 /* 4. Compute BPS rate */
728 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
729 if (ctx
->bps_time
< 1)
731 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
734 file_progress_set_stalled_label (ctx
, stalled_msg
);
735 file_progress_show_bytes (ctx
, *progress_bytes
+ n_read_total
, ctx
->progress_bytes
);
736 return_status
= file_progress_show (ctx
, n_read_total
, file_size
);
738 if (return_status
!= FILE_CONT
)
743 resources
&= ~4; /* copy successful, don't remove target file */
749 while ((resources
& 1) && mc_close (src_desc
) < 0){
750 temp_status
= file_error (_(" Cannot close source file \"%s\" \n %s "), src_path
);
751 if (temp_status
== FILE_RETRY
)
753 if (temp_status
== FILE_ABORT
)
754 return_status
= temp_status
;
758 while ((resources
& 2) && mc_close (dest_desc
) < 0){
759 temp_status
= file_error (_(" Cannot close target file \"%s\" \n %s "), dst_path
);
760 if (temp_status
== FILE_RETRY
)
762 return_status
= temp_status
;
767 /* Remove short file */
769 result
= query_dialog (_("Copy"), _("Incomplete file was retrieved. Keep it?"),
770 D_ERROR
, 2, _("&Delete"), _("&Keep"));
772 mc_unlink (dst_path
);
773 } else if (resources
& (2|8)){
774 /* no short file and destination file exists */
776 if (!appending
&& ctx
->preserve_uidgid
){
777 while (mc_chown (dst_path
, src_uid
, src_gid
)){
778 temp_status
= file_error
779 (_(" Cannot chown target file \"%s\" \n %s "), dst_path
);
780 if (temp_status
== FILE_RETRY
)
782 return_status
= temp_status
;
786 #endif /* !NATIVE_WIN32 */
789 * .ado: according to the XPG4 standard, the file must be closed before
790 * chmod can be invoked
792 if (!appending
&& ctx
->preserve
){
793 while (mc_chmod (dst_path
, src_mode
& ctx
->umask_kill
)){
794 temp_status
= file_error (
795 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
796 if (temp_status
!= FILE_RETRY
){
797 return_status
= temp_status
;
801 mc_utime (dst_path
, &utb
);
805 if (return_status
== FILE_CONT
)
806 return_status
= progress_update_one (ctx
, progress_count
, progress_bytes
,
807 file_size
, is_toplevel_file
);
809 return return_status
;
813 * I think these copy_*_* functions should have a return type.
814 * anyway, this function *must* have two directories as arguments.
816 /* FIXME: This function needs to check the return values of the
819 copy_dir_dir (FileOpContext
*ctx
, char *s
, char *d
, int toplevel
,
820 int move_over
, int delete,
821 struct link
*parent_dirs
,
822 off_t
*progress_count
,
823 double *progress_bytes
)
826 struct stat buf
, cbuf
;
828 char *path
, *mdpath
, *dest_file
, *dest_dir
;
829 int return_status
= FILE_CONT
;
833 /* First get the mode of the source dir */
835 if ((* ctx
->stat_func
) (s
, &cbuf
)){
836 return_status
= file_error (_(" Cannot stat source directory \"%s\" \n %s "), s
);
837 if (return_status
== FILE_RETRY
)
839 return return_status
;
842 if (is_in_linklist (dest_dirs
, s
, &cbuf
)){
843 /* Don't copy a directory we created before (we don't want to copy
844 infinitely if a directory is copied into itself) */
845 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
849 /* Hmm, hardlink to directory??? - Norbert */
850 /* FIXME: In this step we should do something
851 in case the destination already exist */
852 /* Check the hardlinks */
853 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (s
, d
, &cbuf
) == 1){
854 /* We have made a hardlink - no more processing is necessary */
855 return return_status
;
858 if (!S_ISDIR (cbuf
.st_mode
)){
859 return_status
= file_error (_(" Source directory \"%s\" is not a directory \n %s "), s
);
860 if (return_status
== FILE_RETRY
)
862 return return_status
;
865 if (is_in_linklist (parent_dirs
, s
, &cbuf
)){
866 /* we found a cyclic symbolic link */
867 message_2s (1, MSG_ERROR
, _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
871 lp
= g_new (struct link
, 1);
872 lp
->vfs
= vfs_type (s
);
873 lp
->ino
= cbuf
.st_ino
;
874 lp
->dev
= cbuf
.st_dev
;
875 lp
->next
= parent_dirs
;
879 /* Now, check if the dest dir exists, if not, create it. */
880 if (mc_stat (d
, &buf
)){
881 /* Here the dir doesn't exist : make it !*/
884 if (mc_rename (s
, d
) == 0){
885 g_free (parent_dirs
);
889 dest_dir
= g_strdup (d
);
892 * If the destination directory exists, we want to copy the whole
893 * directory, but we only want this to happen once.
895 * Escape sequences added to the * to compiler warnings.
896 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
897 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
899 if (!S_ISDIR (buf
.st_mode
)){
900 return_status
= file_error (_(" Destination \"%s\" must be a directory \n %s "), d
);
901 if (return_status
== FILE_RETRY
)
903 g_free (parent_dirs
);
904 return return_status
;
907 /* Again, I'm getting curious. Is not d already what we wanted, incl.
908 * masked source basename? Is not this just a relict of the past versions?
909 * I'm afraid this will lead into a two level deep dive :(
911 * I think this is indeed the problem. I can not remember any case where
912 * we actually would like that behaviour -miguel
914 * It's a documented feature (option `Dive into subdir if exists' in the
915 * copy/move dialog). -Norbert
917 if (toplevel
&& ctx
->dive_into_subdirs
){
918 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
922 dest_dir
= g_strdup (d
);
927 if (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)){
928 return_status
= file_error (_(" Cannot create target directory \"%s\" \n %s "), dest_dir
);
929 if (return_status
== FILE_RETRY
)
930 goto retry_dst_mkdir
;
934 lp
= g_new (struct link
, 1);
935 mc_stat (dest_dir
, &buf
);
936 lp
->vfs
= vfs_type (dest_dir
);
937 lp
->ino
= buf
.st_ino
;
938 lp
->dev
= buf
.st_dev
;
939 lp
->next
= dest_dirs
;
943 if (ctx
->preserve_uidgid
){
945 if (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)){
946 return_status
= file_error (_(" Cannot chown target directory \"%s\" \n %s "), dest_dir
);
947 if (return_status
== FILE_RETRY
)
948 goto retry_dst_chown
;
952 #endif /* !NATIVE_WIN32 */
955 /* open the source dir for reading */
956 if ((reading
= mc_opendir (s
)) == 0){
960 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
){
962 * Now, we don't want '.' and '..' to be created / copied at any time
964 if (!strcmp (next
->d_name
, "."))
966 if (!strcmp (next
->d_name
, ".."))
969 /* get the filename and add it to the src directory */
970 path
= concat_dir_and_file (s
, next
->d_name
);
972 (* ctx
->stat_func
) (path
, &buf
);
973 if (S_ISDIR (buf
.st_mode
)){
974 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
976 * From here, we just intend to recursively copy subdirs, not
977 * the double functionality of copying different when the target
978 * dir already exists. So, we give the recursive call the flag 0
979 * meaning no toplevel.
981 return_status
= copy_dir_dir (ctx
, path
, mdpath
, 0, 0,
982 delete, parent_dirs
, progress_count
, progress_bytes
);
985 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
986 return_status
= copy_file_file (ctx
, path
, dest_file
, 1,
987 progress_count
, progress_bytes
, 0);
990 if (delete && return_status
== FILE_CONT
){
991 if (ctx
->erase_at_end
){
992 static struct link
*tail
;
993 lp
= g_malloc (sizeof (struct link
) + strlen (path
));
994 strcpy (lp
->name
, path
);
995 lp
->st_mode
= buf
.st_mode
;
1001 erase_list
= tail
= lp
;
1003 if (S_ISDIR (buf
.st_mode
)){
1004 return_status
= erase_dir_iff_empty (ctx
, path
);
1006 return_status
= erase_file (ctx
, path
, 0, 0, 0);
1012 mc_closedir (reading
);
1015 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1016 utb
.actime
= cbuf
.st_atime
;
1017 utb
.modtime
= cbuf
.st_mtime
;
1018 mc_utime(dest_dir
, &utb
);
1023 g_free (parent_dirs
);
1024 return return_status
;
1029 /* {{{ Move routines */
1032 move_file_file (FileOpContext
*ctx
, char *s
, char *d
,
1033 off_t
*progress_count
, double *progress_bytes
)
1035 struct stat src_stats
, dst_stats
;
1036 int return_status
= FILE_CONT
;
1038 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
1039 || file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1045 if (mc_lstat (s
, &src_stats
) != 0){
1046 /* Source doesn't exist */
1047 return_status
= file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1048 if (return_status
== FILE_RETRY
)
1049 goto retry_src_lstat
;
1050 return return_status
;
1053 if (mc_lstat (d
, &dst_stats
) == 0){
1054 /* Destination already exists */
1055 /* .ado: for Win32, no st_ino exists */
1056 #ifndef NATIVE_WIN32
1057 if (src_stats
.st_dev
== dst_stats
.st_dev
1058 && src_stats
.st_ino
== dst_stats
.st_ino
){
1059 int msize
= COLS
- 36;
1060 char st
[MC_MAXPATHLEN
];
1061 char dt
[MC_MAXPATHLEN
];
1067 strcpy (st
, name_trunc (s
, msize
));
1068 strcpy (dt
, name_trunc (d
, msize
));
1069 message_3s (1, MSG_ERROR
, _(" `%s' and `%s' are the same file "),
1074 #endif /* !NATIVE_WIN32 */
1075 if (S_ISDIR (dst_stats
.st_mode
)){
1076 message_2s (1, MSG_ERROR
, _(" Cannot overwrite directory `%s' "), d
);
1081 if (confirm_overwrite
){
1082 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1083 if (return_status
!= FILE_CONT
)
1084 return return_status
;
1086 /* Ok to overwrite */
1089 if (!ctx
->do_append
) {
1090 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1091 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1092 goto retry_src_remove
;
1094 return return_status
;
1097 if (mc_rename (s
, d
) == 0){
1102 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1103 one nfs to the same, but on the server it is on two different
1104 filesystems. Then nfs returns EIO instead of EXDEV.
1105 Hope it will not hurt if we always in case of error try to copy/delete. */
1107 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1109 if (errno
!= EXDEV
){
1110 return_status
= files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
, d
);
1111 if (return_status
== FILE_RETRY
)
1113 return return_status
;
1117 /* Failed because filesystem boundary -> copy the file instead */
1118 return_status
= copy_file_file (ctx
, s
, d
, 0, progress_count
, progress_bytes
, 1);
1119 if (return_status
!= FILE_CONT
)
1120 return return_status
;
1122 if ((return_status
= file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1123 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1124 return return_status
;
1130 return_status
= file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1131 if (return_status
== FILE_RETRY
)
1132 goto retry_src_remove
;
1133 return return_status
;
1136 if (return_status
== FILE_CONT
)
1137 return_status
= progress_update_one (ctx
,
1139 progress_bytes
, src_stats
.st_size
, 1);
1141 return return_status
;
1145 move_dir_dir (FileOpContext
*ctx
, char *s
, char *d
,
1146 off_t
*progress_count
, double *progress_bytes
)
1148 struct stat sbuf
, dbuf
, destbuf
;
1154 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
||
1155 file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1161 if (mc_stat (d
, &dbuf
))
1162 destdir
= g_strdup (d
); /* destination doesn't exist */
1163 else if (!ctx
->dive_into_subdirs
){
1164 destdir
= g_strdup (d
);
1167 destdir
= concat_dir_and_file (d
, x_basename (s
));
1168 #ifndef NATIVE_WIN32
1169 if (sbuf
.st_dev
== dbuf
.st_dev
1170 && sbuf
.st_ino
== dbuf
.st_ino
){
1171 int msize
= COLS
- 36;
1172 char st
[MC_MAXPATHLEN
];
1173 char dt
[MC_MAXPATHLEN
];
1179 strcpy (st
, name_trunc (s
, msize
));
1180 strcpy (dt
, name_trunc (d
, msize
));
1181 message_3s (1, MSG_ERROR
, _(" `%s' and `%s' are the same directory "),
1186 #endif /* !NATIVE_WIN32 */
1188 /* Check if the user inputted an existing dir */
1190 if (!mc_stat (destdir
, &destbuf
)){
1192 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 1, 1, 0,
1193 progress_count
, progress_bytes
);
1195 if (return_status
!= FILE_CONT
)
1199 if (S_ISDIR (destbuf
.st_mode
))
1200 return_status
= file_error (_(" Cannot overwrite directory \"%s\" %s "), destdir
);
1202 return_status
= file_error (_(" Cannot overwrite file \"%s\" %s "), destdir
);
1203 if (return_status
== FILE_RETRY
)
1204 goto retry_dst_stat
;
1207 return return_status
;
1211 if (mc_rename (s
, destdir
) == 0){
1212 return_status
= FILE_CONT
;
1215 /* .ado: Drive, Do we need this anymore? */
1218 /* EXDEV: cross device; does not work everywhere */
1219 if (toupper(s
[0]) != toupper(destdir
[0]))
1224 if (errno
!= EXDEV
){
1225 return_status
= files_error (_(" Cannot move directory \"%s\" to \"%s\" \n %s "), s
, d
);
1226 if (return_status
== FILE_RETRY
)
1233 /* Failed because of filesystem boundary -> copy dir instead */
1234 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 0, 1, 0, progress_count
, progress_bytes
);
1236 if (return_status
!= FILE_CONT
)
1239 if ((return_status
= file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1240 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1244 if (ctx
->erase_at_end
){
1245 for (; erase_list
&& return_status
!= FILE_ABORT
;){
1246 if (S_ISDIR (erase_list
->st_mode
)){
1247 return_status
= erase_dir_iff_empty (ctx
, erase_list
->name
);
1249 return_status
= erase_file (ctx
, erase_list
->name
, 0, 0, 0);
1251 erase_list
= erase_list
->next
;
1255 erase_dir_iff_empty (ctx
, s
);
1261 erase_list
= erase_list
->next
;
1264 return return_status
;
1269 /* {{{ Erase routines */
1270 /* Don't update progress status if progress_count==NULL */
1272 erase_file (FileOpContext
*ctx
, char *s
, off_t
*progress_count
, double *progress_bytes
,
1273 int is_toplevel_file
)
1278 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1282 if (progress_count
&& mc_lstat (s
, &buf
)) {
1283 /* ignore, most likely the mc_unlink fails, too */
1287 while (mc_unlink (s
)){
1288 return_status
= file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1289 if (return_status
!= FILE_RETRY
)
1290 return return_status
;
1294 return progress_update_one (ctx
, progress_count
, progress_bytes
, buf
.st_size
,
1301 recursive_erase (FileOpContext
*ctx
, char *s
, off_t
*progress_count
, double *progress_bytes
)
1303 struct dirent
*next
;
1307 int return_status
= FILE_CONT
;
1309 if (!strcmp (s
, ".."))
1312 reading
= mc_opendir (s
);
1317 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
){
1318 if (!strcmp (next
->d_name
, "."))
1320 if (!strcmp (next
->d_name
, ".."))
1322 path
= concat_dir_and_file (s
, next
->d_name
);
1323 if (mc_lstat (path
, &buf
)){
1325 mc_closedir (reading
);
1328 if (S_ISDIR (buf
.st_mode
))
1329 return_status
= (recursive_erase (ctx
, path
, progress_count
, progress_bytes
)
1332 return_status
= erase_file (ctx
, path
, progress_count
, progress_bytes
, 0);
1335 mc_closedir (reading
);
1336 if (return_status
!= FILE_CONT
)
1337 return return_status
;
1338 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1342 while (my_rmdir (s
)){
1343 return_status
= file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1344 if (return_status
!= FILE_RETRY
)
1345 return return_status
;
1351 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1352 in the directory path points to, 0 else. */
1354 check_dir_is_empty(char *path
)
1360 dir
= mc_opendir (path
);
1364 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)){
1365 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1366 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
1367 continue; /* "." or ".." */
1377 erase_dir (FileOpContext
*ctx
, char *s
, off_t
*progress_count
, double *progress_bytes
)
1381 if (strcmp (s
, "..") == 0)
1384 if (strcmp (s
, ".") == 0)
1387 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1391 /* The old way to detect a non empty directory was:
1392 error = my_rmdir (s);
1393 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1394 For the linux user space nfs server (nfs-server-2.2beta29-2)
1395 we would have to check also for EIO. I hope the new way is
1396 fool proof. (Norbert)
1398 error
= check_dir_is_empty (s
);
1399 if (error
== 0){ /* not empty */
1400 error
= query_recursive (ctx
, s
);
1401 if (error
== FILE_CONT
)
1402 return recursive_erase (ctx
, s
, progress_count
, progress_bytes
);
1407 while (my_rmdir (s
) == -1){
1408 error
= file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1409 if (error
!= FILE_RETRY
)
1417 erase_dir_iff_empty (FileOpContext
*ctx
, char *s
)
1421 if (strcmp (s
, "..") == 0)
1424 if (strcmp (s
, ".") == 0)
1427 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1431 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1434 while (my_rmdir (s
)){
1435 error
= file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1436 if (error
!= FILE_RETRY
)
1445 /* {{{ Panel operate routines */
1447 /* Returns currently selected file or the first marked file if there is one */
1449 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1453 /* No problem with Gnome, as get_current_type never returns view_tree there */
1454 if (get_current_type () == view_tree
){
1455 WTree
*tree
= (WTree
*)get_panel_widget (get_current_index ());
1457 mc_stat (tree
->selected_ptr
->name
, stat_buf
);
1458 return tree
->selected_ptr
->name
;
1462 for (i
= 0; i
< panel
->count
; i
++)
1463 if (panel
->dir
.list
[i
].f
.marked
){
1464 *stat_buf
= panel
->dir
.list
[i
].buf
;
1465 return panel
->dir
.list
[i
].fname
;
1468 *stat_buf
= panel
->dir
.list
[panel
->selected
].buf
;
1469 return panel
->dir
.list
[panel
->selected
].fname
;
1471 g_assert_not_reached ();
1476 is_wildcarded (char *p
)
1481 else if (*p
== '\\' && p
[1] >= '1' && p
[1] <= '9')
1490 * Computes the number of bytes used by the files in a directory
1493 compute_dir_size (char *dirname
, off_t
*ret_marked
, double *ret_total
)
1496 struct dirent
*dirent
;
1498 dir
= mc_opendir (dirname
);
1503 while ((dirent
= mc_readdir (dir
)) != NULL
){
1508 if (strcmp (dirent
->d_name
, ".") == 0)
1510 if (strcmp (dirent
->d_name
, "..") == 0)
1513 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1515 res
= mc_lstat (fullname
, &s
);
1522 if (S_ISDIR (s
.st_mode
)){
1523 off_t subdir_count
= 0;
1524 double subdir_bytes
= 0;
1526 compute_dir_size (fullname
, &subdir_count
, &subdir_bytes
);
1528 *ret_marked
+= subdir_count
;
1529 *ret_total
+= subdir_bytes
;
1532 *ret_total
+= s
.st_size
;
1541 * panel_compute_totals:
1543 * compute the number of files and the number of bytes
1544 * used up by the whole selection, recursing directories
1545 * as required. In addition, it checks to see if it will
1546 * overwrite any files by doing the copy.
1549 panel_compute_totals (WPanel
*panel
, off_t
*ret_marked
, double *ret_total
)
1556 for (i
= 0; i
< panel
->count
; i
++){
1559 if (!panel
->dir
.list
[i
].f
.marked
)
1562 s
= &panel
->dir
.list
[i
].buf
;
1564 if (S_ISDIR (s
->st_mode
)){
1566 off_t subdir_count
= 0;
1567 double subdir_bytes
= 0;
1569 dir_name
= concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1570 compute_dir_size (dir_name
, &subdir_count
, &subdir_bytes
);
1572 *ret_marked
+= subdir_count
;
1573 *ret_total
+= subdir_bytes
;
1577 *ret_total
+= s
->st_size
;
1583 * This array introduced to avoid translation problems. The former (op_names)
1584 * is assumed to be nouns, suitable in dialog box titles; this one should
1585 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1586 * Notice first symbol - it is to fool gettext and force these strings to
1587 * be different for it. First symbol is skipped while building a prompt.
1588 * (I don't use spaces around the words, because someday they could be
1589 * dropped, when widgets get smarter)
1591 static char *op_names1
[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1594 int fmd_xlen
= FMD_XLEN
;
1597 * These are formats for building a prompt. Parts encoded as follows:
1598 * %o - operation from op_names1
1599 * %f - file/files or files/directories, as appropriate
1600 * %m - "with source mask" or question mark for delete
1601 * %s - source name (truncated)
1602 * %d - number of marked files
1603 * %e - "to:" or question mark for delete
1605 * xgettext:no-c-format */
1606 static char* one_format
= N_("%o %f \"%s\"%m");
1607 /* xgettext:no-c-format */
1608 static char* many_format
= N_("%o %d %f%m");
1609 static char* prompt_parts
[] =
1611 N_("file"), N_("files"), N_("directory"), N_("directories"),
1612 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1616 panel_operate_generate_prompt (WPanel
* panel
, int operation
, int only_one
,
1617 struct stat
* src_stat
)
1619 register char *sp
, *cp
;
1621 char format_string
[BUF_MEDIUM
];
1622 char *dp
= format_string
;
1623 char* source
= NULL
;
1626 static int i18n_flag
= 0;
1629 fmd_init_i18n (FALSE
); /* to get proper fmd_xlen */
1631 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1632 op_names1
[i
] = _(op_names1
[i
]);
1634 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1635 prompt_parts
[i
] = _(prompt_parts
[i
]);
1637 one_format
= _(one_format
);
1638 many_format
= _(many_format
);
1641 #endif /* ENABLE_NLS */
1643 sp
= only_one
? one_format
: many_format
;
1646 source
= panel_get_file (panel
, src_stat
);
1657 cp
= op_names1
[operation
] + 1;
1660 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1663 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[6];
1668 cp
= S_ISDIR (src_stat
->st_mode
) ?
1669 prompt_parts
[2] : prompt_parts
[0];
1673 cp
= (panel
->marked
== panel
->dirs_marked
)
1675 : (panel
->dirs_marked
? prompt_parts
[4]
1676 : prompt_parts
[1]);
1697 i
= fmd_xlen
- strlen(format_string
) - 4;
1698 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
, name_trunc (source
, i
));
1702 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
, panel
->marked
);
1703 i
= strlen (cmd_buf
) + 6 - fmd_xlen
;
1707 fmd_init_i18n (TRUE
); /* to recalculate positions of child widgets */
1717 * Performs one of the operations on the selection on the source_panel
1718 * (copy, delete, move).
1720 * Returns 1 if did change the directory
1721 * structure, Returns 0 if user aborted
1724 panel_operate (void *source_panel
, FileOperation operation
, char *thedefault
, int ask_user
)
1726 WPanel
*panel
= source_panel
;
1727 #ifdef WITH_FULL_PATHS
1728 char *source_with_path
= NULL
;
1730 # define source_with_path source
1731 #endif /* !WITH_FULL_PATHS */
1732 char *source
= NULL
;
1735 char *save_cwd
= NULL
, *save_dest
= NULL
;
1736 int only_one
= (get_current_type () == view_tree
) || (panel
->marked
<= 1);
1737 struct stat src_stat
, dst_stat
;
1745 int do_bg
; /* do background operation? */
1747 ctx
= file_op_context_new ();
1750 ctx
->rx
.buffer
= NULL
;
1751 free_linklist (&linklist
);
1752 free_linklist (&dest_dirs
);
1753 if (get_current_type () == view_listing
)
1754 if (!panel
->marked
&& !strcmp (selection (panel
)->fname
, "..")){
1755 message (1, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1756 file_op_context_destroy (ctx
);
1760 if (operation
< OP_COPY
|| operation
> OP_DELETE
) {
1761 file_op_context_destroy (ctx
);
1765 /* Generate confirmation prompt */
1766 source
= panel_operate_generate_prompt (panel
, operation
, only_one
, &src_stat
);
1768 /* Show confirmation dialog */
1769 if (operation
== OP_DELETE
&& confirm_delete
){
1770 if (know_not_what_am_i_doing
)
1773 i
= query_dialog (_(op_names
[operation
]), cmd_buf
,
1774 D_ERROR
, 2, _("&Yes"), _("&No"));
1777 file_op_context_destroy (ctx
);
1780 } else if (operation
!= OP_DELETE
){
1781 ctx
->rx
.buffer
= (char *) g_malloc (MC_MAXPATHLEN
);
1782 ctx
->rx
.allocated
= MC_MAXPATHLEN
;
1783 ctx
->rx
.translate
= 0;
1788 if (thedefault
!= NULL
)
1789 dest_dir
= thedefault
;
1790 else if (get_other_type () == view_listing
)
1791 dest_dir
= opanel
->cwd
;
1793 dest_dir
= panel
->cwd
;
1795 dest
= file_mask_dialog (ctx
, operation
, cmd_buf
, dest_dir
, only_one
, &do_bg
);
1797 g_free (ctx
->rx
.buffer
);
1798 file_op_context_destroy (ctx
);
1802 g_free (ctx
->rx
.buffer
);
1803 file_op_context_destroy (ctx
);
1808 char *all
= "^\\(.*\\)$";
1810 re_compile_pattern (all
, strlen (all
), &ctx
->rx
);
1811 ctx
->dest_mask
= g_strdup ("*");
1813 dest
= g_strdup (thedefault
);
1817 #ifdef WITH_BACKGROUND
1818 /* Did the user select to do a background operation? */
1822 v
= do_background (ctx
, g_strconcat (op_names
[operation
], ": ", panel
->cwd
, NULL
));
1824 message (1, MSG_ERROR
, _(" Sorry, I could not put the job in background "));
1827 /* If we are the parent */
1829 mc_setctl (panel
->cwd
, MCCTL_FORGET_ABOUT
, NULL
);
1830 mc_setctl (dest
, MCCTL_FORGET_ABOUT
, NULL
);
1831 /* file_op_context_destroy (ctx); */
1835 #endif /* WITH_BACKGROUND */
1837 /* Initialize things */
1838 /* We do not want to trash cache every time file is
1839 created/touched. However, this will make our cache contain
1842 if (mc_setctl (dest
, MCCTL_WANT_STALE_DATA
, NULL
))
1843 save_dest
= g_strdup (dest
);
1846 if (mc_setctl (panel
->cwd
, MCCTL_WANT_STALE_DATA
, NULL
))
1847 save_cwd
= g_strdup (panel
->cwd
);
1850 /* Now, let's do the job */
1855 file_op_context_create_ui (ctx
, operation
, 1);
1857 /* This code is only called by the tree and panel code */
1859 /* We now have ETA in all cases */
1861 /* One file: FIXME mc_chdir will take user out of any vfs */
1862 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
1863 mc_chdir (PATH_SEP_STR
);
1865 /* The source and src_stat variables have been initialized before */
1866 #ifdef WITH_FULL_PATHS
1867 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1868 #endif /* WITH_FULL_PATHS */
1870 if (operation
== OP_DELETE
) {
1871 if (S_ISDIR (src_stat
.st_mode
))
1872 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1874 value
= erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1876 temp
= transform_source (ctx
, source_with_path
);
1879 value
= transform_error
;
1881 temp
= concat_dir_and_file (dest
, temp
);
1886 switch (operation
) {
1889 * we use file_mask_op_follow_links only with OP_COPY,
1891 (* ctx
->stat_func
) (source_with_path
, &src_stat
);
1893 if (S_ISDIR (src_stat
.st_mode
))
1894 value
= copy_dir_dir (ctx
, source_with_path
, dest
, 1, 0, 0, 0,
1897 value
= copy_file_file (ctx
, source_with_path
, dest
, 1, &count
, &bytes
, 1);
1901 if (S_ISDIR (src_stat
.st_mode
))
1902 value
= move_dir_dir (ctx
, source_with_path
, dest
, &count
, &bytes
);
1904 value
= move_file_file (ctx
, source_with_path
, dest
, &count
, &bytes
);
1909 message_1s (1, _(" Internal failure "), _(" Unknown file operation "));
1912 } /* Copy or move operation */
1914 if (value
== FILE_CONT
)
1915 unmark_files (panel
);
1918 /* Check destination for copy or move operation */
1919 if (operation
!= OP_DELETE
){
1920 retry_many_dst_stat
:
1921 dst_result
= mc_stat (dest
, &dst_stat
);
1922 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)){
1923 if (file_error (_(" Destination \"%s\" must be a directory \n %s "),
1924 dest
) == FILE_RETRY
)
1925 goto retry_many_dst_stat
;
1930 /* Initialize variables for progress bars */
1931 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
1932 panel_compute_totals (panel
, &ctx
->progress_count
, &ctx
->progress_bytes
);
1933 ctx
->progress_totals_computed
= 1;
1935 ctx
->progress_totals_computed
= 0;
1936 ctx
->progress_count
= panel
->marked
;
1937 ctx
->progress_bytes
= panel
->total
;
1940 /* Loop for every file, perform the actual copy operation */
1941 for (i
= 0; i
< panel
->count
; i
++) {
1942 if (!panel
->dir
.list
[i
].f
.marked
)
1943 continue; /* Skip the unmarked ones */
1945 source
= panel
->dir
.list
[i
].fname
;
1946 src_stat
= panel
->dir
.list
[i
].buf
;
1948 #ifdef WITH_FULL_PATHS
1949 if (source_with_path
)
1950 g_free (source_with_path
);
1951 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1952 #endif /* WITH_FULL_PATHS */
1954 if (operation
== OP_DELETE
){
1955 if (S_ISDIR (src_stat
.st_mode
))
1956 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1958 value
= erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1963 temp
= transform_source (ctx
, source_with_path
);
1965 value
= transform_error
;
1967 temp
= concat_dir_and_file (dest
, temp
);
1972 * we use file_mask_op_follow_links only with OP_COPY,
1974 (* ctx
->stat_func
) (source_with_path
, &src_stat
);
1975 if (S_ISDIR (src_stat
.st_mode
))
1976 value
= copy_dir_dir (ctx
, source_with_path
, temp
, 1, 0, 0, 0,
1979 value
= copy_file_file (ctx
, source_with_path
, temp
, 1,
1981 free_linklist (&dest_dirs
);
1985 if (S_ISDIR (src_stat
.st_mode
))
1986 value
= move_dir_dir (ctx
, source_with_path
, temp
, &count
, &bytes
);
1988 value
= move_file_file (ctx
, source_with_path
, temp
, &count
, &bytes
);
1992 message_1s (1, _(" Internal failure "), _(" Unknown file operation "));
1996 } /* Copy or move operation */
1998 if (value
== FILE_ABORT
)
2001 if (value
== FILE_CONT
)
2002 do_file_mark (panel
, i
, 0);
2004 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
) == FILE_ABORT
)
2007 if (verbose
&& file_progress_show_bytes (ctx
, bytes
, ctx
->progress_bytes
) == FILE_ABORT
)
2010 if (operation
!= OP_DELETE
&& verbose
&& file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2014 } /* Loop for every file */
2020 mc_setctl (save_cwd
, MCCTL_NO_STALE_DATA
, NULL
);
2024 mc_setctl (save_dest
, MCCTL_NO_STALE_DATA
, NULL
);
2028 free_linklist (&linklist
);
2029 free_linklist (&dest_dirs
);
2030 #ifdef WITH_FULL_PATHS
2031 if (source_with_path
)
2032 g_free (source_with_path
);
2033 #endif /* WITH_FULL_PATHS */
2041 if (ctx
->rx
.buffer
) {
2042 g_free (ctx
->rx
.buffer
);
2043 ctx
->rx
.buffer
= NULL
;
2046 if (ctx
->dest_mask
) {
2047 g_free (ctx
->dest_mask
);
2048 ctx
->dest_mask
= NULL
;
2051 #ifdef WITH_BACKGROUND
2052 /* Let our parent know we are saying bye bye */
2053 if (we_are_background
) {
2055 tell_parent (MSG_CHILD_EXITING
);
2058 #endif /* WITH_BACKGROUND */
2060 file_op_context_destroy (ctx
);
2066 /* {{{ Query/status report routines */
2069 real_do_file_error (enum OperationMode mode
, char *error
)
2074 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2075 result
= query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"), _("&Abort"));
2092 /* Report error with one file */
2094 file_error (char *format
, char *file
)
2096 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
,
2097 name_trunc (file
, 30), unix_error_string (errno
));
2099 return do_file_error (cmd_buf
);
2102 /* Report error with two files */
2104 files_error (char *format
, char *file1
, char *file2
)
2109 strcpy (nfile1
, name_trunc (file1
, 15));
2110 strcpy (nfile2
, name_trunc (file2
, 15));
2112 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
, nfile1
, nfile2
,
2113 unix_error_string (errno
));
2115 return do_file_error (cmd_buf
);
2119 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, char *s
)
2124 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
){
2126 mode
== Foreground
? _("\n Directory not empty. \n Delete it recursively? ")
2127 : _("\n Background process: Directory not empty \n Delete it recursively? ");
2128 text
= g_strconcat (_(" Delete: "), name_trunc (s
, 30), " ", NULL
);
2130 if (know_not_what_am_i_doing
)
2132 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2133 _("&Yes"), _("&No"),
2134 _("a&ll"), _("non&E"),
2137 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2140 if (know_not_what_am_i_doing
){
2141 if (ctx
->recursive_result
== RECURSIVE_YES
||
2142 ctx
->recursive_result
== RECURSIVE_ALWAYS
){
2143 text
= g_strconcat (
2144 _(" Type 'yes' if you REALLY want to delete "),
2145 ctx
->recursive_result
== RECURSIVE_YES
2146 ? name_trunc (s
, 19) : _("all the directories "), " ", NULL
);
2147 confirm
= input_dialog (
2148 mode
== Foreground
? _(" Recursive Delete ")
2149 : _(" Background process: Recursive Delete "),
2152 if (!confirm
|| strcmp (confirm
, _("yes")))
2153 ctx
->recursive_result
= RECURSIVE_NEVER
;
2160 switch (ctx
->recursive_result
){
2162 case RECURSIVE_ALWAYS
:
2166 case RECURSIVE_NEVER
:
2169 case RECURSIVE_ABORT
:
2176 #ifdef WITH_BACKGROUND
2178 do_file_error (char *str
)
2180 if (we_are_background
)
2181 return parent_call (real_do_file_error
, NULL
, 1, strlen (str
), str
);
2183 return real_do_file_error (Foreground
, str
);
2187 query_recursive (FileOpContext
*ctx
, char *s
)
2189 if (we_are_background
)
2190 return parent_call (real_query_recursive
, ctx
, 1, strlen (s
), s
);
2192 return real_query_recursive (ctx
, Foreground
, s
);
2196 query_replace (FileOpContext
*ctx
, char *destname
, struct stat
*_s_stat
, struct stat
*_d_stat
)
2198 if (we_are_background
)
2199 return parent_call ((void *)file_progress_real_query_replace
,
2202 strlen (destname
), destname
,
2203 sizeof (struct stat
), _s_stat
,
2204 sizeof(struct stat
), _d_stat
);
2206 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
2211 do_file_error (char *str
)
2213 return real_do_file_error (Foreground
, str
);
2217 query_recursive (FileOpContext
*ctx
, char *s
)
2219 return real_query_recursive (ctx
, Foreground
, s
);
2223 query_replace (FileOpContext
*ctx
, char *destname
, struct stat
*_s_stat
, struct stat
*_d_stat
)
2225 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
2228 #endif /* !WITH_BACKGROUND */
2231 Cause emacs to enter folding mode for this file: