Removed type SHELL_ESCAPED_STR in favour of plain char*
[midnight-commander.git] / src / file.c
blob4ef043c674beb4558bd5f9acfe288f588f571e5a
1 /* File management.
2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1994, 1995 Janne Kukonlehto
6 1994, 1995 Fred Leeflang
7 1994, 1995, 1996 Miguel de Icaza
8 1995, 1996 Jakub Jelinek
9 1997 Norbert Warmuth
10 1998 Pavel Machek
12 The copy code was based in GNU's cp, and was written by:
13 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
15 The move code was based in GNU's mv, and was written by:
16 Mike Parker and David MacKenzie.
18 Janne Kukonlehto added much error recovery to them for being used
19 in an interactive program.
21 This program is free software; you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation; either version 2 of the License, or
24 (at your option) any later version.
26 This program is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
31 You should have received a copy of the GNU General Public License
32 along with this program; if not, write to the Free Software
33 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
36 * Please note that all dialogs used here must be safe for background
37 * operations.
40 /* {{{ Include files */
42 #include <config.h>
44 #include <ctype.h>
45 #include <errno.h>
46 #include <stdio.h>
47 #include <string.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
53 #include <mhl/memory.h>
54 #include <mhl/escape.h>
55 #include <mhl/string.h>
57 #include "global.h"
58 #include "tty.h"
59 #include "eregex.h"
60 #include "setup.h"
61 #include "color.h"
62 #include "win.h"
63 #include "dialog.h"
64 #include "widget.h"
65 #include "main.h" /* cmd_buf */
66 #include "layout.h"
67 #include "widget.h"
68 #include "wtools.h"
69 #include "background.h" /* we_are_background */
70 #include "util.h"
72 /* Needed for current_panel, other_panel and WTree */
73 #include "dir.h"
74 #include "panel.h"
75 #include "file.h"
76 #include "filegui.h"
77 #include "tree.h"
78 #include "key.h"
79 #include "../vfs/vfs-impl.h"
81 /* }}} */
83 /* Hack: the vfs code should not rely on this */
84 #define WITH_FULL_PATHS 1
86 int verbose = 1;
89 * Whether the Midnight Commander tries to provide more
90 * information about copy/move sizes and bytes transfered
91 * at the expense of some speed
93 int file_op_compute_totals = 1;
95 /* This is a hard link cache */
96 struct link {
97 struct link *next;
98 struct vfs_class *vfs;
99 dev_t dev;
100 ino_t ino;
101 short linkcount;
102 mode_t st_mode;
103 char name[1];
106 /* the hard link cache */
107 static struct link *linklist = NULL;
109 /* the files-to-be-erased list */
110 static struct link *erase_list;
113 * In copy_dir_dir we use two additional single linked lists: The first -
114 * variable name `parent_dirs' - holds information about already copied
115 * directories and is used to detect cyclic symbolic links.
116 * The second (`dest_dirs' below) holds information about just created
117 * target directories and is used to detect when an directory is copied
118 * into itself (we don't want to copy infinitly).
119 * Both lists don't use the linkcount and name structure members of struct
120 * link.
122 static struct link *dest_dirs = 0;
124 const char *op_names[3] = {
125 N_(" Copy "),
126 N_(" Move "),
127 N_(" Delete ")
130 /* }}} */
132 static int query_replace (FileOpContext * ctx, const char *destname,
133 struct stat *_s_stat, struct stat *_d_stat);
134 static int query_recursive (FileOpContext * ctx, const char *s);
135 static int do_file_error (const char *str);
136 static int erase_dir_iff_empty (FileOpContext *ctx, const char *s);
137 static int erase_file (FileOpContext *ctx, const char *s,
138 off_t *progress_count, double *progress_bytes,
139 int is_toplevel_file);
140 static int files_error (const char *format, const char *file1,
141 const char *file2);
144 enum CaseConvs { NO_CONV = 0, UP_CHAR = 1, LOW_CHAR = 2, UP_SECT =
145 4, LOW_SECT = 8 };
147 static char
148 convert_case (char c, enum CaseConvs *conversion)
150 if (*conversion & UP_CHAR) {
151 *conversion &= ~UP_CHAR;
152 return toupper ((unsigned char) c);
153 } else if (*conversion & LOW_CHAR) {
154 *conversion &= ~LOW_CHAR;
155 return tolower ((unsigned char) c);
156 } else if (*conversion & UP_SECT) {
157 return toupper ((unsigned char) c);
158 } else if (*conversion & LOW_SECT) {
159 return tolower ((unsigned char) c);
160 } else
161 return c;
164 static int transform_error = 0;
166 static const char *
167 do_transform_source (FileOpContext *ctx, const char *source)
169 size_t j, k, l, len;
170 const char *fnsource = x_basename (source);
171 int next_reg;
172 enum CaseConvs case_conv = NO_CONV;
173 static char fntarget[MC_MAXPATHLEN];
175 len = strlen (fnsource);
176 j = re_match (&ctx->rx, fnsource, len, 0, &ctx->regs);
177 if (j != len) {
178 transform_error = FILE_SKIP;
179 return NULL;
181 for (next_reg = 1, j = 0, k = 0; j < strlen (ctx->dest_mask); j++) {
182 switch (ctx->dest_mask[j]) {
183 case '\\':
184 if (mhl_shell_is_char_escaped (&ctx->dest_mask[j])){
185 fntarget[k++] = ctx->dest_mask[j++];
186 fntarget[k++] = ctx->dest_mask[j];
187 break;
188 } else {
189 j++;
190 if (!isdigit ((unsigned char) ctx->dest_mask[j])) {
191 /* Backslash followed by non-digit */
192 switch (ctx->dest_mask[j]) {
193 case 'U':
194 case_conv |= UP_SECT;
195 case_conv &= ~LOW_SECT;
196 break;
197 case 'u':
198 case_conv |= UP_CHAR;
199 break;
200 case 'L':
201 case_conv |= LOW_SECT;
202 case_conv &= ~UP_SECT;
203 break;
204 case 'l':
205 case_conv |= LOW_CHAR;
206 break;
207 case 'E':
208 case_conv = NO_CONV;
209 break;
210 default:
211 /* Backslash as quote mark */
212 fntarget[k++] =
213 convert_case (ctx->dest_mask[j], &case_conv);
215 break;
216 } else {
217 /* Backslash followed by digit */
218 next_reg = ctx->dest_mask[j] - '0';
219 /* Fall through */
223 case '*':
224 if (next_reg < 0 || next_reg >= RE_NREGS
225 || ctx->regs.start[next_reg] < 0) {
226 message (D_ERROR, MSG_ERROR, _(" Invalid target mask "));
227 transform_error = FILE_ABORT;
228 return NULL;
230 for (l = (size_t) ctx->regs.start[next_reg];
231 l < (size_t) ctx->regs.end[next_reg]; l++)
232 fntarget[k++] = convert_case (fnsource[l], &case_conv);
233 next_reg++;
234 break;
236 default:
237 fntarget[k++] = convert_case (ctx->dest_mask[j], &case_conv);
238 break;
241 fntarget[k] = 0;
242 return fntarget;
245 static const char *
246 transform_source (FileOpContext *ctx, const char *source)
248 char *s = g_strdup (source);
249 char *q;
250 const char *p;
252 /* We remove \n from the filename since regex routines would use \n as an anchor */
253 /* this is just to be allowed to maniupulate file names with \n on it */
254 for (q = s; *q; q++) {
255 if (*q == '\n')
256 *q = ' ';
258 p = do_transform_source (ctx, s);
259 g_free (s);
260 return p;
263 static void
264 free_linklist (struct link **linklist)
266 struct link *lp, *lp2;
268 for (lp = *linklist; lp != NULL; lp = lp2) {
269 lp2 = lp->next;
270 g_free (lp);
272 *linklist = NULL;
275 static int
276 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
278 ino_t ino = sb->st_ino;
279 dev_t dev = sb->st_dev;
280 #ifdef USE_VFS
281 struct vfs_class *vfs = vfs_get_class (path);
282 #endif /* USE_VFS */
284 while (lp) {
285 #ifdef USE_VFS
286 if (lp->vfs == vfs)
287 #endif /* USE_VFS */
288 if (lp->ino == ino && lp->dev == dev)
289 return 1;
290 lp = lp->next;
292 return 0;
296 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
297 * and a hardlink was succesfully made
299 static int
300 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
302 struct link *lp;
303 struct vfs_class *my_vfs = vfs_get_class (src_name);
304 ino_t ino = pstat->st_ino;
305 dev_t dev = pstat->st_dev;
306 struct stat link_stat;
307 const char *p;
309 if (vfs_file_class_flags (src_name) & VFSF_NOLINKS)
310 return 0;
312 for (lp = linklist; lp != NULL; lp = lp->next)
313 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev) {
314 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
315 && link_stat.st_dev == dev
316 && vfs_get_class (lp->name) == my_vfs) {
317 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
318 was copied to */
319 if (vfs_get_class (dst_name) == vfs_get_class (p)) {
320 if (!mc_stat (p, &link_stat)) {
321 if (!mc_link (p, dst_name))
322 return 1;
326 message (D_ERROR, MSG_ERROR, _(" Cannot make the hardlink "));
327 return 0;
329 lp = (struct link *) g_malloc (sizeof (struct link) + strlen (src_name)
330 + strlen (dst_name) + 1);
331 if (lp) {
332 char *lpdstname;
333 lp->vfs = my_vfs;
334 lp->ino = ino;
335 lp->dev = dev;
336 strcpy (lp->name, src_name);
337 lpdstname = lp->name + strlen(lp->name) + 1;
338 strcpy (lpdstname, dst_name);
339 lp->next = linklist;
340 linklist = lp;
342 return 0;
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).
352 static int
353 make_symlink (FileOpContext *ctx, const char *src_path, const char *dst_path)
355 char link_target[MC_MAXPATHLEN];
356 int len;
357 int return_status;
358 struct stat sb;
359 int dst_is_symlink;
361 if (mc_lstat (dst_path, &sb) == 0 && S_ISLNK (sb.st_mode))
362 dst_is_symlink = 1;
363 else
364 dst_is_symlink = 0;
366 retry_src_readlink:
367 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
368 if (len < 0) {
369 return_status =
370 file_error (_(" Cannot read source link \"%s\" \n %s "),
371 src_path);
372 if (return_status == FILE_RETRY)
373 goto retry_src_readlink;
374 return return_status;
376 link_target[len] = 0;
378 if (ctx->stable_symlinks)
379 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path)) {
380 message (D_ERROR, MSG_ERROR,
381 _(" Cannot make stable symlinks across "
382 "non-local filesystems: \n\n"
383 " Option Stable Symlinks will be disabled "));
384 ctx->stable_symlinks = 0;
387 if (ctx->stable_symlinks && *link_target != PATH_SEP) {
388 char *p, *q, *s;
390 const char *r = strrchr (src_path, PATH_SEP);
392 if (r) {
393 p = g_strndup (src_path, r - src_path + 1);
394 if (*dst_path == PATH_SEP)
395 q = g_strdup (dst_path);
396 else
397 q = g_strconcat (p, dst_path, (char *) NULL);
398 s = strrchr (q, PATH_SEP);
399 if (s) {
400 s[1] = 0;
401 s = g_strconcat (p, link_target, (char *) NULL);
402 g_free (p);
403 g_strlcpy (link_target, s, sizeof (link_target));
404 g_free (s);
405 s = diff_two_paths (q, link_target);
406 if (s) {
407 g_strlcpy (link_target, s, sizeof (link_target));
408 g_free (s);
410 } else
411 g_free (p);
412 g_free (q);
415 retry_dst_symlink:
416 if (mc_symlink (link_target, dst_path) == 0)
417 /* Success */
418 return FILE_CONT;
420 * if dst_exists, it is obvious that this had failed.
421 * We can delete the old symlink and try again...
423 if (dst_is_symlink) {
424 if (!mc_unlink (dst_path))
425 if (mc_symlink (link_target, dst_path) == 0)
426 /* Success */
427 return FILE_CONT;
429 return_status =
430 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
431 dst_path);
432 if (return_status == FILE_RETRY)
433 goto retry_dst_symlink;
434 return return_status;
437 static int
438 progress_update_one (FileOpContext *ctx,
439 off_t *progress_count,
440 double *progress_bytes, off_t add, int is_toplevel_file)
442 int ret;
444 if (is_toplevel_file || ctx->progress_totals_computed) {
445 (*progress_count)++;
446 (*progress_bytes) += add;
449 /* Apply some heuristic here to not call the update stuff very often */
450 ret =
451 file_progress_show_count (ctx, *progress_count,
452 ctx->progress_count);
453 if (ret != FILE_CONT)
454 return ret;
455 ret =
456 file_progress_show_bytes (ctx, *progress_bytes,
457 ctx->progress_bytes);
459 return ret;
462 /* Status of the destination file */
463 enum {
464 DEST_NONE, /* Not created */
465 DEST_SHORT, /* Created, not fully copied */
466 DEST_FULL /* Created, fully copied */
470 copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path,
471 int ask_overwrite, off_t *progress_count,
472 double *progress_bytes, int is_toplevel_file)
474 uid_t src_uid = (uid_t) - 1;
475 gid_t src_gid = (gid_t) - 1;
477 char *buf = NULL;
478 int buf_size = BUF_8K;
479 int src_desc, dest_desc = -1;
480 int n_read, n_written;
481 mode_t src_mode = 0; /* The mode of the source file */
482 struct stat sb, sb2;
483 struct utimbuf utb;
484 int dst_exists = 0, appending = 0;
485 off_t n_read_total = 0, file_size = -1;
486 int return_status, temp_status;
487 struct timeval tv_transfer_start;
488 int dst_status = DEST_NONE; /* 1 if the file is not fully copied */
490 /* FIXME: We should not be using global variables! */
491 ctx->do_reget = 0;
492 return_status = FILE_RETRY;
494 if (file_progress_show_source (ctx, src_path) == FILE_ABORT ||
495 file_progress_show_target (ctx, dst_path) == FILE_ABORT)
496 return FILE_ABORT;
498 mc_refresh ();
500 while (mc_stat (dst_path, &sb2) == 0) {
501 if (S_ISDIR (sb2.st_mode)) {
502 return_status =
503 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
504 dst_path);
505 if (return_status == FILE_RETRY)
506 continue;
507 return return_status;
509 dst_exists = 1;
510 break;
513 while ((*ctx->stat_func) (src_path, &sb)) {
514 return_status =
515 file_error (_(" Cannot stat source file \"%s\" \n %s "),
516 src_path);
517 if (return_status != FILE_RETRY)
518 return return_status;
521 if (dst_exists) {
522 /* Destination already exists */
523 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino) {
524 message (D_ERROR, MSG_ERROR,
525 _(" `%s' and `%s' are the same file "), src_path, dst_path);
526 do_refresh ();
527 return FILE_SKIP;
530 /* Should we replace destination? */
531 if (ask_overwrite) {
532 ctx->do_reget = 0;
533 return_status = query_replace (ctx, dst_path, &sb, &sb2);
534 if (return_status != FILE_CONT)
535 return return_status;
539 if (!ctx->do_append) {
540 /* Check the hardlinks */
541 if (!ctx->follow_links && sb.st_nlink > 1 &&
542 check_hardlinks (src_path, dst_path, &sb) == 1) {
543 /* We have made a hardlink - no more processing is necessary */
544 return FILE_CONT;
547 if (S_ISLNK (sb.st_mode)) {
548 int retval;
550 retval = make_symlink (ctx, src_path, dst_path);
551 return retval;
554 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
555 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) ||
556 S_ISSOCK (sb.st_mode)) {
557 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill,
558 sb.st_rdev) < 0) {
559 return_status = file_error (
560 _(" Cannot create special file \"%s\" \n %s "), dst_path);
561 if (return_status == FILE_RETRY)
562 continue;
563 return return_status;
565 /* Success */
567 while (ctx->preserve_uidgid
568 && mc_chown (dst_path, sb.st_uid, sb.st_gid)) {
569 temp_status = file_error (
570 _(" Cannot chown target file \"%s\" \n %s "), dst_path);
571 if (temp_status == FILE_RETRY)
572 continue;
573 return temp_status;
575 while (ctx->preserve &&
576 mc_chmod (dst_path, sb.st_mode & ctx->umask_kill)) {
577 temp_status = file_error (
578 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
579 if (temp_status == FILE_RETRY)
580 continue;
581 return temp_status;
583 return FILE_CONT;
587 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
589 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0) {
590 return_status = file_error (
591 _(" Cannot open source file \"%s\" \n %s "), src_path);
592 if (return_status == FILE_RETRY)
593 continue;
594 ctx->do_append = 0;
595 return return_status;
598 if (ctx->do_reget) {
599 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget) {
600 message (D_ERROR, _("Warning"),
601 _(" Reget failed, about to overwrite file "));
602 ctx->do_reget = ctx->do_append = 0;
606 while (mc_fstat (src_desc, &sb)) {
607 return_status = file_error (
608 _(" Cannot fstat source file \"%s\" \n %s "), src_path);
609 if (return_status == FILE_RETRY)
610 continue;
611 ctx->do_append = 0;
612 goto ret;
614 src_mode = sb.st_mode;
615 src_uid = sb.st_uid;
616 src_gid = sb.st_gid;
617 utb.actime = sb.st_atime;
618 utb.modtime = sb.st_mtime;
619 file_size = sb.st_size;
621 /* Create the new regular file with small permissions initially,
622 do not create a security hole. FIXME: You have security hole
623 here, btw. Imagine copying to /tmp and symlink attack :-( */
625 while ((dest_desc = mc_open (dst_path, O_WRONLY | (ctx->do_append ?
626 O_APPEND : (O_CREAT | O_TRUNC)), 0600)) < 0) {
627 return_status = file_error (
628 _(" Cannot create target file \"%s\" \n %s "), dst_path);
629 if (return_status == FILE_RETRY)
630 continue;
631 ctx->do_append = 0;
632 goto ret;
634 dst_status = DEST_SHORT; /* file opened, but not fully copied */
636 appending = ctx->do_append;
637 ctx->do_append = 0;
639 /* Find out the optimal buffer size. */
640 while (mc_fstat (dest_desc, &sb)) {
641 return_status = file_error (
642 _(" Cannot fstat target file \"%s\" \n %s "), dst_path);
643 if (return_status == FILE_RETRY)
644 continue;
645 goto ret;
647 buf = g_malloc (buf_size);
649 ctx->eta_secs = 0.0;
650 ctx->bps = 0;
652 return_status = file_progress_show (ctx, 0, file_size);
654 mc_refresh ();
656 if (return_status != FILE_CONT)
657 goto ret;
660 struct timeval tv_current, tv_last_update, tv_last_input;
661 int secs, update_secs;
662 long dt;
663 const char *stalled_msg;
665 tv_last_update = tv_transfer_start;
667 for (;;) {
668 /* src_read */
669 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
670 n_read = -1;
671 else
672 while ((n_read = mc_read (src_desc, buf, buf_size)) < 0) {
673 return_status = file_error (
674 _(" Cannot read source file \"%s\" \n %s "), src_path);
675 if (return_status == FILE_RETRY)
676 continue;
677 goto ret;
679 if (n_read == 0)
680 break;
682 gettimeofday (&tv_current, NULL);
684 if (n_read > 0) {
685 char *t = buf;
686 n_read_total += n_read;
688 /* Windows NT ftp servers report that files have no
689 * permissions: -------, so if we happen to have actually
690 * read something, we should fix the permissions.
692 if (!(src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)))
693 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
694 gettimeofday (&tv_last_input, NULL);
696 /* dst_write */
697 while ((n_written =
698 mc_write (dest_desc, t, n_read)) < n_read) {
699 if (n_written > 0) {
700 n_read -= n_written;
701 t += n_written;
702 continue;
704 return_status =
705 file_error (_(" Cannot write target file \"%s\" \n %s "),
706 dst_path);
707 if (return_status != FILE_RETRY)
708 goto ret;
712 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
713 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
714 if (secs > 2) {
715 rotate_dash ();
716 tv_last_update = tv_current;
719 /* 2. Check for a stalled condition */
720 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
721 stalled_msg = "";
722 if (update_secs > 4) {
723 stalled_msg = _("(stalled)");
726 /* 3. Compute ETA */
727 if (secs > 2) {
728 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
730 if (n_read_total) {
731 ctx->eta_secs =
732 ((dt / (double) n_read_total) * file_size) - dt;
733 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
734 } else
735 ctx->eta_secs = 0.0;
738 /* 4. Compute BPS rate */
739 if (secs > 2) {
740 ctx->bps_time =
741 (tv_current.tv_sec - tv_transfer_start.tv_sec);
742 if (ctx->bps_time < 1)
743 ctx->bps_time = 1;
744 ctx->bps = n_read_total / ctx->bps_time;
747 file_progress_set_stalled_label (ctx, stalled_msg);
748 return_status = file_progress_show_bytes (ctx, *progress_bytes +
749 n_read_total + ctx->do_reget, ctx->progress_bytes);
750 if (return_status == FILE_CONT) {
751 return_status =
752 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size);
754 mc_refresh ();
755 if (return_status != FILE_CONT)
756 goto ret;
760 dst_status = DEST_FULL; /* copy successful, don't remove target file */
762 ret:
763 g_free (buf);
765 while (src_desc != -1 && mc_close (src_desc) < 0) {
766 temp_status = file_error (
767 _(" Cannot close source file \"%s\" \n %s "), src_path);
768 if (temp_status == FILE_RETRY)
769 continue;
770 if (temp_status == FILE_ABORT)
771 return_status = temp_status;
772 break;
775 while (dest_desc != -1 && mc_close (dest_desc) < 0) {
776 temp_status = file_error (
777 _(" Cannot close target file \"%s\" \n %s "), dst_path);
778 if (temp_status == FILE_RETRY)
779 continue;
780 return_status = temp_status;
781 break;
784 if (dst_status == DEST_SHORT) {
785 /* Remove short file */
786 int result;
787 result = query_dialog (_("Copy"),
788 _("Incomplete file was retrieved. Keep it?"),
789 D_ERROR, 2, _("&Delete"), _("&Keep"));
790 if (!result)
791 mc_unlink (dst_path);
792 } else if (dst_status == DEST_FULL) {
793 /* Copy has succeeded */
794 if (!appending && ctx->preserve_uidgid) {
795 while (mc_chown (dst_path, src_uid, src_gid)) {
796 temp_status = file_error (
797 _(" Cannot chown target file \"%s\" \n %s "), dst_path);
798 if (temp_status == FILE_RETRY)
799 continue;
800 return_status = temp_status;
801 break;
805 if (!appending && ctx->preserve) {
806 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill))) {
807 temp_status = file_error (
808 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
809 if (temp_status != FILE_RETRY) {
810 return_status = temp_status;
811 break;
814 mc_utime (dst_path, &utb);
818 if (return_status == FILE_CONT)
819 return_status =
820 progress_update_one (ctx, progress_count, progress_bytes,
821 file_size, is_toplevel_file);
823 return return_status;
827 * I think these copy_*_* functions should have a return type.
828 * anyway, this function *must* have two directories as arguments.
830 /* FIXME: This function needs to check the return values of the
831 function calls */
833 copy_dir_dir (FileOpContext *ctx, const char *s, const char *d, int toplevel,
834 int move_over, int delete, struct link *parent_dirs,
835 off_t *progress_count, double *progress_bytes)
837 struct dirent *next;
838 struct stat buf, cbuf;
839 DIR *reading;
840 char *path, *mdpath, *dest_file, *dest_dir;
841 int return_status = FILE_CONT;
842 struct utimbuf utb;
843 struct link *lp;
845 /* First get the mode of the source dir */
846 retry_src_stat:
847 if ((*ctx->stat_func) (s, &cbuf)) {
848 return_status =
849 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s);
850 if (return_status == FILE_RETRY)
851 goto retry_src_stat;
852 return return_status;
855 if (is_in_linklist (dest_dirs, s, &cbuf)) {
856 /* Don't copy a directory we created before (we don't want to copy
857 infinitely if a directory is copied into itself) */
858 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
859 return FILE_CONT;
862 /* Hmm, hardlink to directory??? - Norbert */
863 /* FIXME: In this step we should do something
864 in case the destination already exist */
865 /* Check the hardlinks */
866 if (ctx->preserve && cbuf.st_nlink > 1
867 && check_hardlinks (s, d, &cbuf) == 1) {
868 /* We have made a hardlink - no more processing is necessary */
869 return return_status;
872 if (!S_ISDIR (cbuf.st_mode)) {
873 return_status =
874 file_error (_(" Source \"%s\" is not a directory \n %s "), s);
875 if (return_status == FILE_RETRY)
876 goto retry_src_stat;
877 return return_status;
880 if (is_in_linklist (parent_dirs, s, &cbuf)) {
881 /* we found a cyclic symbolic link */
882 message (D_ERROR, MSG_ERROR,
883 _(" Cannot copy cyclic symbolic link \n `%s' "), s);
884 return FILE_SKIP;
887 lp = g_new (struct link, 1);
888 lp->vfs = vfs_get_class (s);
889 lp->ino = cbuf.st_ino;
890 lp->dev = cbuf.st_dev;
891 lp->next = parent_dirs;
892 parent_dirs = lp;
894 retry_dst_stat:
895 /* Now, check if the dest dir exists, if not, create it. */
896 if (mc_stat (d, &buf)) {
897 /* Here the dir doesn't exist : make it ! */
899 if (move_over) {
900 if (mc_rename (s, d) == 0) {
901 g_free (parent_dirs);
902 return FILE_CONT;
905 dest_dir = g_strdup (d);
906 } else {
908 * If the destination directory exists, we want to copy the whole
909 * directory, but we only want this to happen once.
911 * Escape sequences added to the * to compiler warnings.
912 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
913 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
915 if (!S_ISDIR (buf.st_mode)) {
916 return_status = file_error(
917 _(" Destination \"%s\" must be a directory \n %s "), d);
918 if (return_status == FILE_RETRY)
919 goto retry_dst_stat;
920 g_free (parent_dirs);
921 return return_status;
923 /* Dive into subdir if exists */
924 if (toplevel && ctx->dive_into_subdirs) {
925 dest_dir = mhl_str_dir_plus_file (d, x_basename (s));
926 } else {
927 dest_dir = g_strdup (d);
928 goto dont_mkdir;
931 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU)) {
932 return_status = file_error (
933 _(" Cannot create target directory \"%s\" \n %s "), dest_dir);
934 if (return_status != FILE_RETRY)
935 goto ret;
938 lp = g_new (struct link, 1);
939 mc_stat (dest_dir, &buf);
940 lp->vfs = vfs_get_class (dest_dir);
941 lp->ino = buf.st_ino;
942 lp->dev = buf.st_dev;
943 lp->next = dest_dirs;
944 dest_dirs = lp;
946 if (ctx->preserve_uidgid) {
947 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid)) {
948 return_status = file_error (
949 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir);
950 if (return_status != FILE_RETRY)
951 goto ret;
955 dont_mkdir:
956 /* open the source dir for reading */
957 if ((reading = mc_opendir (s)) == 0) {
958 goto ret;
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, "."))
966 continue;
967 if (!strcmp (next->d_name, ".."))
968 continue;
970 /* get the filename and add it to the src directory */
971 path = mhl_str_dir_plus_file (s, next->d_name);
973 (*ctx->stat_func) (path, &buf);
974 if (S_ISDIR (buf.st_mode)) {
975 mdpath = mhl_str_dir_plus_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, delete,
983 parent_dirs, progress_count, progress_bytes);
984 g_free (mdpath);
985 } else {
986 dest_file = mhl_str_dir_plus_file (dest_dir, x_basename (path));
987 return_status = copy_file_file (ctx, path, dest_file, 1,
988 progress_count, progress_bytes, 0);
989 g_free (dest_file);
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;
997 lp->next = 0;
998 if (erase_list) {
999 tail->next = lp;
1000 tail = lp;
1001 } else
1002 erase_list = tail = lp;
1003 } else {
1004 if (S_ISDIR (buf.st_mode)) {
1005 return_status = erase_dir_iff_empty (ctx, path);
1006 } else
1007 return_status = erase_file (ctx, path, 0, 0, 0);
1010 g_free (path);
1012 mc_closedir (reading);
1014 if (ctx->preserve) {
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);
1021 ret:
1022 g_free (dest_dir);
1023 g_free (parent_dirs);
1024 return return_status;
1027 /* }}} */
1029 /* {{{ Move routines */
1031 static int
1032 move_file_file (FileOpContext *ctx, const char *s, const char *d,
1033 off_t *progress_count, double *progress_bytes)
1035 struct stat src_stats, dst_stats;
1036 int return_status = FILE_CONT;
1037 gboolean copy_done = FALSE;
1039 if (file_progress_show_source (ctx, s) == FILE_ABORT
1040 || file_progress_show_target (ctx, d) == FILE_ABORT)
1041 return FILE_ABORT;
1043 mc_refresh ();
1045 while (mc_lstat (s, &src_stats) != 0) {
1046 /* Source doesn't exist */
1047 return_status =
1048 file_error (_(" Cannot stat file \"%s\" \n %s "), s);
1049 if (return_status != FILE_RETRY)
1050 return return_status;
1053 if (mc_lstat (d, &dst_stats) == 0) {
1054 if (src_stats.st_dev == dst_stats.st_dev
1055 && src_stats.st_ino == dst_stats.st_ino) {
1056 int msize = COLS - 36;
1057 char st[MC_MAXPATHLEN];
1058 char dt[MC_MAXPATHLEN];
1060 if (msize < 0)
1061 msize = 40;
1062 msize /= 2;
1064 strcpy (st, path_trunc (s, msize));
1065 strcpy (dt, path_trunc (d, msize));
1066 message (D_ERROR, MSG_ERROR,
1067 _(" `%s' and `%s' are the same file "), st, dt);
1068 do_refresh ();
1069 return FILE_SKIP;
1072 if (S_ISDIR (dst_stats.st_mode)) {
1073 message (D_ERROR, MSG_ERROR,
1074 _(" Cannot overwrite directory `%s' "), d);
1075 do_refresh ();
1076 return FILE_SKIP;
1079 if (confirm_overwrite) {
1080 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1081 if (return_status != FILE_CONT)
1082 return return_status;
1084 /* Ok to overwrite */
1087 if (!ctx->do_append) {
1088 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks) {
1089 if ((return_status = make_symlink (ctx, s, d)) == FILE_CONT) {
1090 goto retry_src_remove;
1091 } else
1092 return return_status;
1095 if (mc_rename (s, d) == 0) {
1096 return progress_update_one (ctx, progress_count,
1097 progress_bytes,
1098 src_stats.st_size, 1);
1101 #if 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. */
1106 else
1107 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1109 if (errno != EXDEV) {
1110 return_status =
1111 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s,
1113 if (return_status == FILE_RETRY)
1114 goto retry_rename;
1115 return return_status;
1117 #endif
1119 /* Failed because filesystem boundary -> copy the file instead */
1120 return_status =
1121 copy_file_file (ctx, s, d, 0, progress_count, progress_bytes, 1);
1122 if (return_status != FILE_CONT)
1123 return return_status;
1125 copy_done = TRUE;
1127 if ((return_status =
1128 file_progress_show_source (ctx, NULL)) != FILE_CONT
1129 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1130 return return_status;
1132 mc_refresh ();
1134 retry_src_remove:
1135 if (mc_unlink (s)) {
1136 return_status =
1137 file_error (_(" Cannot remove file \"%s\" \n %s "), s);
1138 if (return_status == FILE_RETRY)
1139 goto retry_src_remove;
1140 return return_status;
1143 if (!copy_done) {
1144 return_status = progress_update_one (ctx,
1145 progress_count,
1146 progress_bytes,
1147 src_stats.st_size, 1);
1150 return return_status;
1154 move_dir_dir (FileOpContext *ctx, const char *s, const char *d,
1155 off_t *progress_count, double *progress_bytes)
1157 struct stat sbuf, dbuf, destbuf;
1158 struct link *lp;
1159 char *destdir;
1160 int return_status;
1161 int move_over = 0;
1163 if (file_progress_show_source (ctx, s) == FILE_ABORT ||
1164 file_progress_show_target (ctx, d) == FILE_ABORT)
1165 return FILE_ABORT;
1167 mc_refresh ();
1169 mc_stat (s, &sbuf);
1170 if (mc_stat (d, &dbuf))
1171 destdir = g_strdup (d); /* destination doesn't exist */
1172 else if (!ctx->dive_into_subdirs) {
1173 destdir = g_strdup (d);
1174 move_over = 1;
1175 } else
1176 destdir = mhl_str_dir_plus_file (d, x_basename (s));
1178 if (sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino) {
1179 int msize = COLS - 36;
1180 char st[MC_MAXPATHLEN];
1181 char dt[MC_MAXPATHLEN];
1183 if (msize < 0)
1184 msize = 40;
1185 msize /= 2;
1187 strcpy (st, path_trunc (s, msize));
1188 strcpy (dt, path_trunc (d, msize));
1189 message (D_ERROR, MSG_ERROR,
1190 _(" `%s' and `%s' are the same directory "), st, dt);
1191 do_refresh ();
1192 return FILE_SKIP;
1195 /* Check if the user inputted an existing dir */
1196 retry_dst_stat:
1197 if (!mc_stat (destdir, &destbuf)) {
1198 if (move_over) {
1199 return_status = copy_dir_dir (ctx, s, destdir, 0, 1, 1, 0,
1200 progress_count, progress_bytes);
1202 if (return_status != FILE_CONT)
1203 goto ret;
1204 goto oktoret;
1205 } else {
1206 if (S_ISDIR (destbuf.st_mode))
1207 return_status =
1208 file_error (_
1209 (" Cannot overwrite directory \"%s\" %s "),
1210 destdir);
1211 else
1212 return_status =
1213 file_error (_(" Cannot overwrite file \"%s\" %s "),
1214 destdir);
1215 if (return_status == FILE_RETRY)
1216 goto retry_dst_stat;
1218 g_free (destdir);
1219 return return_status;
1222 retry_rename:
1223 if (mc_rename (s, destdir) == 0) {
1224 return_status = FILE_CONT;
1225 goto ret;
1228 if (errno != EXDEV) {
1229 return_status =
1230 files_error (_
1231 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1232 s, d);
1233 if (return_status == FILE_RETRY)
1234 goto retry_rename;
1235 goto ret;
1237 /* Failed because of filesystem boundary -> copy dir instead */
1238 return_status =
1239 copy_dir_dir (ctx, s, destdir, 0, 0, 1, 0, progress_count,
1240 progress_bytes);
1242 if (return_status != FILE_CONT)
1243 goto ret;
1244 oktoret:
1245 if ((return_status =
1246 file_progress_show_source (ctx, NULL)) != FILE_CONT
1247 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1248 goto ret;
1250 mc_refresh ();
1251 if (ctx->erase_at_end) {
1252 for (; erase_list && return_status != FILE_ABORT;) {
1253 if (S_ISDIR (erase_list->st_mode)) {
1254 return_status =
1255 erase_dir_iff_empty (ctx, erase_list->name);
1256 } else
1257 return_status =
1258 erase_file (ctx, erase_list->name, 0, 0, 0);
1259 lp = erase_list;
1260 erase_list = erase_list->next;
1261 g_free (lp);
1264 erase_dir_iff_empty (ctx, s);
1266 ret:
1267 g_free (destdir);
1268 while (erase_list) {
1269 lp = erase_list;
1270 erase_list = erase_list->next;
1271 g_free (lp);
1273 return return_status;
1276 /* }}} */
1278 /* {{{ Erase routines */
1279 /* Don't update progress status if progress_count==NULL */
1280 static int
1281 erase_file (FileOpContext *ctx, const char *s, off_t *progress_count,
1282 double *progress_bytes, int is_toplevel_file)
1284 int return_status;
1285 struct stat buf;
1287 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1288 return FILE_ABORT;
1289 mc_refresh ();
1291 if (progress_count && mc_lstat (s, &buf)) {
1292 /* ignore, most likely the mc_unlink fails, too */
1293 buf.st_size = 0;
1296 while (mc_unlink (s)) {
1297 return_status =
1298 file_error (_(" Cannot delete file \"%s\" \n %s "), s);
1299 if (return_status != FILE_RETRY)
1300 return return_status;
1303 if (progress_count)
1304 return progress_update_one (ctx, progress_count, progress_bytes,
1305 buf.st_size, is_toplevel_file);
1306 else
1307 return FILE_CONT;
1310 static int
1311 recursive_erase (FileOpContext *ctx, const char *s, off_t *progress_count,
1312 double *progress_bytes)
1314 struct dirent *next;
1315 struct stat buf;
1316 DIR *reading;
1317 char *path;
1318 int return_status = FILE_CONT;
1320 if (!strcmp (s, ".."))
1321 return 1;
1323 reading = mc_opendir (s);
1325 if (!reading)
1326 return 1;
1328 while ((next = mc_readdir (reading)) && return_status == FILE_CONT) {
1329 if (!strcmp (next->d_name, "."))
1330 continue;
1331 if (!strcmp (next->d_name, ".."))
1332 continue;
1333 path = mhl_str_dir_plus_file (s, next->d_name);
1334 if (mc_lstat (path, &buf)) {
1335 g_free (path);
1336 mc_closedir (reading);
1337 return 1;
1339 if (S_ISDIR (buf.st_mode))
1340 return_status =
1341 (recursive_erase
1342 (ctx, path, progress_count, progress_bytes)
1343 != FILE_CONT);
1344 else
1345 return_status =
1346 erase_file (ctx, path, progress_count, progress_bytes, 0);
1347 g_free (path);
1349 mc_closedir (reading);
1350 if (return_status != FILE_CONT)
1351 return return_status;
1352 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1353 return FILE_ABORT;
1354 mc_refresh ();
1356 while (my_rmdir (s)) {
1357 return_status =
1358 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1359 if (return_status != FILE_RETRY)
1360 return return_status;
1363 return FILE_CONT;
1366 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1367 in the directory path points to, 0 else. */
1368 static int
1369 check_dir_is_empty (const char *path)
1371 DIR *dir;
1372 struct dirent *d;
1373 int i;
1375 dir = mc_opendir (path);
1376 if (!dir)
1377 return -1;
1379 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir)) {
1380 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1381 (d->d_name[1] == '.'
1382 && d->d_name[2] == '\0')))
1383 continue; /* "." or ".." */
1384 i = 0;
1385 break;
1388 mc_closedir (dir);
1389 return i;
1393 erase_dir (FileOpContext *ctx, const char *s, off_t *progress_count,
1394 double *progress_bytes)
1396 int error;
1398 if (strcmp (s, "..") == 0)
1399 return FILE_SKIP;
1401 if (strcmp (s, ".") == 0)
1402 return FILE_SKIP;
1404 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1405 return FILE_ABORT;
1406 mc_refresh ();
1408 /* The old way to detect a non empty directory was:
1409 error = my_rmdir (s);
1410 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1411 For the linux user space nfs server (nfs-server-2.2beta29-2)
1412 we would have to check also for EIO. I hope the new way is
1413 fool proof. (Norbert)
1415 error = check_dir_is_empty (s);
1416 if (error == 0) { /* not empty */
1417 error = query_recursive (ctx, s);
1418 if (error == FILE_CONT)
1419 return recursive_erase (ctx, s, progress_count,
1420 progress_bytes);
1421 else
1422 return error;
1425 while (my_rmdir (s) == -1) {
1426 error =
1427 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1428 if (error != FILE_RETRY)
1429 return error;
1432 return FILE_CONT;
1435 static int
1436 erase_dir_iff_empty (FileOpContext *ctx, const char *s)
1438 int error;
1440 if (strcmp (s, "..") == 0)
1441 return FILE_SKIP;
1443 if (strcmp (s, ".") == 0)
1444 return FILE_SKIP;
1446 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1447 return FILE_ABORT;
1448 mc_refresh ();
1450 if (1 != check_dir_is_empty (s)) /* not empty or error */
1451 return FILE_CONT;
1453 while (my_rmdir (s)) {
1454 error =
1455 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1456 if (error != FILE_RETRY)
1457 return error;
1460 return FILE_CONT;
1463 /* }}} */
1465 /* {{{ Panel operate routines */
1468 * Return currently selected entry name or the name of the first marked
1469 * entry if there is one.
1471 static char *
1472 panel_get_file (WPanel *panel, struct stat *stat_buf)
1474 int i;
1476 if (get_current_type () == view_tree) {
1477 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
1478 char *tree_name = tree_selected_name (tree);
1480 mc_stat (tree_name, stat_buf);
1481 return tree_name;
1484 if (panel->marked) {
1485 for (i = 0; i < panel->count; i++)
1486 if (panel->dir.list[i].f.marked) {
1487 *stat_buf = panel->dir.list[i].st;
1488 return panel->dir.list[i].fname;
1490 } else {
1491 *stat_buf = panel->dir.list[panel->selected].st;
1492 return panel->dir.list[panel->selected].fname;
1494 g_assert_not_reached ();
1495 return NULL;
1499 * compute_dir_size:
1501 * Computes the number of bytes used by the files in a directory
1503 void
1504 compute_dir_size (const char *dirname, off_t *ret_marked, double *ret_total)
1506 DIR *dir;
1507 struct dirent *dirent;
1509 dir = mc_opendir (dirname);
1511 if (!dir)
1512 return;
1514 while ((dirent = mc_readdir (dir)) != NULL) {
1515 struct stat s;
1516 char *fullname;
1517 int res;
1519 if (strcmp (dirent->d_name, ".") == 0)
1520 continue;
1521 if (strcmp (dirent->d_name, "..") == 0)
1522 continue;
1524 fullname = mhl_str_dir_plus_file (dirname, dirent->d_name);
1526 res = mc_lstat (fullname, &s);
1528 if (res != 0) {
1529 g_free (fullname);
1530 continue;
1533 if (S_ISDIR (s.st_mode)) {
1534 off_t subdir_count = 0;
1535 double subdir_bytes = 0;
1537 compute_dir_size (fullname, &subdir_count, &subdir_bytes);
1539 *ret_marked += subdir_count;
1540 *ret_total += subdir_bytes;
1541 } else {
1542 (*ret_marked)++;
1543 *ret_total += s.st_size;
1545 g_free (fullname);
1548 mc_closedir (dir);
1552 * panel_compute_totals:
1554 * compute the number of files and the number of bytes
1555 * used up by the whole selection, recursing directories
1556 * as required. In addition, it checks to see if it will
1557 * overwrite any files by doing the copy.
1559 static void
1560 panel_compute_totals (WPanel *panel, off_t *ret_marked, double *ret_total)
1562 int i;
1564 *ret_marked = 0;
1565 *ret_total = 0.0;
1567 for (i = 0; i < panel->count; i++) {
1568 struct stat *s;
1570 if (!panel->dir.list[i].f.marked)
1571 continue;
1573 s = &panel->dir.list[i].st;
1575 if (S_ISDIR (s->st_mode)) {
1576 char *dir_name;
1577 off_t subdir_count = 0;
1578 double subdir_bytes = 0;
1580 dir_name =
1581 mhl_str_dir_plus_file (panel->cwd, panel->dir.list[i].fname);
1582 compute_dir_size (dir_name, &subdir_count, &subdir_bytes);
1584 *ret_marked += subdir_count;
1585 *ret_total += subdir_bytes;
1586 g_free (dir_name);
1587 } else {
1588 (*ret_marked)++;
1589 *ret_total += s->st_size;
1595 * This array introduced to avoid translation problems. The former (op_names)
1596 * is assumed to be nouns, suitable in dialog box titles; this one should
1597 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1598 * Notice first symbol - it is to fool gettext and force these strings to
1599 * be different for it. First symbol is skipped while building a prompt.
1600 * (I don't use spaces around the words, because someday they could be
1601 * dropped, when widgets get smarter)
1603 static const char *op_names1[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1604 #define FMD_XLEN 64
1606 int fmd_xlen = FMD_XLEN;
1609 * These are formats for building a prompt. Parts encoded as follows:
1610 * %o - operation from op_names1
1611 * %f - file/files or files/directories, as appropriate
1612 * %m - "with source mask" or question mark for delete
1613 * %s - source name (truncated)
1614 * %d - number of marked files
1615 * %e - "to:" or question mark for delete
1617 * xgettext:no-c-format */
1618 static const char *one_format = N_("%o %f \"%s\"%m");
1619 /* xgettext:no-c-format */
1620 static const char *many_format = N_("%o %d %f%m");
1621 static const char *prompt_parts[] = {
1622 N_("file"), N_("files"), N_("directory"), N_("directories"),
1623 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1627 * Generate user prompt for panel operation.
1628 * single_source is the name if the source entry or NULL for multiple
1629 * entries.
1630 * src_stat is only used when single_source is not NULL.
1632 static void
1633 panel_operate_generate_prompt (const WPanel *panel, const int operation,
1634 const char *single_source,
1635 const struct stat *src_stat)
1637 register const char *sp, *cp;
1638 register int i;
1639 char format_string[BUF_MEDIUM];
1640 char *dp = format_string;
1642 #ifdef ENABLE_NLS
1643 static int i18n_flag = 0;
1644 if (!i18n_flag) {
1645 fmd_init_i18n (FALSE); /* to get proper fmd_xlen */
1647 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1648 op_names1[i] = _(op_names1[i]);
1650 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1651 prompt_parts[i] = _(prompt_parts[i]);
1653 one_format = _(one_format);
1654 many_format = _(many_format);
1655 i18n_flag = 1;
1657 #endif /* ENABLE_NLS */
1659 sp = single_source ? one_format : many_format;
1661 while (*sp) {
1662 switch (*sp) {
1663 case '%':
1664 cp = NULL;
1665 switch (sp[1]) {
1666 case 'o':
1667 cp = op_names1[operation] + 1;
1668 break;
1669 case 'm':
1670 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1671 break;
1672 case 'e':
1673 cp = operation == OP_DELETE ? "?" : prompt_parts[6];
1674 break;
1675 case 'f':
1676 if (single_source) {
1677 cp = S_ISDIR (src_stat->
1678 st_mode) ? prompt_parts[2] :
1679 prompt_parts[0];
1680 } else {
1681 cp = (panel->marked == panel->dirs_marked)
1682 ? prompt_parts[3]
1683 : (panel->dirs_marked ? prompt_parts[4]
1684 : prompt_parts[1]);
1686 break;
1687 default:
1688 *dp++ = *sp++;
1690 if (cp) {
1691 sp += 2;
1692 while (*cp)
1693 *dp++ = *cp++;
1695 break;
1696 default:
1697 *dp++ = *sp++;
1700 *dp = '\0';
1702 if (single_source) {
1703 i = fmd_xlen - strlen (format_string) - 4;
1704 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1705 name_trunc (single_source, i));
1706 } else {
1707 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1708 panel->marked);
1709 i = strlen (cmd_buf) + 6 - fmd_xlen;
1710 if (i > 0) {
1711 fmd_xlen += i;
1712 fmd_init_i18n (TRUE); /* to recalculate positions of child widgets */
1718 * panel_operate:
1720 * Performs one of the operations on the selection on the source_panel
1721 * (copy, delete, move).
1723 * Returns 1 if did change the directory
1724 * structure, Returns 0 if user aborted
1726 * force_single forces operation on the current entry and affects
1727 * default destination. Current filename is used as default.
1730 panel_operate (void *source_panel, FileOperation operation,
1731 int force_single)
1733 WPanel *panel = source_panel;
1734 #ifdef WITH_FULL_PATHS
1735 char *source_with_path = NULL;
1736 #else
1737 # define source_with_path source
1738 #endif /* !WITH_FULL_PATHS */
1739 char *source = NULL;
1740 char *dest = NULL;
1741 const char *temp = NULL;
1742 char *save_cwd = NULL, *save_dest = NULL;
1743 int single_entry = (get_current_type () == view_tree)
1744 || (panel->marked <= 1) || force_single;
1745 struct stat src_stat, dst_stat;
1746 int i, value;
1747 FileOpContext *ctx;
1749 off_t count = 0;
1750 double bytes = 0;
1752 int dst_result;
1753 int do_bg = 0; /* do background operation? */
1755 free_linklist (&linklist);
1756 free_linklist (&dest_dirs);
1758 if (single_entry) {
1759 if (force_single) {
1760 source = selection (panel)->fname;
1761 src_stat = selection (panel)->st;
1762 } else {
1763 source = panel_get_file (panel, &src_stat);
1766 if (!strcmp (source, "..")) {
1767 message (D_ERROR, MSG_ERROR, _(" Cannot operate on \"..\"! "));
1768 return 0;
1772 /* Generate confirmation prompt */
1773 panel_operate_generate_prompt (panel, operation, source, &src_stat);
1775 ctx = file_op_context_new (operation);
1777 /* Show confirmation dialog */
1778 if (operation == OP_DELETE && confirm_delete) {
1779 if (safe_delete)
1780 query_set_sel (1);
1782 i = query_dialog (_(op_names[operation]), cmd_buf, D_ERROR, 2,
1783 _("&Yes"), _("&No"));
1785 if (i != 0) {
1786 file_op_context_destroy (ctx);
1787 return 0;
1789 } else if (operation != OP_DELETE) {
1790 char *dest_dir;
1791 char *dest_dir_;
1793 /* Forced single operations default to the original name */
1794 if (force_single)
1795 dest_dir = source;
1796 else if (get_other_type () == view_listing)
1797 dest_dir = other_panel->cwd;
1798 else
1799 dest_dir = panel->cwd;
1802 * Add trailing backslash only when do non-locally ops.
1803 * It saves user from occasional file renames (when destination
1804 * dir is deleted)
1806 if (force_single)
1807 /* just copy */
1808 dest_dir_ = mhl_str_dup (dest_dir);
1809 else
1810 /* add trailing separator */
1811 if (*dest_dir && strcmp(&dest_dir[strlen(dest_dir)-1], PATH_SEP_STR)) {
1812 dest_dir_ = mhl_str_concat (dest_dir, PATH_SEP_STR);
1813 } else {
1814 dest_dir_ = mhl_str_dup (dest_dir);
1816 if (!dest_dir_) {
1817 file_op_context_destroy (ctx);
1818 return 0;
1821 dest =
1822 file_mask_dialog (ctx, operation, cmd_buf, dest_dir_,
1823 single_entry, &do_bg);
1824 g_free(dest_dir_);
1826 if (!dest) {
1827 file_op_context_destroy (ctx);
1828 return 0;
1830 if (!*dest) {
1831 file_op_context_destroy (ctx);
1832 g_free (dest);
1833 return 0;
1836 #ifdef WITH_BACKGROUND
1837 /* Did the user select to do a background operation? */
1838 if (do_bg) {
1839 int v;
1841 v = do_background (ctx,
1842 g_strconcat (op_names[operation], ": ",
1843 panel->cwd, NULL));
1844 if (v == -1) {
1845 message (D_ERROR, MSG_ERROR,
1846 _(" Sorry, I could not put the job in background "));
1849 /* If we are the parent */
1850 if (v == 1) {
1851 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
1852 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
1853 /* file_op_context_destroy (ctx); */
1854 return 0;
1857 #endif /* WITH_BACKGROUND */
1859 /* Initialize things */
1860 /* We do not want to trash cache every time file is
1861 created/touched. However, this will make our cache contain
1862 invalid data. */
1863 if (dest) {
1864 if (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1))
1865 save_dest = g_strdup (dest);
1867 if (panel->cwd) {
1868 if (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1))
1869 save_cwd = g_strdup (panel->cwd);
1872 /* Now, let's do the job */
1874 if (do_bg)
1875 ctx->ui = NULL;
1876 else
1877 file_op_context_create_ui (ctx, 1);
1879 /* This code is only called by the tree and panel code */
1880 if (single_entry) {
1881 /* We now have ETA in all cases */
1883 /* One file: FIXME mc_chdir will take user out of any vfs */
1884 if (operation != OP_COPY && get_current_type () == view_tree)
1885 mc_chdir (PATH_SEP_STR);
1887 /* The source and src_stat variables have been initialized before */
1888 #ifdef WITH_FULL_PATHS
1889 source_with_path = mhl_str_dir_plus_file (panel->cwd, source);
1890 #endif /* WITH_FULL_PATHS */
1892 if (operation == OP_DELETE) {
1893 if (S_ISDIR (src_stat.st_mode))
1894 value = erase_dir (ctx, source_with_path, &count, &bytes);
1895 else
1896 value =
1897 erase_file (ctx, source_with_path, &count, &bytes, 1);
1898 } else {
1899 temp = transform_source (ctx, source_with_path);
1901 if (temp == NULL) {
1902 value = transform_error;
1903 } else {
1904 char *temp2 = mhl_str_dir_plus_file (dest, temp);
1905 g_free (dest);
1906 dest = temp2;
1907 temp = NULL;
1909 switch (operation) {
1910 case OP_COPY:
1912 * we use file_mask_op_follow_links only with OP_COPY,
1914 (*ctx->stat_func) (source_with_path, &src_stat);
1916 if (S_ISDIR (src_stat.st_mode))
1917 value =
1918 copy_dir_dir (ctx, source_with_path, dest, 1,
1919 0, 0, 0, &count, &bytes);
1920 else
1921 value =
1922 copy_file_file (ctx, source_with_path, dest, 1,
1923 &count, &bytes, 1);
1924 break;
1926 case OP_MOVE:
1927 if (S_ISDIR (src_stat.st_mode))
1928 value =
1929 move_dir_dir (ctx, source_with_path, dest,
1930 &count, &bytes);
1931 else
1932 value =
1933 move_file_file (ctx, source_with_path, dest,
1934 &count, &bytes);
1935 break;
1937 default:
1938 /* Unknown file operation */
1939 abort ();
1942 } /* Copy or move operation */
1944 if ((value == FILE_CONT) && !force_single)
1945 unmark_files (panel);
1946 } else {
1947 /* Many files */
1948 /* Check destination for copy or move operation */
1949 if (operation != OP_DELETE) {
1950 retry_many_dst_stat:
1951 dst_result = mc_stat (dest, &dst_stat);
1952 if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)) {
1953 if (file_error
1954 (_(" Destination \"%s\" must be a directory \n %s "),
1955 dest) == FILE_RETRY)
1956 goto retry_many_dst_stat;
1957 goto clean_up;
1961 /* Initialize variables for progress bars */
1962 if (operation != OP_MOVE && verbose && file_op_compute_totals) {
1963 panel_compute_totals (panel, &ctx->progress_count,
1964 &ctx->progress_bytes);
1965 ctx->progress_totals_computed = 1;
1966 } else {
1967 ctx->progress_totals_computed = 0;
1968 ctx->progress_count = panel->marked;
1969 ctx->progress_bytes = panel->total;
1972 /* Loop for every file, perform the actual copy operation */
1973 for (i = 0; i < panel->count; i++) {
1974 if (!panel->dir.list[i].f.marked)
1975 continue; /* Skip the unmarked ones */
1977 source = panel->dir.list[i].fname;
1978 src_stat = panel->dir.list[i].st;
1980 #ifdef WITH_FULL_PATHS
1981 g_free (source_with_path);
1982 source_with_path = mhl_str_dir_plus_file (panel->cwd, source);
1983 #endif /* WITH_FULL_PATHS */
1985 if (operation == OP_DELETE) {
1986 if (S_ISDIR (src_stat.st_mode))
1987 value =
1988 erase_dir (ctx, source_with_path, &count, &bytes);
1989 else
1990 value =
1991 erase_file (ctx, source_with_path, &count, &bytes,
1993 } else {
1994 temp = transform_source (ctx, source_with_path);
1995 if (temp == NULL)
1996 value = transform_error;
1997 else {
1998 char *temp2 = mhl_str_dir_plus_file (dest, temp);
2000 source_with_path = mhl_shell_unescape_buf(source_with_path);
2001 temp2 = mhl_shell_unescape_buf(temp2);
2003 switch (operation) {
2004 case OP_COPY:
2006 * we use file_mask_op_follow_links only with OP_COPY
2008 (*ctx->stat_func) (source_with_path, &src_stat);
2009 if (S_ISDIR (src_stat.st_mode))
2010 value =
2011 copy_dir_dir (ctx, source_with_path, temp2,
2012 1, 0, 0, 0, &count, &bytes);
2013 else
2014 value =
2015 copy_file_file (ctx, source_with_path,
2016 temp2, 1, &count, &bytes,
2018 free_linklist (&dest_dirs);
2019 break;
2021 case OP_MOVE:
2022 if (S_ISDIR (src_stat.st_mode))
2023 value =
2024 move_dir_dir (ctx, source_with_path, temp2,
2025 &count, &bytes);
2026 else
2027 value =
2028 move_file_file (ctx, source_with_path,
2029 temp2, &count, &bytes);
2030 break;
2032 default:
2033 /* Unknown file operation */
2034 abort ();
2036 g_free (temp2);
2038 } /* Copy or move operation */
2040 if (value == FILE_ABORT)
2041 goto clean_up;
2043 if (value == FILE_CONT)
2044 do_file_mark (panel, i, 0);
2046 if (file_progress_show_count (ctx, count, ctx->progress_count)
2047 == FILE_ABORT)
2048 goto clean_up;
2050 if (verbose
2051 && file_progress_show_bytes (ctx, bytes,
2052 ctx->progress_bytes) ==
2053 FILE_ABORT)
2054 goto clean_up;
2056 if (operation != OP_DELETE && verbose
2057 && file_progress_show (ctx, 0, 0) == FILE_ABORT)
2058 goto clean_up;
2060 mc_refresh ();
2061 } /* Loop for every file */
2062 } /* Many entries */
2063 clean_up:
2064 /* Clean up */
2066 if (save_cwd) {
2067 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2068 g_free (save_cwd);
2070 if (save_dest) {
2071 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2072 g_free (save_dest);
2075 free_linklist (&linklist);
2076 free_linklist (&dest_dirs);
2077 #ifdef WITH_FULL_PATHS
2078 g_free (source_with_path);
2079 #endif /* WITH_FULL_PATHS */
2080 g_free (dest);
2081 g_free (ctx->dest_mask);
2082 ctx->dest_mask = NULL;
2083 #ifdef WITH_BACKGROUND
2084 /* Let our parent know we are saying bye bye */
2085 if (we_are_background) {
2086 vfs_shut ();
2087 _exit (0);
2089 #endif /* WITH_BACKGROUND */
2091 file_op_context_destroy (ctx);
2092 return 1;
2095 /* }}} */
2097 /* {{{ Query/status report routines */
2099 static int
2100 real_do_file_error (enum OperationMode mode, const char *error)
2102 int result;
2103 const char *msg;
2105 msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
2106 result =
2107 query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"),
2108 _("&Abort"));
2110 switch (result) {
2111 case 0:
2112 do_refresh ();
2113 return FILE_SKIP;
2115 case 1:
2116 do_refresh ();
2117 return FILE_RETRY;
2119 case 2:
2120 default:
2121 return FILE_ABORT;
2125 /* Report error with one file */
2127 file_error (const char *format, const char *file)
2129 g_snprintf (cmd_buf, sizeof (cmd_buf), format,
2130 path_trunc (file, 30), unix_error_string (errno));
2132 return do_file_error (cmd_buf);
2135 /* Report error with two files */
2136 static int
2137 files_error (const char *format, const char *file1, const char *file2)
2139 char nfile1[16];
2140 char nfile2[16];
2142 strcpy (nfile1, path_trunc (file1, 15));
2143 strcpy (nfile2, path_trunc (file2, 15));
2145 g_snprintf (cmd_buf, sizeof (cmd_buf), format, nfile1, nfile2,
2146 unix_error_string (errno));
2148 return do_file_error (cmd_buf);
2151 static int
2152 real_query_recursive (FileOpContext *ctx, enum OperationMode mode, const char *s)
2154 gchar *text;
2156 if (ctx->recursive_result < RECURSIVE_ALWAYS) {
2157 const char *msg =
2158 mode ==
2159 Foreground ?
2160 _("\n Directory not empty. \n"
2161 " Delete it recursively? ")
2162 : _("\n Background process: Directory not empty \n"
2163 " Delete it recursively? ");
2164 text = g_strconcat (_(" Delete: "), path_trunc (s, 30), " ", (char *) NULL);
2166 if (safe_delete)
2167 query_set_sel (1);
2168 ctx->recursive_result = query_dialog (text, msg, D_ERROR, 5,
2169 _("&Yes"), _("&No"),
2170 _("A&ll"), _("Non&e"),
2171 _("&Abort"));
2173 if (ctx->recursive_result != RECURSIVE_ABORT)
2174 do_refresh ();
2175 g_free (text);
2178 switch (ctx->recursive_result) {
2179 case RECURSIVE_YES:
2180 case RECURSIVE_ALWAYS:
2181 return FILE_CONT;
2183 case RECURSIVE_NO:
2184 case RECURSIVE_NEVER:
2185 return FILE_SKIP;
2187 case RECURSIVE_ABORT:
2189 default:
2190 return FILE_ABORT;
2194 #ifdef WITH_BACKGROUND
2195 static int
2196 do_file_error (const char *str)
2198 if (we_are_background)
2199 return parent_call (real_do_file_error, NULL, 1, strlen (str),
2200 str);
2201 else
2202 return real_do_file_error (Foreground, str);
2205 static int
2206 query_recursive (FileOpContext *ctx, const char *s)
2208 if (we_are_background)
2209 return parent_call (real_query_recursive, ctx, 1, strlen (s), s);
2210 else
2211 return real_query_recursive (ctx, Foreground, s);
2214 static int
2215 query_replace (FileOpContext *ctx, const char *destname, struct stat *_s_stat,
2216 struct stat *_d_stat)
2218 if (we_are_background)
2219 return parent_call ((void *) file_progress_real_query_replace,
2220 ctx,
2222 strlen (destname), destname,
2223 sizeof (struct stat), _s_stat,
2224 sizeof (struct stat), _d_stat);
2225 else
2226 return file_progress_real_query_replace (ctx, Foreground, destname,
2227 _s_stat, _d_stat);
2230 #else
2231 static int
2232 do_file_error (const char *str)
2234 return real_do_file_error (Foreground, str);
2237 static int
2238 query_recursive (FileOpContext *ctx, const char *s)
2240 return real_query_recursive (ctx, Foreground, s);
2243 static int
2244 query_replace (FileOpContext *ctx, const char *destname, struct stat *_s_stat,
2245 struct stat *_d_stat)
2247 return file_progress_real_query_replace (ctx, Foreground, destname,
2248 _s_stat, _d_stat);
2251 #endif /* !WITH_BACKGROUND */
2254 Cause emacs to enter folding mode for this file:
2255 Local variables:
2256 end: