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 */
77 /* Needed by query_replace */
83 #include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
88 /* Needed for current_panel, other_panel and WTree */
95 #include "../vfs/vfs.h"
99 /* rcsid [] = "$Id$" */
104 * Whether the Midnight Commander tries to provide more
105 * information about copy/move sizes and bytes transfered
106 * at the expense of some speed
108 int file_op_compute_totals
= 1;
110 /* If on, it gets a little scrict with dangerous operations */
111 int know_not_what_am_i_doing
= 0;
113 /* This is a hard link cache */
124 /* the hard link cache */
125 struct link
*linklist
= NULL
;
127 /* the files-to-be-erased list */
128 struct link
*erase_list
;
131 * In copy_dir_dir we use two additional single linked lists: The first -
132 * variable name `parent_dirs' - holds information about already copied
133 * directories and is used to detect cyclic symbolic links.
134 * The second (`dest_dirs' below) holds information about just created
135 * target directories and is used to detect when an directory is copied
136 * into itself (we don't want to copy infinitly).
137 * Both lists don't use the linkcount and name structure members of struct
140 struct link
*dest_dirs
= 0;
142 char *op_names
[3] = {
150 static int query_replace (FileOpContext
*ctx
, char *destname
,
151 struct stat
*_s_stat
, struct stat
*_d_stat
);
152 static int query_recursive (FileOpContext
*ctx
, char *s
);
153 static int do_file_error (char *str
);
156 enum CaseConvs
{ NO_CONV
=0, UP_CHAR
=1, LOW_CHAR
=2, UP_SECT
=4, LOW_SECT
=8 };
159 convert_case (int c
, enum CaseConvs
*conversion
)
161 if (*conversion
& UP_CHAR
){
162 *conversion
&= ~UP_CHAR
;
164 } else if (*conversion
& LOW_CHAR
){
165 *conversion
&= ~LOW_CHAR
;
167 } else if (*conversion
& UP_SECT
){
169 } else if (*conversion
& LOW_SECT
){
175 static int transform_error
= 0;
177 static unsigned char *
178 do_transform_source (FileOpContext
*ctx
, unsigned char *source
)
181 unsigned char *fnsource
= x_basename (source
);
183 enum CaseConvs case_conv
= NO_CONV
;
184 static unsigned char fntarget
[MC_MAXPATHLEN
];
186 len
= strlen (fnsource
);
187 j
= re_match (&ctx
->rx
, fnsource
, len
, 0, &ctx
->regs
);
189 transform_error
= FILE_SKIP
;
192 for (next_reg
= 1, j
= 0, k
= 0; j
< strlen (ctx
->dest_mask
); j
++){
193 switch (ctx
->dest_mask
[j
]){
196 if (! isdigit ((unsigned char) ctx
->dest_mask
[j
])){
197 /* Backslash followed by non-digit */
198 switch (ctx
->dest_mask
[j
]){
200 case_conv
|= UP_SECT
;
201 case_conv
&= ~LOW_SECT
;
204 case_conv
|= UP_CHAR
;
207 case_conv
|= LOW_SECT
;
208 case_conv
&= ~UP_SECT
;
211 case_conv
|= LOW_CHAR
;
217 /* Backslash as quote mark */
218 fntarget
[k
++] = convert_case (ctx
->dest_mask
[j
], &case_conv
);
222 /* Backslash followed by digit */
223 next_reg
= ctx
->dest_mask
[j
] - '0';
228 if (next_reg
< 0 || next_reg
>= RE_NREGS
229 || ctx
->regs
.start
[next_reg
] < 0){
230 message_1s (1, MSG_ERROR
, _(" Invalid target mask "));
231 transform_error
= FILE_ABORT
;
234 for (l
= ctx
->regs
.start
[next_reg
]; 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
&& vfs_type (lp
->name
) == my_vfs
){
319 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
321 if (vfs_type (dst_name
) == vfs_type (p
)){
322 if (!mc_stat (p
, &link_stat
)){
323 if (!mc_link (p
, dst_name
))
328 message_1s (1, MSG_ERROR
, _(" Could not make the hardlink "));
331 lp
= (struct link
*) g_malloc (sizeof (struct link
) + strlen (src_name
)
332 + strlen (dst_name
) + 1);
337 strcpy (lp
->name
, src_name
);
338 p
= strchr (lp
->name
, 0) + 1;
339 strcpy (p
, dst_name
);
347 * Duplicate the contents of the symbolic link src_path in dst_path.
348 * Try to make a stable symlink if the option "stable symlink" was
349 * set in the file mask dialog.
350 * If dst_path is an existing symlink it will be deleted silently
351 * (upper levels take already care of existing files at dst_path).
354 make_symlink (FileOpContext
*ctx
, char *src_path
, char *dst_path
)
356 char link_target
[MC_MAXPATHLEN
];
362 if (mc_lstat (dst_path
, &sb
) == 0 && S_ISLNK (sb
.st_mode
))
368 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
);
370 return_status
= file_error (_(" Cannot read source link \"%s\" \n %s "), src_path
);
371 if (return_status
== FILE_RETRY
)
372 goto retry_src_readlink
;
373 return return_status
;
375 link_target
[len
] = 0;
377 if (ctx
->stable_symlinks
)
378 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
)) {
379 message_1s (1, MSG_ERROR
,
380 _(" Cannot make stable symlinks across "
381 "non-local filesystems: \n\n"
382 " Option Stable Symlinks will be disabled "));
383 ctx
->stable_symlinks
= 0;
386 if (ctx
->stable_symlinks
&& *link_target
!= PATH_SEP
) {
389 p
= g_strdup (src_path
);
390 r
= strrchr (p
, PATH_SEP
);
394 if (*dst_path
== PATH_SEP
)
395 q
= g_strdup (dst_path
);
397 q
= g_strconcat (p
, dst_path
, NULL
);
398 r
= strrchr (q
, PATH_SEP
);
401 s
= g_strconcat (p
, link_target
, NULL
);
402 strcpy (link_target
, s
);
404 s
= diff_two_paths (q
, link_target
);
406 strcpy (link_target
, s
);
415 if (mc_symlink (link_target
, dst_path
) == 0)
419 * if dst_exists, it is obvious that this had failed.
420 * We can delete the old symlink and try again...
423 if (!mc_unlink (dst_path
))
424 if (mc_symlink (link_target
, dst_path
) == 0)
428 return_status
= file_error (_(" Cannot create target symlink \"%s\" \n %s "), dst_path
);
429 if (return_status
== FILE_RETRY
)
430 goto retry_dst_symlink
;
431 return return_status
;
435 progress_update_one (FileOpContext
*ctx
,
436 off_t
*progress_count
,
437 double *progress_bytes
,
439 int is_toplevel_file
)
443 if (is_toplevel_file
|| ctx
->progress_totals_computed
) {
445 (*progress_bytes
) += add
;
448 /* Apply some heuristic here to not call the update stuff very often */
449 ret
= file_progress_show_count (ctx
, *progress_count
, ctx
->progress_count
);
450 if (ret
!= FILE_CONT
)
452 ret
= file_progress_show_bytes (ctx
, *progress_bytes
, ctx
->progress_bytes
);
458 copy_file_file (FileOpContext
*ctx
, char *src_path
, char *dst_path
, int ask_overwrite
,
459 off_t
*progress_count
, double *progress_bytes
,
460 int is_toplevel_file
)
463 uid_t src_uid
= (uid_t
) -1;
464 gid_t src_gid
= (gid_t
) -1;
467 int buf_size
= BUF_8K
;
468 int src_desc
, dest_desc
= 0;
469 int n_read
, n_written
;
470 int src_mode
= 0; /* The mode of the source file */
473 int dst_exists
= 0, appending
= 0;
474 off_t n_read_total
= 0, file_size
= -1;
475 int return_status
, temp_status
;
476 struct timeval tv_transfer_start
;
478 /* bitmask used to remember which resourses we should release on return
479 A single goto label is much easier to handle than a bunch of gotos ;-). */
480 unsigned resources
= 0;
482 /* FIXME: We should not be using global variables! */
484 return_status
= FILE_RETRY
;
486 if (file_progress_show_source (ctx
, src_path
) == FILE_ABORT
||
487 file_progress_show_target (ctx
, dst_path
) == FILE_ABORT
)
493 if (mc_stat (dst_path
, &sb2
) == 0){
494 if (S_ISDIR (sb2
.st_mode
)){
495 return_status
= file_error (_(" Cannot overwrite directory \"%s\" \n %s "), dst_path
);
496 if (return_status
== FILE_RETRY
)
498 return return_status
;
503 while ((* ctx
->stat_func
) (src_path
, &sb
)) {
504 return_status
= file_error (_(" Cannot stat source file \"%s\" \n %s "), src_path
);
505 if (return_status
!= FILE_RETRY
)
506 return return_status
;
510 /* .ado: For OS/2 or NT: no st_ino exists, it is better to just try to
511 * overwrite the target file
514 /* Destination already exists */
515 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
){
516 message_3s (1, MSG_ERROR
, _(" `%s' and `%s' are the same file. "), src_path
, dst_path
);
522 /* Should we replace destination? */
525 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
526 if (return_status
!= FILE_CONT
)
527 return return_status
;
531 if (!ctx
->do_append
) {
532 /* .ado: OS2 and NT don't have hardlinks */
534 /* Check the hardlinks */
535 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
536 check_hardlinks (src_path
, dst_path
, &sb
) == 1){
537 /* We have made a hardlink - no more processing is necessary */
538 return return_status
;
541 if (S_ISLNK (sb
.st_mode
)) {
544 retval
= make_symlink (ctx
, src_path
, dst_path
);
550 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) || S_ISFIFO (sb
.st_mode
)
551 || S_ISSOCK (sb
.st_mode
)){
552 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
, sb
.st_rdev
) < 0){
553 return_status
= file_error (_(" Cannot create special file \"%s\" \n %s "), dst_path
);
554 if (return_status
== FILE_RETRY
)
556 return return_status
;
561 while (ctx
->preserve_uidgid
&& mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)){
562 temp_status
= file_error (_(" Cannot chown target file \"%s\" \n %s "), dst_path
);
563 if (temp_status
== FILE_RETRY
)
568 while (ctx
->preserve
&&
569 (mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
) < 0)){
570 temp_status
= file_error (_(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
571 if (temp_status
== FILE_RETRY
)
579 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
581 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0){
582 return_status
= file_error (_(" Cannot open source file \"%s\" \n %s "), src_path
);
583 if (return_status
== FILE_RETRY
)
586 return return_status
;
591 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
){
592 message_1s (1, _(" Warning "), _(" Reget failed, about to overwrite file "));
593 ctx
->do_reget
= ctx
->do_append
= 0;
597 while (mc_fstat (src_desc
, &sb
)){
598 return_status
= file_error (_(" Cannot fstat source file \"%s\" \n %s "), src_path
);
599 if (return_status
== FILE_RETRY
)
604 src_mode
= sb
.st_mode
;
609 utb
.actime
= sb
.st_atime
;
610 utb
.modtime
= sb
.st_mtime
;
611 file_size
= sb
.st_size
;
613 /* Create the new regular file with small permissions initially,
614 do not create a security hole. FIXME: You have security hole
615 here, btw. Imagine copying to /tmp and symlink attack :-( */
617 while ((dest_desc
= mc_open (dst_path
, O_WRONLY
|
618 (ctx
->do_append
? O_APPEND
: (O_CREAT
| O_TRUNC
)), 0600)) < 0){
619 return_status
= file_error (_(" Cannot create target file \"%s\" \n %s "), dst_path
);
620 if (return_status
== FILE_RETRY
)
625 resources
|= 2; /* dst_path exists/dst_path opened */
626 resources
|= 4; /* remove short file */
628 appending
= ctx
->do_append
;
631 /* Find out the optimal buffer size. */
632 while (mc_fstat (dest_desc
, &sb
)){
633 return_status
= file_error (_(" Cannot fstat target file \"%s\" \n %s "), dst_path
);
634 if (return_status
== FILE_RETRY
)
638 buf
= (char *) g_malloc (buf_size
);
643 return_status
= file_progress_show (ctx
, 0, file_size
);
647 if (return_status
!= FILE_CONT
)
651 struct timeval tv_current
, tv_last_update
, tv_last_input
;
652 int secs
, update_secs
;
656 tv_last_update
= tv_transfer_start
;
660 if (mc_ctl (src_desc
, MCCTL_IS_NOTREADY
, 0))
663 while ((n_read
= mc_read (src_desc
, buf
, buf_size
))<0){
664 return_status
= file_error(_(" Cannot read source file \"%s\" \n %s "), src_path
);
665 if (return_status
== FILE_RETRY
)
672 gettimeofday (&tv_current
, NULL
);
675 n_read_total
+= n_read
;
677 /* Windows NT ftp servers report that files have no
678 * permissions: -------, so if we happen to have actually
679 * read something, we should fix the permissions.
682 ((S_IRUSR
|S_IWUSR
|S_IXUSR
) /* user */
683 |(S_IXOTH
|S_IWOTH
|S_IROTH
) /* other */
684 |(S_IXGRP
|S_IWGRP
|S_IRGRP
)))) /* group */
685 src_mode
= S_IRUSR
|S_IWUSR
|S_IROTH
|S_IRGRP
;
686 gettimeofday (&tv_last_input
, NULL
);
689 while ((n_written
= mc_write (dest_desc
, buf
, n_read
)) < n_read
){
694 return_status
= file_error(_(" Cannot write target file \"%s\" \n %s "),
696 if (return_status
== FILE_RETRY
)
702 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
703 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
706 tv_last_update
= tv_current
;
709 /* 2. Check for a stalled condition */
710 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
712 if (update_secs
> 4){
713 stalled_msg
= _("(stalled)");
718 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
721 ctx
->eta_secs
= ((dt
/ (double) n_read_total
) * file_size
) - dt
;
722 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
727 /* 4. Compute BPS rate */
729 ctx
->bps_time
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
730 if (ctx
->bps_time
< 1)
732 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
735 file_progress_set_stalled_label (ctx
, stalled_msg
);
736 file_progress_show_bytes (ctx
, *progress_bytes
+ n_read_total
, ctx
->progress_bytes
);
737 return_status
= file_progress_show (ctx
, n_read_total
, file_size
);
739 if (return_status
!= FILE_CONT
)
744 resources
&= ~4; /* copy successful, don't remove target file */
750 while ((resources
& 1) && mc_close (src_desc
) < 0){
751 temp_status
= file_error (_(" Cannot close source file \"%s\" \n %s "), src_path
);
752 if (temp_status
== FILE_RETRY
)
754 if (temp_status
== FILE_ABORT
)
755 return_status
= temp_status
;
759 while ((resources
& 2) && mc_close (dest_desc
) < 0){
760 temp_status
= file_error (_(" Cannot close target file \"%s\" \n %s "), dst_path
);
761 if (temp_status
== FILE_RETRY
)
763 return_status
= temp_status
;
768 /* Remove short file */
770 result
= query_dialog (_("Copy"), _("Incomplete file was retrieved. Keep it?"),
771 D_ERROR
, 2, _("&Delete"), _("&Keep"));
773 mc_unlink (dst_path
);
774 } else if (resources
& (2|8)){
775 /* no short file and destination file exists */
777 if (!appending
&& ctx
->preserve_uidgid
){
778 while (mc_chown (dst_path
, src_uid
, src_gid
)){
779 temp_status
= file_error
780 (_(" Cannot chown target file \"%s\" \n %s "), dst_path
);
781 if (temp_status
== FILE_RETRY
)
783 return_status
= temp_status
;
790 * .ado: according to the XPG4 standard, the file must be closed before
791 * chmod can be invoked
793 if (!appending
&& ctx
->preserve
){
794 while (mc_chmod (dst_path
, src_mode
& ctx
->umask_kill
)){
795 temp_status
= file_error (
796 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
797 if (temp_status
!= FILE_RETRY
){
798 return_status
= temp_status
;
802 mc_utime (dst_path
, &utb
);
806 if (return_status
== FILE_CONT
)
807 return_status
= progress_update_one (ctx
, progress_count
, progress_bytes
,
808 file_size
, is_toplevel_file
);
810 return return_status
;
814 * I think these copy_*_* functions should have a return type.
815 * anyway, this function *must* have two directories as arguments.
817 /* FIXME: This function needs to check the return values of the
820 copy_dir_dir (FileOpContext
*ctx
, char *s
, char *d
, int toplevel
,
821 int move_over
, int delete,
822 struct link
*parent_dirs
,
823 off_t
*progress_count
,
824 double *progress_bytes
)
827 struct stat buf
, cbuf
;
829 char *path
, *mdpath
, *dest_file
, *dest_dir
;
830 int return_status
= FILE_CONT
;
834 /* First get the mode of the source dir */
836 if ((* ctx
->stat_func
) (s
, &cbuf
)){
837 return_status
= file_error (_(" Cannot stat source directory \"%s\" \n %s "), s
);
838 if (return_status
== FILE_RETRY
)
840 return return_status
;
843 if (is_in_linklist (dest_dirs
, s
, &cbuf
)){
844 /* Don't copy a directory we created before (we don't want to copy
845 infinitely if a directory is copied into itself) */
846 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
850 /* Hmm, hardlink to directory??? - Norbert */
851 /* FIXME: In this step we should do something
852 in case the destination already exist */
853 /* Check the hardlinks */
854 if (ctx
->preserve
&& cbuf
.st_nlink
> 1 && check_hardlinks (s
, d
, &cbuf
) == 1){
855 /* We have made a hardlink - no more processing is necessary */
856 return return_status
;
859 if (!S_ISDIR (cbuf
.st_mode
)){
860 return_status
= file_error (_(" Source directory \"%s\" is not a directory \n %s "), s
);
861 if (return_status
== FILE_RETRY
)
863 return return_status
;
866 if (is_in_linklist (parent_dirs
, s
, &cbuf
)){
867 /* we found a cyclic symbolic link */
868 message_2s (1, MSG_ERROR
, _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
872 lp
= g_new (struct link
, 1);
873 lp
->vfs
= vfs_type (s
);
874 lp
->ino
= cbuf
.st_ino
;
875 lp
->dev
= cbuf
.st_dev
;
876 lp
->next
= parent_dirs
;
880 /* Now, check if the dest dir exists, if not, create it. */
881 if (mc_stat (d
, &buf
)){
882 /* Here the dir doesn't exist : make it !*/
885 if (mc_rename (s
, d
) == 0){
886 g_free (parent_dirs
);
890 dest_dir
= g_strdup (d
);
893 * If the destination directory exists, we want to copy the whole
894 * directory, but we only want this to happen once.
896 * Escape sequences added to the * to compiler warnings.
897 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
898 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
900 if (!S_ISDIR (buf
.st_mode
)){
901 return_status
= file_error (_(" Destination \"%s\" must be a directory \n %s "), d
);
902 if (return_status
== FILE_RETRY
)
904 g_free (parent_dirs
);
905 return return_status
;
908 /* Again, I'm getting curious. Is not d already what we wanted, incl.
909 * masked source basename? Is not this just a relict of the past versions?
910 * I'm afraid this will lead into a two level deep dive :(
912 * I think this is indeed the problem. I can not remember any case where
913 * we actually would like that behaviour -miguel
915 * It's a documented feature (option `Dive into subdir if exists' in the
916 * copy/move dialog). -Norbert
918 if (toplevel
&& ctx
->dive_into_subdirs
){
919 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
923 dest_dir
= g_strdup (d
);
928 if (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)){
929 return_status
= file_error (_(" Cannot create target directory \"%s\" \n %s "), dest_dir
);
930 if (return_status
== FILE_RETRY
)
931 goto retry_dst_mkdir
;
935 lp
= g_new (struct link
, 1);
936 mc_stat (dest_dir
, &buf
);
937 lp
->vfs
= vfs_type (dest_dir
);
938 lp
->ino
= buf
.st_ino
;
939 lp
->dev
= buf
.st_dev
;
940 lp
->next
= dest_dirs
;
944 if (ctx
->preserve_uidgid
){
946 if (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)){
947 return_status
= file_error (_(" Cannot chown target directory \"%s\" \n %s "), dest_dir
);
948 if (return_status
== FILE_RETRY
)
949 goto retry_dst_chown
;
956 /* open the source dir for reading */
957 if ((reading
= mc_opendir (s
)) == 0){
961 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
){
963 * Now, we don't want '.' and '..' to be created / copied at any time
965 if (!strcmp (next
->d_name
, "."))
967 if (!strcmp (next
->d_name
, ".."))
970 /* get the filename and add it to the src directory */
971 path
= concat_dir_and_file (s
, next
->d_name
);
973 (* ctx
->stat_func
) (path
, &buf
);
974 if (S_ISDIR (buf
.st_mode
)){
975 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
977 * From here, we just intend to recursively copy subdirs, not
978 * the double functionality of copying different when the target
979 * dir already exists. So, we give the recursive call the flag 0
980 * meaning no toplevel.
982 return_status
= copy_dir_dir (ctx
, path
, mdpath
, 0, 0,
983 delete, parent_dirs
, progress_count
, progress_bytes
);
986 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
987 return_status
= copy_file_file (ctx
, path
, dest_file
, 1,
988 progress_count
, progress_bytes
, 0);
991 if (delete && return_status
== FILE_CONT
){
992 if (ctx
->erase_at_end
){
993 static struct link
*tail
;
994 lp
= g_malloc (sizeof (struct link
) + strlen (path
));
995 strcpy (lp
->name
, path
);
996 lp
->st_mode
= buf
.st_mode
;
1002 erase_list
= tail
= lp
;
1004 if (S_ISDIR (buf
.st_mode
)){
1005 return_status
= erase_dir_iff_empty (ctx
, path
);
1007 return_status
= erase_file (ctx
, path
, 0, 0, 0);
1013 mc_closedir (reading
);
1016 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1017 utb
.actime
= cbuf
.st_atime
;
1018 utb
.modtime
= cbuf
.st_mtime
;
1019 mc_utime(dest_dir
, &utb
);
1024 g_free (parent_dirs
);
1025 return return_status
;
1030 /* {{{ Move routines */
1033 move_file_file (FileOpContext
*ctx
, char *s
, char *d
,
1034 off_t
*progress_count
, double *progress_bytes
)
1036 struct stat src_stats
, dst_stats
;
1037 int return_status
= FILE_CONT
;
1039 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
1040 || file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1046 if (mc_lstat (s
, &src_stats
) != 0){
1047 /* Source doesn't exist */
1048 return_status
= file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1049 if (return_status
== FILE_RETRY
)
1050 goto retry_src_lstat
;
1051 return return_status
;
1054 if (mc_lstat (d
, &dst_stats
) == 0){
1055 /* Destination already exists */
1056 /* .ado: for OS/2 and NT, no st_ino exists */
1058 if (src_stats
.st_dev
== dst_stats
.st_dev
1059 && src_stats
.st_ino
== dst_stats
.st_ino
){
1060 int msize
= COLS
- 36;
1061 char st
[MC_MAXPATHLEN
];
1062 char dt
[MC_MAXPATHLEN
];
1068 strcpy (st
, name_trunc (s
, msize
));
1069 strcpy (dt
, name_trunc (d
, msize
));
1070 message_3s (1, MSG_ERROR
, _(" `%s' and `%s' are the same file "),
1075 #endif /* !OS2_NT */
1076 if (S_ISDIR (dst_stats
.st_mode
)){
1077 message_2s (1, MSG_ERROR
, _(" Cannot overwrite directory `%s' "), d
);
1082 if (confirm_overwrite
){
1083 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1084 if (return_status
!= FILE_CONT
)
1085 return return_status
;
1087 /* Ok to overwrite */
1090 if (!ctx
->do_append
) {
1091 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1092 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1093 goto retry_src_remove
;
1095 return return_status
;
1098 if (mc_rename (s
, d
) == 0){
1103 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1104 one nfs to the same, but on the server it is on two different
1105 filesystems. Then nfs returns EIO instead of EXDEV.
1106 Hope it will not hurt if we always in case of error try to copy/delete. */
1108 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1110 if (errno
!= EXDEV
){
1111 return_status
= files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
, d
);
1112 if (return_status
== FILE_RETRY
)
1114 return return_status
;
1118 /* Failed because filesystem boundary -> copy the file instead */
1119 return_status
= copy_file_file (ctx
, s
, d
, 0, progress_count
, progress_bytes
, 1);
1120 if (return_status
!= FILE_CONT
)
1121 return return_status
;
1123 if ((return_status
= file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1124 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1125 return return_status
;
1131 return_status
= file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1132 if (return_status
== FILE_RETRY
)
1133 goto retry_src_remove
;
1134 return return_status
;
1137 if (return_status
== FILE_CONT
)
1138 return_status
= progress_update_one (ctx
,
1140 progress_bytes
, src_stats
.st_size
, 1);
1142 return return_status
;
1146 move_dir_dir (FileOpContext
*ctx
, char *s
, char *d
,
1147 off_t
*progress_count
, double *progress_bytes
)
1149 struct stat sbuf
, dbuf
, destbuf
;
1155 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
||
1156 file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1162 if (mc_stat (d
, &dbuf
))
1163 destdir
= g_strdup (d
); /* destination doesn't exist */
1164 else if (!ctx
->dive_into_subdirs
){
1165 destdir
= g_strdup (d
);
1168 destdir
= concat_dir_and_file (d
, x_basename (s
));
1170 if (sbuf
.st_dev
== dbuf
.st_dev
1171 && sbuf
.st_ino
== dbuf
.st_ino
){
1172 int msize
= COLS
- 36;
1173 char st
[MC_MAXPATHLEN
];
1174 char dt
[MC_MAXPATHLEN
];
1180 strcpy (st
, name_trunc (s
, msize
));
1181 strcpy (dt
, name_trunc (d
, msize
));
1182 message_3s (1, MSG_ERROR
, _(" `%s' and `%s' are the same directory "),
1187 #endif /* !OS2_NT */
1189 /* Check if the user inputted an existing dir */
1191 if (!mc_stat (destdir
, &destbuf
)){
1193 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 1, 1, 0,
1194 progress_count
, progress_bytes
);
1196 if (return_status
!= FILE_CONT
)
1200 if (S_ISDIR (destbuf
.st_mode
))
1201 return_status
= file_error (_(" Cannot overwrite directory \"%s\" %s "), destdir
);
1203 return_status
= file_error (_(" Cannot overwrite file \"%s\" %s "), destdir
);
1204 if (return_status
== FILE_RETRY
)
1205 goto retry_dst_stat
;
1208 return return_status
;
1212 if (mc_rename (s
, destdir
) == 0){
1213 return_status
= FILE_CONT
;
1216 /* .ado: Drive, Do we need this anymore? */
1219 /* EXDEV: cross device; does not work everywhere */
1220 if (toupper(s
[0]) != toupper(destdir
[0]))
1225 if (errno
!= EXDEV
){
1226 return_status
= files_error (_(" Cannot move directory \"%s\" to \"%s\" \n %s "), s
, d
);
1227 if (return_status
== FILE_RETRY
)
1234 /* Failed because of filesystem boundary -> copy dir instead */
1235 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 0, 1, 0, progress_count
, progress_bytes
);
1237 if (return_status
!= FILE_CONT
)
1240 if ((return_status
= file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1241 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1245 if (ctx
->erase_at_end
){
1246 for (; erase_list
&& return_status
!= FILE_ABORT
;){
1247 if (S_ISDIR (erase_list
->st_mode
)){
1248 return_status
= erase_dir_iff_empty (ctx
, erase_list
->name
);
1250 return_status
= erase_file (ctx
, erase_list
->name
, 0, 0, 0);
1252 erase_list
= erase_list
->next
;
1256 erase_dir_iff_empty (ctx
, s
);
1262 erase_list
= erase_list
->next
;
1265 return return_status
;
1270 /* {{{ Erase routines */
1271 /* Don't update progress status if progress_count==NULL */
1273 erase_file (FileOpContext
*ctx
, char *s
, off_t
*progress_count
, double *progress_bytes
,
1274 int is_toplevel_file
)
1279 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1283 if (progress_count
&& mc_lstat (s
, &buf
)) {
1284 /* ignore, most likely the mc_unlink fails, too */
1288 while (mc_unlink (s
)){
1289 return_status
= file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1290 if (return_status
!= FILE_RETRY
)
1291 return return_status
;
1295 return progress_update_one (ctx
, progress_count
, progress_bytes
, buf
.st_size
,
1302 recursive_erase (FileOpContext
*ctx
, char *s
, off_t
*progress_count
, double *progress_bytes
)
1304 struct dirent
*next
;
1308 int return_status
= FILE_CONT
;
1310 if (!strcmp (s
, ".."))
1313 reading
= mc_opendir (s
);
1318 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
){
1319 if (!strcmp (next
->d_name
, "."))
1321 if (!strcmp (next
->d_name
, ".."))
1323 path
= concat_dir_and_file (s
, next
->d_name
);
1324 if (mc_lstat (path
, &buf
)){
1326 mc_closedir (reading
);
1329 if (S_ISDIR (buf
.st_mode
))
1330 return_status
= (recursive_erase (ctx
, path
, progress_count
, progress_bytes
)
1333 return_status
= erase_file (ctx
, path
, progress_count
, progress_bytes
, 0);
1336 mc_closedir (reading
);
1337 if (return_status
!= FILE_CONT
)
1338 return return_status
;
1339 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1343 while (my_rmdir (s
)){
1344 return_status
= file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1345 if (return_status
!= FILE_RETRY
)
1346 return return_status
;
1352 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1353 in the directory path points to, 0 else. */
1355 check_dir_is_empty(char *path
)
1361 dir
= mc_opendir (path
);
1365 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)){
1366 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1367 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
1368 continue; /* "." or ".." */
1378 erase_dir (FileOpContext
*ctx
, char *s
, off_t
*progress_count
, double *progress_bytes
)
1382 if (strcmp (s
, "..") == 0)
1385 if (strcmp (s
, ".") == 0)
1388 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1392 /* The old way to detect a non empty directory was:
1393 error = my_rmdir (s);
1394 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1395 For the linux user space nfs server (nfs-server-2.2beta29-2)
1396 we would have to check also for EIO. I hope the new way is
1397 fool proof. (Norbert)
1399 error
= check_dir_is_empty (s
);
1400 if (error
== 0){ /* not empty */
1401 error
= query_recursive (ctx
, s
);
1402 if (error
== FILE_CONT
)
1403 return recursive_erase (ctx
, s
, progress_count
, progress_bytes
);
1408 while (my_rmdir (s
) == -1){
1409 error
= file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1410 if (error
!= FILE_RETRY
)
1418 erase_dir_iff_empty (FileOpContext
*ctx
, char *s
)
1422 if (strcmp (s
, "..") == 0)
1425 if (strcmp (s
, ".") == 0)
1428 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1432 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1435 while (my_rmdir (s
)){
1436 error
= file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1437 if (error
!= FILE_RETRY
)
1446 /* {{{ Panel operate routines */
1448 /* Returns currently selected file or the first marked file if there is one */
1450 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1454 /* No problem with Gnome, as get_current_type never returns view_tree there */
1455 if (get_current_type () == view_tree
){
1456 WTree
*tree
= (WTree
*)get_panel_widget (get_current_index ());
1458 mc_stat (tree
->selected_ptr
->name
, stat_buf
);
1459 return tree
->selected_ptr
->name
;
1463 for (i
= 0; i
< panel
->count
; i
++)
1464 if (panel
->dir
.list
[i
].f
.marked
){
1465 *stat_buf
= panel
->dir
.list
[i
].buf
;
1466 return panel
->dir
.list
[i
].fname
;
1469 *stat_buf
= panel
->dir
.list
[panel
->selected
].buf
;
1470 return panel
->dir
.list
[panel
->selected
].fname
;
1472 g_assert_not_reached ();
1477 is_wildcarded (char *p
)
1482 else if (*p
== '\\' && p
[1] >= '1' && p
[1] <= '9')
1491 * Computes the number of bytes used by the files in a directory
1494 compute_dir_size (char *dirname
, off_t
*ret_marked
, double *ret_total
)
1497 struct dirent
*dirent
;
1499 dir
= mc_opendir (dirname
);
1504 while ((dirent
= mc_readdir (dir
)) != NULL
){
1509 if (strcmp (dirent
->d_name
, ".") == 0)
1511 if (strcmp (dirent
->d_name
, "..") == 0)
1514 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1516 res
= mc_lstat (fullname
, &s
);
1523 if (S_ISDIR (s
.st_mode
)){
1524 off_t subdir_count
= 0;
1525 double subdir_bytes
= 0;
1527 compute_dir_size (fullname
, &subdir_count
, &subdir_bytes
);
1529 *ret_marked
+= subdir_count
;
1530 *ret_total
+= subdir_bytes
;
1533 *ret_total
+= s
.st_size
;
1542 * panel_compute_totals:
1544 * compute the number of files and the number of bytes
1545 * used up by the whole selection, recursing directories
1546 * as required. In addition, it checks to see if it will
1547 * overwrite any files by doing the copy.
1550 panel_compute_totals (WPanel
*panel
, off_t
*ret_marked
, double *ret_total
)
1557 for (i
= 0; i
< panel
->count
; i
++){
1560 if (!panel
->dir
.list
[i
].f
.marked
)
1563 s
= &panel
->dir
.list
[i
].buf
;
1565 if (S_ISDIR (s
->st_mode
)){
1567 off_t subdir_count
= 0;
1568 double subdir_bytes
= 0;
1570 dir_name
= concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1571 compute_dir_size (dir_name
, &subdir_count
, &subdir_bytes
);
1573 *ret_marked
+= subdir_count
;
1574 *ret_total
+= subdir_bytes
;
1578 *ret_total
+= s
->st_size
;
1584 * This array introduced to avoid translation problems. The former (op_names)
1585 * is assumed to be nouns, suitable in dialog box titles; this one should
1586 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1587 * Notice first symbol - it is to fool gettext and force these strings to
1588 * be different for it. First symbol is skipped while building a prompt.
1589 * (I don't use spaces around the words, because someday they could be
1590 * dropped, when widgets get smarter)
1592 static char *op_names1
[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1595 int fmd_xlen
= FMD_XLEN
;
1598 * These are formats for building a prompt. Parts encoded as follows:
1599 * %o - operation from op_names1
1600 * %f - file/files or files/directories, as appropriate
1601 * %m - "with source mask" or question mark for delete
1602 * %s - source name (truncated)
1603 * %d - number of marked files
1604 * %e - "to:" or question mark for delete
1606 * xgettext:no-c-format */
1607 static char* one_format
= N_("%o %f \"%s\"%m");
1608 /* xgettext:no-c-format */
1609 static char* many_format
= N_("%o %d %f%m");
1610 static char* prompt_parts
[] =
1612 N_("file"), N_("files"), N_("directory"), N_("directories"),
1613 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1617 panel_operate_generate_prompt (WPanel
* panel
, int operation
, int only_one
,
1618 struct stat
* src_stat
)
1620 register char *sp
, *cp
;
1622 char format_string
[BUF_MEDIUM
];
1623 char *dp
= format_string
;
1624 char* source
= NULL
;
1627 static int i18n_flag
= 0;
1630 fmd_init_i18n (FALSE
); /* to get proper fmd_xlen */
1632 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1633 op_names1
[i
] = _(op_names1
[i
]);
1635 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1636 prompt_parts
[i
] = _(prompt_parts
[i
]);
1638 one_format
= _(one_format
);
1639 many_format
= _(many_format
);
1642 #endif /* ENABLE_NLS */
1644 sp
= only_one
? one_format
: many_format
;
1647 source
= panel_get_file (panel
, src_stat
);
1658 cp
= op_names1
[operation
] + 1;
1661 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[5];
1664 cp
= operation
== OP_DELETE
? "?" : prompt_parts
[6];
1669 cp
= S_ISDIR (src_stat
->st_mode
) ?
1670 prompt_parts
[2] : prompt_parts
[0];
1674 cp
= (panel
->marked
== panel
->dirs_marked
)
1676 : (panel
->dirs_marked
? prompt_parts
[4]
1677 : prompt_parts
[1]);
1698 i
= fmd_xlen
- strlen(format_string
) - 4;
1699 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
, name_trunc (source
, i
));
1703 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format_string
, panel
->marked
);
1704 i
= strlen (cmd_buf
) + 6 - fmd_xlen
;
1708 fmd_init_i18n (TRUE
); /* to recalculate positions of child widgets */
1718 * Performs one of the operations on the selection on the source_panel
1719 * (copy, delete, move).
1721 * Returns 1 if did change the directory
1722 * structure, Returns 0 if user aborted
1725 panel_operate (void *source_panel
, FileOperation operation
, char *thedefault
, int ask_user
)
1727 WPanel
*panel
= source_panel
;
1728 #ifdef WITH_FULL_PATHS
1729 char *source_with_path
= NULL
;
1731 # define source_with_path source
1732 #endif /* !WITH_FULL_PATHS */
1733 char *source
= NULL
;
1736 char *save_cwd
= NULL
, *save_dest
= NULL
;
1737 int only_one
= (get_current_type () == view_tree
) || (panel
->marked
<= 1);
1738 struct stat src_stat
, dst_stat
;
1746 int do_bg
; /* do background operation? */
1748 ctx
= file_op_context_new ();
1751 ctx
->rx
.buffer
= NULL
;
1752 free_linklist (&linklist
);
1753 free_linklist (&dest_dirs
);
1754 if (get_current_type () == view_listing
)
1755 if (!panel
->marked
&& !strcmp (selection (panel
)->fname
, "..")){
1756 message (1, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1757 file_op_context_destroy (ctx
);
1761 if (operation
< OP_COPY
|| operation
> OP_DELETE
) {
1762 file_op_context_destroy (ctx
);
1766 /* Generate confirmation prompt */
1767 source
= panel_operate_generate_prompt (panel
, operation
, only_one
, &src_stat
);
1769 /* Show confirmation dialog */
1770 if (operation
== OP_DELETE
&& confirm_delete
){
1771 if (know_not_what_am_i_doing
)
1774 i
= query_dialog (_(op_names
[operation
]), cmd_buf
,
1775 D_ERROR
, 2, _("&Yes"), _("&No"));
1778 file_op_context_destroy (ctx
);
1781 } else if (operation
!= OP_DELETE
){
1782 ctx
->rx
.buffer
= (char *) g_malloc (MC_MAXPATHLEN
);
1783 ctx
->rx
.allocated
= MC_MAXPATHLEN
;
1784 ctx
->rx
.translate
= 0;
1789 if (thedefault
!= NULL
)
1790 dest_dir
= thedefault
;
1791 else if (get_other_type () == view_listing
)
1792 dest_dir
= opanel
->cwd
;
1794 dest_dir
= panel
->cwd
;
1796 dest
= file_mask_dialog (ctx
, operation
, cmd_buf
, dest_dir
, only_one
, &do_bg
);
1798 g_free (ctx
->rx
.buffer
);
1799 file_op_context_destroy (ctx
);
1803 g_free (ctx
->rx
.buffer
);
1804 file_op_context_destroy (ctx
);
1809 char *all
= "^\\(.*\\)$";
1811 re_compile_pattern (all
, strlen (all
), &ctx
->rx
);
1812 ctx
->dest_mask
= g_strdup ("*");
1814 dest
= g_strdup (thedefault
);
1818 #ifdef WITH_BACKGROUND
1819 /* Did the user select to do a background operation? */
1823 v
= do_background (ctx
, g_strconcat (op_names
[operation
], ": ", panel
->cwd
, NULL
));
1825 message (1, MSG_ERROR
, _(" Sorry, I could not put the job in background "));
1828 /* If we are the parent */
1830 mc_setctl (panel
->cwd
, MCCTL_FORGET_ABOUT
, NULL
);
1831 mc_setctl (dest
, MCCTL_FORGET_ABOUT
, NULL
);
1832 /* file_op_context_destroy (ctx); */
1836 #endif /* WITH_BACKGROUND */
1838 /* Initialize things */
1839 /* We do not want to trash cache every time file is
1840 created/touched. However, this will make our cache contain
1843 if (mc_setctl (dest
, MCCTL_WANT_STALE_DATA
, NULL
))
1844 save_dest
= g_strdup (dest
);
1847 if (mc_setctl (panel
->cwd
, MCCTL_WANT_STALE_DATA
, NULL
))
1848 save_cwd
= g_strdup (panel
->cwd
);
1851 /* Now, let's do the job */
1856 file_op_context_create_ui (ctx
, operation
, 1);
1858 /* This code is only called by the tree and panel code */
1860 /* We now have ETA in all cases */
1862 /* One file: FIXME mc_chdir will take user out of any vfs */
1863 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
1864 mc_chdir (PATH_SEP_STR
);
1866 /* The source and src_stat variables have been initialized before */
1867 #ifdef WITH_FULL_PATHS
1868 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1869 #endif /* WITH_FULL_PATHS */
1871 if (operation
== OP_DELETE
) {
1872 if (S_ISDIR (src_stat
.st_mode
))
1873 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1875 value
= erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1877 temp
= transform_source (ctx
, source_with_path
);
1880 value
= transform_error
;
1882 temp
= concat_dir_and_file (dest
, temp
);
1887 switch (operation
) {
1890 * we use file_mask_op_follow_links only with OP_COPY,
1892 (* ctx
->stat_func
) (source_with_path
, &src_stat
);
1894 if (S_ISDIR (src_stat
.st_mode
))
1895 value
= copy_dir_dir (ctx
, source_with_path
, dest
, 1, 0, 0, 0,
1898 value
= copy_file_file (ctx
, source_with_path
, dest
, 1, &count
, &bytes
, 1);
1902 if (S_ISDIR (src_stat
.st_mode
))
1903 value
= move_dir_dir (ctx
, source_with_path
, dest
, &count
, &bytes
);
1905 value
= move_file_file (ctx
, source_with_path
, dest
, &count
, &bytes
);
1910 message_1s (1, _(" Internal failure "), _(" Unknown file operation "));
1913 } /* Copy or move operation */
1915 if (value
== FILE_CONT
)
1916 unmark_files (panel
);
1919 /* Check destination for copy or move operation */
1920 if (operation
!= OP_DELETE
){
1921 retry_many_dst_stat
:
1922 dst_result
= mc_stat (dest
, &dst_stat
);
1923 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)){
1924 if (file_error (_(" Destination \"%s\" must be a directory \n %s "),
1925 dest
) == FILE_RETRY
)
1926 goto retry_many_dst_stat
;
1931 /* Initialize variables for progress bars */
1932 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
1933 panel_compute_totals (panel
, &ctx
->progress_count
, &ctx
->progress_bytes
);
1934 ctx
->progress_totals_computed
= 1;
1936 ctx
->progress_totals_computed
= 0;
1937 ctx
->progress_count
= panel
->marked
;
1938 ctx
->progress_bytes
= panel
->total
;
1941 /* Loop for every file, perform the actual copy operation */
1942 for (i
= 0; i
< panel
->count
; i
++) {
1943 if (!panel
->dir
.list
[i
].f
.marked
)
1944 continue; /* Skip the unmarked ones */
1946 source
= panel
->dir
.list
[i
].fname
;
1947 src_stat
= panel
->dir
.list
[i
].buf
;
1949 #ifdef WITH_FULL_PATHS
1950 if (source_with_path
)
1951 g_free (source_with_path
);
1952 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1953 #endif /* WITH_FULL_PATHS */
1955 if (operation
== OP_DELETE
){
1956 if (S_ISDIR (src_stat
.st_mode
))
1957 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1959 value
= erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1964 temp
= transform_source (ctx
, source_with_path
);
1966 value
= transform_error
;
1968 temp
= concat_dir_and_file (dest
, temp
);
1973 * we use file_mask_op_follow_links only with OP_COPY,
1975 (* ctx
->stat_func
) (source_with_path
, &src_stat
);
1976 if (S_ISDIR (src_stat
.st_mode
))
1977 value
= copy_dir_dir (ctx
, source_with_path
, temp
, 1, 0, 0, 0,
1980 value
= copy_file_file (ctx
, source_with_path
, temp
, 1,
1982 free_linklist (&dest_dirs
);
1986 if (S_ISDIR (src_stat
.st_mode
))
1987 value
= move_dir_dir (ctx
, source_with_path
, temp
, &count
, &bytes
);
1989 value
= move_file_file (ctx
, source_with_path
, temp
, &count
, &bytes
);
1993 message_1s (1, _(" Internal failure "), _(" Unknown file operation "));
1997 } /* Copy or move operation */
1999 if (value
== FILE_ABORT
)
2002 if (value
== FILE_CONT
)
2003 do_file_mark (panel
, i
, 0);
2005 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
) == FILE_ABORT
)
2008 if (verbose
&& file_progress_show_bytes (ctx
, bytes
, ctx
->progress_bytes
) == FILE_ABORT
)
2011 if (operation
!= OP_DELETE
&& verbose
&& file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2015 } /* Loop for every file */
2021 mc_setctl (save_cwd
, MCCTL_NO_STALE_DATA
, NULL
);
2025 mc_setctl (save_dest
, MCCTL_NO_STALE_DATA
, NULL
);
2029 free_linklist (&linklist
);
2030 free_linklist (&dest_dirs
);
2031 #ifdef WITH_FULL_PATHS
2032 if (source_with_path
)
2033 g_free (source_with_path
);
2034 #endif /* WITH_FULL_PATHS */
2042 if (ctx
->rx
.buffer
) {
2043 g_free (ctx
->rx
.buffer
);
2044 ctx
->rx
.buffer
= NULL
;
2047 if (ctx
->dest_mask
) {
2048 g_free (ctx
->dest_mask
);
2049 ctx
->dest_mask
= NULL
;
2052 #ifdef WITH_BACKGROUND
2053 /* Let our parent know we are saying bye bye */
2054 if (we_are_background
) {
2056 tell_parent (MSG_CHILD_EXITING
);
2059 #endif /* WITH_BACKGROUND */
2061 file_op_context_destroy (ctx
);
2067 /* {{{ Query/status report routines */
2070 real_do_file_error (enum OperationMode mode
, char *error
)
2075 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2076 result
= query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"), _("&Abort"));
2093 /* Report error with one file */
2095 file_error (char *format
, char *file
)
2097 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
,
2098 name_trunc (file
, 30), unix_error_string (errno
));
2100 return do_file_error (cmd_buf
);
2103 /* Report error with two files */
2105 files_error (char *format
, char *file1
, char *file2
)
2110 strcpy (nfile1
, name_trunc (file1
, 15));
2111 strcpy (nfile2
, name_trunc (file2
, 15));
2113 g_snprintf (cmd_buf
, sizeof (cmd_buf
), format
, nfile1
, nfile2
,
2114 unix_error_string (errno
));
2116 return do_file_error (cmd_buf
);
2120 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, char *s
)
2125 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
){
2127 mode
== Foreground
? _("\n Directory not empty. \n Delete it recursively? ")
2128 : _("\n Background process: Directory not empty \n Delete it recursively? ");
2129 text
= g_strconcat (_(" Delete: "), name_trunc (s
, 30), " ", NULL
);
2131 if (know_not_what_am_i_doing
)
2133 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2134 _("&Yes"), _("&No"),
2135 _("a&ll"), _("non&E"),
2138 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2141 if (know_not_what_am_i_doing
){
2142 if (ctx
->recursive_result
== RECURSIVE_YES
||
2143 ctx
->recursive_result
== RECURSIVE_ALWAYS
){
2144 text
= g_strconcat (
2145 _(" Type 'yes' if you REALLY want to delete "),
2146 ctx
->recursive_result
== RECURSIVE_YES
2147 ? name_trunc (s
, 19) : _("all the directories "), " ", NULL
);
2148 confirm
= input_dialog (
2149 mode
== Foreground
? _(" Recursive Delete ")
2150 : _(" Background process: Recursive Delete "),
2153 if (!confirm
|| strcmp (confirm
, _("yes")))
2154 ctx
->recursive_result
= RECURSIVE_NEVER
;
2161 switch (ctx
->recursive_result
){
2163 case RECURSIVE_ALWAYS
:
2167 case RECURSIVE_NEVER
:
2170 case RECURSIVE_ABORT
:
2177 #ifdef WITH_BACKGROUND
2179 do_file_error (char *str
)
2181 if (we_are_background
)
2182 return parent_call (real_do_file_error
, NULL
, 1, strlen (str
), str
);
2184 return real_do_file_error (Foreground
, str
);
2188 query_recursive (FileOpContext
*ctx
, char *s
)
2190 if (we_are_background
)
2191 return parent_call (real_query_recursive
, ctx
, 1, strlen (s
), s
);
2193 return real_query_recursive (ctx
, Foreground
, s
);
2197 query_replace (FileOpContext
*ctx
, char *destname
, struct stat
*_s_stat
, struct stat
*_d_stat
)
2199 if (we_are_background
)
2200 return parent_call ((void *)file_progress_real_query_replace
,
2203 strlen (destname
), destname
,
2204 sizeof (struct stat
), _s_stat
,
2205 sizeof(struct stat
), _d_stat
);
2207 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
2212 do_file_error (char *str
)
2214 return real_do_file_error (Foreground
, str
);
2218 query_recursive (FileOpContext
*ctx
, char *s
)
2220 return real_query_recursive (ctx
, Foreground
, s
);
2224 query_replace (FileOpContext
*ctx
, char *destname
, struct stat
*_s_stat
, struct stat
*_d_stat
)
2226 return file_progress_real_query_replace (ctx
, Foreground
, destname
, _s_stat
, _d_stat
);
2229 #endif /* !WITH_BACKGROUND */
2232 Cause emacs to enter folding mode for this file: