Reverted the use of mhl_str_dup and use g_strdup instead.
[midnight-commander.git] / src / file.c
blob2d78e054c565bdb0fcf984b50fc9b017d4046403
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/types.h>
54 #include <mhl/memory.h>
55 #include <mhl/escape.h>
56 #include <mhl/string.h>
58 #include "global.h"
59 #include "tty.h"
60 #include "eregex.h"
61 #include "setup.h"
62 #include "color.h"
63 #include "win.h"
64 #include "dialog.h"
65 #include "widget.h"
66 #include "main.h" /* cmd_buf */
67 #include "layout.h"
68 #include "widget.h"
69 #include "wtools.h"
70 #include "background.h" /* we_are_background */
71 #include "util.h"
73 /* Needed for current_panel, other_panel and WTree */
74 #include "dir.h"
75 #include "panel.h"
76 #include "file.h"
77 #include "filegui.h"
78 #include "tree.h"
79 #include "key.h"
80 #include "../vfs/vfs-impl.h"
82 /* }}} */
84 /* Hack: the vfs code should not rely on this */
85 #define WITH_FULL_PATHS 1
87 int verbose = 1;
90 * Whether the Midnight Commander tries to provide more
91 * information about copy/move sizes and bytes transfered
92 * at the expense of some speed
94 int file_op_compute_totals = 1;
96 /* This is a hard link cache */
97 struct link {
98 struct link *next;
99 struct vfs_class *vfs;
100 dev_t dev;
101 ino_t ino;
102 short linkcount;
103 mode_t st_mode;
104 char name[1];
107 /* the hard link cache */
108 static struct link *linklist = NULL;
110 /* the files-to-be-erased list */
111 static struct link *erase_list;
114 * In copy_dir_dir we use two additional single linked lists: The first -
115 * variable name `parent_dirs' - holds information about already copied
116 * directories and is used to detect cyclic symbolic links.
117 * The second (`dest_dirs' below) holds information about just created
118 * target directories and is used to detect when an directory is copied
119 * into itself (we don't want to copy infinitly).
120 * Both lists don't use the linkcount and name structure members of struct
121 * link.
123 static struct link *dest_dirs = 0;
125 const char *op_names[3] = {
126 N_(" Copy "),
127 N_(" Move "),
128 N_(" Delete ")
131 /* }}} */
133 static int query_replace (FileOpContext * ctx, const char *destname,
134 struct stat *_s_stat, struct stat *_d_stat);
135 static int query_recursive (FileOpContext * ctx, const char *s);
136 static int do_file_error (const char *str);
137 static int erase_dir_iff_empty (FileOpContext *ctx, const char *s);
138 static int erase_file (FileOpContext *ctx, const char *s,
139 off_t *progress_count, double *progress_bytes,
140 int is_toplevel_file);
141 static int files_error (const char *format, const char *file1,
142 const char *file2);
145 enum CaseConvs { NO_CONV = 0, UP_CHAR = 1, LOW_CHAR = 2, UP_SECT =
146 4, LOW_SECT = 8 };
148 static char
149 convert_case (char c, enum CaseConvs *conversion)
151 if (*conversion & UP_CHAR) {
152 *conversion &= ~UP_CHAR;
153 return toupper ((unsigned char) c);
154 } else if (*conversion & LOW_CHAR) {
155 *conversion &= ~LOW_CHAR;
156 return tolower ((unsigned char) c);
157 } else if (*conversion & UP_SECT) {
158 return toupper ((unsigned char) c);
159 } else if (*conversion & LOW_SECT) {
160 return tolower ((unsigned char) c);
161 } else
162 return c;
165 static int transform_error = 0;
167 static const char *
168 do_transform_source (FileOpContext *ctx, const char *source)
170 size_t j, k, l, len;
171 const char *fnsource = x_basename (source);
172 int next_reg;
173 enum CaseConvs case_conv = NO_CONV;
174 static char fntarget[MC_MAXPATHLEN];
176 len = strlen (fnsource);
177 j = re_match (&ctx->rx, fnsource, len, 0, &ctx->regs);
178 if (j != len) {
179 transform_error = FILE_SKIP;
180 return NULL;
182 for (next_reg = 1, j = 0, k = 0; j < strlen (ctx->dest_mask); j++) {
183 switch (ctx->dest_mask[j]) {
184 case '\\':
185 if (mhl_shell_is_char_escaped (&ctx->dest_mask[j])){
186 fntarget[k++] = ctx->dest_mask[j++];
187 fntarget[k++] = ctx->dest_mask[j];
188 break;
189 } else {
190 j++;
191 if (!isdigit ((unsigned char) ctx->dest_mask[j])) {
192 /* Backslash followed by non-digit */
193 switch (ctx->dest_mask[j]) {
194 case 'U':
195 case_conv |= UP_SECT;
196 case_conv &= ~LOW_SECT;
197 break;
198 case 'u':
199 case_conv |= UP_CHAR;
200 break;
201 case 'L':
202 case_conv |= LOW_SECT;
203 case_conv &= ~UP_SECT;
204 break;
205 case 'l':
206 case_conv |= LOW_CHAR;
207 break;
208 case 'E':
209 case_conv = NO_CONV;
210 break;
211 default:
212 /* Backslash as quote mark */
213 fntarget[k++] =
214 convert_case (ctx->dest_mask[j], &case_conv);
216 break;
217 } else {
218 /* Backslash followed by digit */
219 next_reg = ctx->dest_mask[j] - '0';
220 /* Fall through */
224 case '*':
225 if (next_reg < 0 || next_reg >= RE_NREGS
226 || ctx->regs.start[next_reg] < 0) {
227 message (D_ERROR, MSG_ERROR, _(" Invalid target mask "));
228 transform_error = FILE_ABORT;
229 return NULL;
231 for (l = (size_t) ctx->regs.start[next_reg];
232 l < (size_t) ctx->regs.end[next_reg]; l++)
233 fntarget[k++] = convert_case (fnsource[l], &case_conv);
234 next_reg++;
235 break;
237 default:
238 fntarget[k++] = convert_case (ctx->dest_mask[j], &case_conv);
239 break;
242 fntarget[k] = 0;
243 return fntarget;
246 static const char *
247 transform_source (FileOpContext *ctx, const char *source)
249 char *s = g_strdup (source);
250 char *q;
251 const char *p;
253 /* We remove \n from the filename since regex routines would use \n as an anchor */
254 /* this is just to be allowed to maniupulate file names with \n on it */
255 for (q = s; *q; q++) {
256 if (*q == '\n')
257 *q = ' ';
259 p = do_transform_source (ctx, s);
260 g_free (s);
261 return p;
264 static void
265 free_linklist (struct link **linklist)
267 struct link *lp, *lp2;
269 for (lp = *linklist; lp != NULL; lp = lp2) {
270 lp2 = lp->next;
271 g_free (lp);
273 *linklist = NULL;
276 static int
277 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
279 ino_t ino = sb->st_ino;
280 dev_t dev = sb->st_dev;
281 #ifdef USE_VFS
282 struct vfs_class *vfs = vfs_get_class (path);
283 #endif /* USE_VFS */
285 while (lp) {
286 #ifdef USE_VFS
287 if (lp->vfs == vfs)
288 #endif /* USE_VFS */
289 if (lp->ino == ino && lp->dev == dev)
290 return 1;
291 lp = lp->next;
293 return 0;
297 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
298 * and a hardlink was succesfully made
300 static int
301 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
303 struct link *lp;
304 struct vfs_class *my_vfs = vfs_get_class (src_name);
305 ino_t ino = pstat->st_ino;
306 dev_t dev = pstat->st_dev;
307 struct stat link_stat;
308 const char *p;
310 if (vfs_file_class_flags (src_name) & VFSF_NOLINKS)
311 return 0;
313 for (lp = linklist; lp != NULL; lp = lp->next)
314 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev) {
315 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
316 && link_stat.st_dev == dev
317 && vfs_get_class (lp->name) == my_vfs) {
318 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
319 was copied to */
320 if (vfs_get_class (dst_name) == vfs_get_class (p)) {
321 if (!mc_stat (p, &link_stat)) {
322 if (!mc_link (p, dst_name))
323 return 1;
327 message (D_ERROR, MSG_ERROR, _(" Cannot make the hardlink "));
328 return 0;
330 lp = (struct link *) g_malloc (sizeof (struct link) + strlen (src_name)
331 + strlen (dst_name) + 1);
332 if (lp) {
333 char *lpdstname;
334 lp->vfs = my_vfs;
335 lp->ino = ino;
336 lp->dev = dev;
337 strcpy (lp->name, src_name);
338 lpdstname = lp->name + strlen(lp->name) + 1;
339 strcpy (lpdstname, dst_name);
340 lp->next = linklist;
341 linklist = lp;
343 return 0;
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).
353 static int
354 make_symlink (FileOpContext *ctx, const char *src_path, const char *dst_path)
356 char link_target[MC_MAXPATHLEN];
357 int len;
358 int return_status;
359 struct stat sb;
360 int dst_is_symlink;
362 if (mc_lstat (dst_path, &sb) == 0 && S_ISLNK (sb.st_mode))
363 dst_is_symlink = 1;
364 else
365 dst_is_symlink = 0;
367 retry_src_readlink:
368 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
369 if (len < 0) {
370 return_status =
371 file_error (_(" Cannot read source link \"%s\" \n %s "),
372 src_path);
373 if (return_status == FILE_RETRY)
374 goto retry_src_readlink;
375 return return_status;
377 link_target[len] = 0;
379 if (ctx->stable_symlinks)
380 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path)) {
381 message (D_ERROR, MSG_ERROR,
382 _(" Cannot make stable symlinks across "
383 "non-local filesystems: \n\n"
384 " Option Stable Symlinks will be disabled "));
385 ctx->stable_symlinks = 0;
388 if (ctx->stable_symlinks && *link_target != PATH_SEP) {
389 char *p, *q, *s;
391 const char *r = strrchr (src_path, PATH_SEP);
393 if (r) {
394 p = g_strndup (src_path, r - src_path + 1);
395 if (*dst_path == PATH_SEP)
396 q = g_strdup (dst_path);
397 else
398 q = g_strconcat (p, dst_path, (char *) NULL);
399 s = strrchr (q, PATH_SEP);
400 if (s) {
401 s[1] = 0;
402 s = g_strconcat (p, link_target, (char *) NULL);
403 g_free (p);
404 g_strlcpy (link_target, s, sizeof (link_target));
405 g_free (s);
406 s = diff_two_paths (q, link_target);
407 if (s) {
408 g_strlcpy (link_target, s, sizeof (link_target));
409 g_free (s);
411 } else
412 g_free (p);
413 g_free (q);
416 retry_dst_symlink:
417 if (mc_symlink (link_target, dst_path) == 0)
418 /* Success */
419 return FILE_CONT;
421 * if dst_exists, it is obvious that this had failed.
422 * We can delete the old symlink and try again...
424 if (dst_is_symlink) {
425 if (!mc_unlink (dst_path))
426 if (mc_symlink (link_target, dst_path) == 0)
427 /* Success */
428 return FILE_CONT;
430 return_status =
431 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
432 dst_path);
433 if (return_status == FILE_RETRY)
434 goto retry_dst_symlink;
435 return return_status;
438 static int
439 progress_update_one (FileOpContext *ctx,
440 off_t *progress_count,
441 double *progress_bytes, off_t add, int is_toplevel_file)
443 int ret;
445 if (is_toplevel_file || ctx->progress_totals_computed) {
446 (*progress_count)++;
447 (*progress_bytes) += add;
450 /* Apply some heuristic here to not call the update stuff very often */
451 ret =
452 file_progress_show_count (ctx, *progress_count,
453 ctx->progress_count);
454 if (ret != FILE_CONT)
455 return ret;
456 ret =
457 file_progress_show_bytes (ctx, *progress_bytes,
458 ctx->progress_bytes);
460 return ret;
463 /* Status of the destination file */
464 enum {
465 DEST_NONE, /* Not created */
466 DEST_SHORT, /* Created, not fully copied */
467 DEST_FULL /* Created, fully copied */
471 copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path,
472 int ask_overwrite, off_t *progress_count,
473 double *progress_bytes, int is_toplevel_file)
475 uid_t src_uid = (uid_t) - 1;
476 gid_t src_gid = (gid_t) - 1;
478 char *buf = NULL;
479 int buf_size = BUF_8K;
480 int src_desc, dest_desc = -1;
481 int n_read, n_written;
482 mode_t src_mode = 0; /* The mode of the source file */
483 struct stat sb, sb2;
484 struct utimbuf utb;
485 int dst_exists = 0, appending = 0;
486 off_t n_read_total = 0, file_size = -1;
487 int return_status, temp_status;
488 struct timeval tv_transfer_start;
489 int dst_status = DEST_NONE; /* 1 if the file is not fully copied */
491 /* FIXME: We should not be using global variables! */
492 ctx->do_reget = 0;
493 return_status = FILE_RETRY;
495 if (file_progress_show_source (ctx, src_path) == FILE_ABORT ||
496 file_progress_show_target (ctx, dst_path) == FILE_ABORT)
497 return FILE_ABORT;
499 mc_refresh ();
501 while (mc_stat (dst_path, &sb2) == 0) {
502 if (S_ISDIR (sb2.st_mode)) {
503 return_status =
504 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
505 dst_path);
506 if (return_status == FILE_RETRY)
507 continue;
508 return return_status;
510 dst_exists = 1;
511 break;
514 while ((*ctx->stat_func) (src_path, &sb)) {
515 return_status =
516 file_error (_(" Cannot stat source file \"%s\" \n %s "),
517 src_path);
518 if (return_status != FILE_RETRY)
519 return return_status;
522 if (dst_exists) {
523 /* Destination already exists */
524 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino) {
525 message (D_ERROR, MSG_ERROR,
526 _(" `%s' and `%s' are the same file "), src_path, dst_path);
527 do_refresh ();
528 return FILE_SKIP;
531 /* Should we replace destination? */
532 if (ask_overwrite) {
533 ctx->do_reget = 0;
534 return_status = query_replace (ctx, dst_path, &sb, &sb2);
535 if (return_status != FILE_CONT)
536 return return_status;
540 if (!ctx->do_append) {
541 /* Check the hardlinks */
542 if (!ctx->follow_links && sb.st_nlink > 1 &&
543 check_hardlinks (src_path, dst_path, &sb) == 1) {
544 /* We have made a hardlink - no more processing is necessary */
545 return FILE_CONT;
548 if (S_ISLNK (sb.st_mode)) {
549 int retval;
551 retval = make_symlink (ctx, src_path, dst_path);
552 return retval;
555 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
556 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) ||
557 S_ISSOCK (sb.st_mode)) {
558 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill,
559 sb.st_rdev) < 0) {
560 return_status = file_error (
561 _(" Cannot create special file \"%s\" \n %s "), dst_path);
562 if (return_status == FILE_RETRY)
563 continue;
564 return return_status;
566 /* Success */
568 while (ctx->preserve_uidgid
569 && mc_chown (dst_path, sb.st_uid, sb.st_gid)) {
570 temp_status = file_error (
571 _(" Cannot chown target file \"%s\" \n %s "), dst_path);
572 if (temp_status == FILE_RETRY)
573 continue;
574 return temp_status;
576 while (ctx->preserve &&
577 mc_chmod (dst_path, sb.st_mode & ctx->umask_kill)) {
578 temp_status = file_error (
579 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
580 if (temp_status == FILE_RETRY)
581 continue;
582 return temp_status;
584 return FILE_CONT;
588 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
590 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0) {
591 return_status = file_error (
592 _(" Cannot open source file \"%s\" \n %s "), src_path);
593 if (return_status == FILE_RETRY)
594 continue;
595 ctx->do_append = 0;
596 return return_status;
599 if (ctx->do_reget) {
600 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget) {
601 message (D_ERROR, _("Warning"),
602 _(" Reget failed, about to overwrite file "));
603 ctx->do_reget = ctx->do_append = 0;
607 while (mc_fstat (src_desc, &sb)) {
608 return_status = file_error (
609 _(" Cannot fstat source file \"%s\" \n %s "), src_path);
610 if (return_status == FILE_RETRY)
611 continue;
612 ctx->do_append = 0;
613 goto ret;
615 src_mode = sb.st_mode;
616 src_uid = sb.st_uid;
617 src_gid = sb.st_gid;
618 utb.actime = sb.st_atime;
619 utb.modtime = sb.st_mtime;
620 file_size = sb.st_size;
622 /* Create the new regular file with small permissions initially,
623 do not create a security hole. FIXME: You have security hole
624 here, btw. Imagine copying to /tmp and symlink attack :-( */
626 while ((dest_desc = mc_open (dst_path, O_WRONLY | (ctx->do_append ?
627 O_APPEND : (O_CREAT | O_TRUNC)), 0600)) < 0) {
628 return_status = file_error (
629 _(" Cannot create target file \"%s\" \n %s "), dst_path);
630 if (return_status == FILE_RETRY)
631 continue;
632 ctx->do_append = 0;
633 goto ret;
635 dst_status = DEST_SHORT; /* file opened, but not fully copied */
637 appending = ctx->do_append;
638 ctx->do_append = 0;
640 /* Find out the optimal buffer size. */
641 while (mc_fstat (dest_desc, &sb)) {
642 return_status = file_error (
643 _(" Cannot fstat target file \"%s\" \n %s "), dst_path);
644 if (return_status == FILE_RETRY)
645 continue;
646 goto ret;
648 buf = g_malloc (buf_size);
650 ctx->eta_secs = 0.0;
651 ctx->bps = 0;
653 return_status = file_progress_show (ctx, 0, file_size);
655 mc_refresh ();
657 if (return_status != FILE_CONT)
658 goto ret;
661 struct timeval tv_current, tv_last_update, tv_last_input;
662 int secs, update_secs;
663 long dt;
664 const char *stalled_msg;
666 tv_last_update = tv_transfer_start;
668 for (;;) {
669 /* src_read */
670 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
671 n_read = -1;
672 else
673 while ((n_read = mc_read (src_desc, buf, buf_size)) < 0) {
674 return_status = file_error (
675 _(" Cannot read source file \"%s\" \n %s "), src_path);
676 if (return_status == FILE_RETRY)
677 continue;
678 goto ret;
680 if (n_read == 0)
681 break;
683 gettimeofday (&tv_current, NULL);
685 if (n_read > 0) {
686 char *t = buf;
687 n_read_total += n_read;
689 /* Windows NT ftp servers report that files have no
690 * permissions: -------, so if we happen to have actually
691 * read something, we should fix the permissions.
693 if (!(src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)))
694 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
695 gettimeofday (&tv_last_input, NULL);
697 /* dst_write */
698 while ((n_written =
699 mc_write (dest_desc, t, n_read)) < n_read) {
700 if (n_written > 0) {
701 n_read -= n_written;
702 t += n_written;
703 continue;
705 return_status =
706 file_error (_(" Cannot write target file \"%s\" \n %s "),
707 dst_path);
708 if (return_status != FILE_RETRY)
709 goto ret;
713 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
714 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
715 if (secs > 2) {
716 rotate_dash ();
717 tv_last_update = tv_current;
720 /* 2. Check for a stalled condition */
721 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
722 stalled_msg = "";
723 if (update_secs > 4) {
724 stalled_msg = _("(stalled)");
727 /* 3. Compute ETA */
728 if (secs > 2) {
729 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
731 if (n_read_total) {
732 ctx->eta_secs =
733 ((dt / (double) n_read_total) * file_size) - dt;
734 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
735 } else
736 ctx->eta_secs = 0.0;
739 /* 4. Compute BPS rate */
740 if (secs > 2) {
741 ctx->bps_time =
742 (tv_current.tv_sec - tv_transfer_start.tv_sec);
743 if (ctx->bps_time < 1)
744 ctx->bps_time = 1;
745 ctx->bps = n_read_total / ctx->bps_time;
748 file_progress_set_stalled_label (ctx, stalled_msg);
749 return_status = file_progress_show_bytes (ctx, *progress_bytes +
750 n_read_total + ctx->do_reget, ctx->progress_bytes);
751 if (return_status == FILE_CONT) {
752 return_status =
753 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size);
755 mc_refresh ();
756 if (return_status != FILE_CONT)
757 goto ret;
761 dst_status = DEST_FULL; /* copy successful, don't remove target file */
763 ret:
764 g_free (buf);
766 while (src_desc != -1 && mc_close (src_desc) < 0) {
767 temp_status = file_error (
768 _(" Cannot close source file \"%s\" \n %s "), src_path);
769 if (temp_status == FILE_RETRY)
770 continue;
771 if (temp_status == FILE_ABORT)
772 return_status = temp_status;
773 break;
776 while (dest_desc != -1 && mc_close (dest_desc) < 0) {
777 temp_status = file_error (
778 _(" Cannot close target file \"%s\" \n %s "), dst_path);
779 if (temp_status == FILE_RETRY)
780 continue;
781 return_status = temp_status;
782 break;
785 if (dst_status == DEST_SHORT) {
786 /* Remove short file */
787 int result;
788 result = query_dialog (_("Copy"),
789 _("Incomplete file was retrieved. Keep it?"),
790 D_ERROR, 2, _("&Delete"), _("&Keep"));
791 if (!result)
792 mc_unlink (dst_path);
793 } else if (dst_status == DEST_FULL) {
794 /* Copy has succeeded */
795 if (!appending && ctx->preserve_uidgid) {
796 while (mc_chown (dst_path, src_uid, src_gid)) {
797 temp_status = file_error (
798 _(" Cannot chown target file \"%s\" \n %s "), dst_path);
799 if (temp_status == FILE_RETRY)
800 continue;
801 return_status = temp_status;
802 break;
806 if (!appending && ctx->preserve) {
807 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill))) {
808 temp_status = file_error (
809 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
810 if (temp_status != FILE_RETRY) {
811 return_status = temp_status;
812 break;
815 mc_utime (dst_path, &utb);
819 if (return_status == FILE_CONT)
820 return_status =
821 progress_update_one (ctx, progress_count, progress_bytes,
822 file_size, is_toplevel_file);
824 return return_status;
828 * I think these copy_*_* functions should have a return type.
829 * anyway, this function *must* have two directories as arguments.
831 /* FIXME: This function needs to check the return values of the
832 function calls */
834 copy_dir_dir (FileOpContext *ctx, const char *s, const char *d, int toplevel,
835 int move_over, int delete, struct link *parent_dirs,
836 off_t *progress_count, double *progress_bytes)
838 struct dirent *next;
839 struct stat buf, cbuf;
840 DIR *reading;
841 char *path, *mdpath, *dest_file, *dest_dir;
842 int return_status = FILE_CONT;
843 struct utimbuf utb;
844 struct link *lp;
846 /* First get the mode of the source dir */
847 retry_src_stat:
848 if ((*ctx->stat_func) (s, &cbuf)) {
849 return_status =
850 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s);
851 if (return_status == FILE_RETRY)
852 goto retry_src_stat;
853 return return_status;
856 if (is_in_linklist (dest_dirs, s, &cbuf)) {
857 /* Don't copy a directory we created before (we don't want to copy
858 infinitely if a directory is copied into itself) */
859 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
860 return FILE_CONT;
863 /* Hmm, hardlink to directory??? - Norbert */
864 /* FIXME: In this step we should do something
865 in case the destination already exist */
866 /* Check the hardlinks */
867 if (ctx->preserve && cbuf.st_nlink > 1
868 && check_hardlinks (s, d, &cbuf) == 1) {
869 /* We have made a hardlink - no more processing is necessary */
870 return return_status;
873 if (!S_ISDIR (cbuf.st_mode)) {
874 return_status =
875 file_error (_(" Source \"%s\" is not a directory \n %s "), s);
876 if (return_status == FILE_RETRY)
877 goto retry_src_stat;
878 return return_status;
881 if (is_in_linklist (parent_dirs, s, &cbuf)) {
882 /* we found a cyclic symbolic link */
883 message (D_ERROR, MSG_ERROR,
884 _(" Cannot copy cyclic symbolic link \n `%s' "), s);
885 return FILE_SKIP;
888 lp = g_new (struct link, 1);
889 lp->vfs = vfs_get_class (s);
890 lp->ino = cbuf.st_ino;
891 lp->dev = cbuf.st_dev;
892 lp->next = parent_dirs;
893 parent_dirs = lp;
895 retry_dst_stat:
896 /* Now, check if the dest dir exists, if not, create it. */
897 if (mc_stat (d, &buf)) {
898 /* Here the dir doesn't exist : make it ! */
900 if (move_over) {
901 if (mc_rename (s, d) == 0) {
902 g_free (parent_dirs);
903 return FILE_CONT;
906 dest_dir = g_strdup (d);
907 } else {
909 * If the destination directory exists, we want to copy the whole
910 * directory, but we only want this to happen once.
912 * Escape sequences added to the * to compiler warnings.
913 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
914 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
916 if (!S_ISDIR (buf.st_mode)) {
917 return_status = file_error(
918 _(" Destination \"%s\" must be a directory \n %s "), d);
919 if (return_status == FILE_RETRY)
920 goto retry_dst_stat;
921 g_free (parent_dirs);
922 return return_status;
924 /* Dive into subdir if exists */
925 if (toplevel && ctx->dive_into_subdirs) {
926 dest_dir = mhl_str_dir_plus_file (d, x_basename (s));
927 } else {
928 dest_dir = g_strdup (d);
929 goto dont_mkdir;
932 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU)) {
933 return_status = file_error (
934 _(" Cannot create target directory \"%s\" \n %s "), dest_dir);
935 if (return_status != FILE_RETRY)
936 goto ret;
939 lp = g_new (struct link, 1);
940 mc_stat (dest_dir, &buf);
941 lp->vfs = vfs_get_class (dest_dir);
942 lp->ino = buf.st_ino;
943 lp->dev = buf.st_dev;
944 lp->next = dest_dirs;
945 dest_dirs = lp;
947 if (ctx->preserve_uidgid) {
948 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid)) {
949 return_status = file_error (
950 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir);
951 if (return_status != FILE_RETRY)
952 goto ret;
956 dont_mkdir:
957 /* open the source dir for reading */
958 if ((reading = mc_opendir (s)) == 0) {
959 goto ret;
962 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT) {
964 * Now, we don't want '.' and '..' to be created / copied at any time
966 if (!strcmp (next->d_name, "."))
967 continue;
968 if (!strcmp (next->d_name, ".."))
969 continue;
971 /* get the filename and add it to the src directory */
972 path = mhl_str_dir_plus_file (s, next->d_name);
974 (*ctx->stat_func) (path, &buf);
975 if (S_ISDIR (buf.st_mode)) {
976 mdpath = mhl_str_dir_plus_file (dest_dir, next->d_name);
978 * From here, we just intend to recursively copy subdirs, not
979 * the double functionality of copying different when the target
980 * dir already exists. So, we give the recursive call the flag 0
981 * meaning no toplevel.
983 return_status = copy_dir_dir (ctx, path, mdpath, 0, 0, delete,
984 parent_dirs, progress_count, progress_bytes);
985 g_free (mdpath);
986 } else {
987 dest_file = mhl_str_dir_plus_file (dest_dir, x_basename (path));
988 return_status = copy_file_file (ctx, path, dest_file, 1,
989 progress_count, progress_bytes, 0);
990 g_free (dest_file);
992 if (delete && return_status == FILE_CONT) {
993 if (ctx->erase_at_end) {
994 static struct link *tail;
995 lp = g_malloc (sizeof (struct link) + strlen (path));
996 strcpy (lp->name, path);
997 lp->st_mode = buf.st_mode;
998 lp->next = 0;
999 if (erase_list) {
1000 tail->next = lp;
1001 tail = lp;
1002 } else
1003 erase_list = tail = lp;
1004 } else {
1005 if (S_ISDIR (buf.st_mode)) {
1006 return_status = erase_dir_iff_empty (ctx, path);
1007 } else
1008 return_status = erase_file (ctx, path, 0, 0, 0);
1011 g_free (path);
1013 mc_closedir (reading);
1015 if (ctx->preserve) {
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);
1022 ret:
1023 g_free (dest_dir);
1024 g_free (parent_dirs);
1025 return return_status;
1028 /* }}} */
1030 /* {{{ Move routines */
1032 static int
1033 move_file_file (FileOpContext *ctx, const char *s, const char *d,
1034 off_t *progress_count, double *progress_bytes)
1036 struct stat src_stats, dst_stats;
1037 int return_status = FILE_CONT;
1038 bool copy_done = FALSE;
1040 if (file_progress_show_source (ctx, s) == FILE_ABORT
1041 || file_progress_show_target (ctx, d) == FILE_ABORT)
1042 return FILE_ABORT;
1044 mc_refresh ();
1046 while (mc_lstat (s, &src_stats) != 0) {
1047 /* Source doesn't exist */
1048 return_status =
1049 file_error (_(" Cannot stat file \"%s\" \n %s "), s);
1050 if (return_status != FILE_RETRY)
1051 return return_status;
1054 if (mc_lstat (d, &dst_stats) == 0) {
1055 if (src_stats.st_dev == dst_stats.st_dev
1056 && src_stats.st_ino == dst_stats.st_ino) {
1057 int msize = COLS - 36;
1058 char st[MC_MAXPATHLEN];
1059 char dt[MC_MAXPATHLEN];
1061 if (msize < 0)
1062 msize = 40;
1063 msize /= 2;
1065 strcpy (st, path_trunc (s, msize));
1066 strcpy (dt, path_trunc (d, msize));
1067 message (D_ERROR, MSG_ERROR,
1068 _(" `%s' and `%s' are the same file "), st, dt);
1069 do_refresh ();
1070 return FILE_SKIP;
1073 if (S_ISDIR (dst_stats.st_mode)) {
1074 message (D_ERROR, MSG_ERROR,
1075 _(" Cannot overwrite directory `%s' "), d);
1076 do_refresh ();
1077 return FILE_SKIP;
1080 if (confirm_overwrite) {
1081 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1082 if (return_status != FILE_CONT)
1083 return return_status;
1085 /* Ok to overwrite */
1088 if (!ctx->do_append) {
1089 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks) {
1090 if ((return_status = make_symlink (ctx, s, d)) == FILE_CONT) {
1091 goto retry_src_remove;
1092 } else
1093 return return_status;
1096 if (mc_rename (s, d) == 0) {
1097 return progress_update_one (ctx, progress_count,
1098 progress_bytes,
1099 src_stats.st_size, 1);
1102 #if 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. */
1107 else
1108 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1110 if (errno != EXDEV) {
1111 return_status =
1112 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s,
1114 if (return_status == FILE_RETRY)
1115 goto retry_rename;
1116 return return_status;
1118 #endif
1120 /* Failed because filesystem boundary -> copy the file instead */
1121 return_status =
1122 copy_file_file (ctx, s, d, 0, progress_count, progress_bytes, 1);
1123 if (return_status != FILE_CONT)
1124 return return_status;
1126 copy_done = TRUE;
1128 if ((return_status =
1129 file_progress_show_source (ctx, NULL)) != FILE_CONT
1130 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1131 return return_status;
1133 mc_refresh ();
1135 retry_src_remove:
1136 if (mc_unlink (s)) {
1137 return_status =
1138 file_error (_(" Cannot remove file \"%s\" \n %s "), s);
1139 if (return_status == FILE_RETRY)
1140 goto retry_src_remove;
1141 return return_status;
1144 if (!copy_done) {
1145 return_status = progress_update_one (ctx,
1146 progress_count,
1147 progress_bytes,
1148 src_stats.st_size, 1);
1151 return return_status;
1155 move_dir_dir (FileOpContext *ctx, const char *s, const char *d,
1156 off_t *progress_count, double *progress_bytes)
1158 struct stat sbuf, dbuf, destbuf;
1159 struct link *lp;
1160 char *destdir;
1161 int return_status;
1162 int move_over = 0;
1164 if (file_progress_show_source (ctx, s) == FILE_ABORT ||
1165 file_progress_show_target (ctx, d) == FILE_ABORT)
1166 return FILE_ABORT;
1168 mc_refresh ();
1170 mc_stat (s, &sbuf);
1171 if (mc_stat (d, &dbuf))
1172 destdir = g_strdup (d); /* destination doesn't exist */
1173 else if (!ctx->dive_into_subdirs) {
1174 destdir = g_strdup (d);
1175 move_over = 1;
1176 } else
1177 destdir = mhl_str_dir_plus_file (d, x_basename (s));
1179 if (sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino) {
1180 int msize = COLS - 36;
1181 char st[MC_MAXPATHLEN];
1182 char dt[MC_MAXPATHLEN];
1184 if (msize < 0)
1185 msize = 40;
1186 msize /= 2;
1188 strcpy (st, path_trunc (s, msize));
1189 strcpy (dt, path_trunc (d, msize));
1190 message (D_ERROR, MSG_ERROR,
1191 _(" `%s' and `%s' are the same directory "), st, dt);
1192 do_refresh ();
1193 return FILE_SKIP;
1196 /* Check if the user inputted an existing dir */
1197 retry_dst_stat:
1198 if (!mc_stat (destdir, &destbuf)) {
1199 if (move_over) {
1200 return_status = copy_dir_dir (ctx, s, destdir, 0, 1, 1, 0,
1201 progress_count, progress_bytes);
1203 if (return_status != FILE_CONT)
1204 goto ret;
1205 goto oktoret;
1206 } else {
1207 if (S_ISDIR (destbuf.st_mode))
1208 return_status =
1209 file_error (_
1210 (" Cannot overwrite directory \"%s\" %s "),
1211 destdir);
1212 else
1213 return_status =
1214 file_error (_(" Cannot overwrite file \"%s\" %s "),
1215 destdir);
1216 if (return_status == FILE_RETRY)
1217 goto retry_dst_stat;
1219 g_free (destdir);
1220 return return_status;
1223 retry_rename:
1224 if (mc_rename (s, destdir) == 0) {
1225 return_status = FILE_CONT;
1226 goto ret;
1229 if (errno != EXDEV) {
1230 return_status =
1231 files_error (_
1232 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1233 s, d);
1234 if (return_status == FILE_RETRY)
1235 goto retry_rename;
1236 goto ret;
1238 /* Failed because of filesystem boundary -> copy dir instead */
1239 return_status =
1240 copy_dir_dir (ctx, s, destdir, 0, 0, 1, 0, progress_count,
1241 progress_bytes);
1243 if (return_status != FILE_CONT)
1244 goto ret;
1245 oktoret:
1246 if ((return_status =
1247 file_progress_show_source (ctx, NULL)) != FILE_CONT
1248 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1249 goto ret;
1251 mc_refresh ();
1252 if (ctx->erase_at_end) {
1253 for (; erase_list && return_status != FILE_ABORT;) {
1254 if (S_ISDIR (erase_list->st_mode)) {
1255 return_status =
1256 erase_dir_iff_empty (ctx, erase_list->name);
1257 } else
1258 return_status =
1259 erase_file (ctx, erase_list->name, 0, 0, 0);
1260 lp = erase_list;
1261 erase_list = erase_list->next;
1262 g_free (lp);
1265 erase_dir_iff_empty (ctx, s);
1267 ret:
1268 g_free (destdir);
1269 while (erase_list) {
1270 lp = erase_list;
1271 erase_list = erase_list->next;
1272 g_free (lp);
1274 return return_status;
1277 /* }}} */
1279 /* {{{ Erase routines */
1280 /* Don't update progress status if progress_count==NULL */
1281 static int
1282 erase_file (FileOpContext *ctx, const char *s, off_t *progress_count,
1283 double *progress_bytes, int is_toplevel_file)
1285 int return_status;
1286 struct stat buf;
1288 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1289 return FILE_ABORT;
1290 mc_refresh ();
1292 if (progress_count && mc_lstat (s, &buf)) {
1293 /* ignore, most likely the mc_unlink fails, too */
1294 buf.st_size = 0;
1297 while (mc_unlink (s)) {
1298 return_status =
1299 file_error (_(" Cannot delete file \"%s\" \n %s "), s);
1300 if (return_status != FILE_RETRY)
1301 return return_status;
1304 if (progress_count)
1305 return progress_update_one (ctx, progress_count, progress_bytes,
1306 buf.st_size, is_toplevel_file);
1307 else
1308 return FILE_CONT;
1311 static int
1312 recursive_erase (FileOpContext *ctx, const char *s, off_t *progress_count,
1313 double *progress_bytes)
1315 struct dirent *next;
1316 struct stat buf;
1317 DIR *reading;
1318 char *path;
1319 int return_status = FILE_CONT;
1321 if (!strcmp (s, ".."))
1322 return 1;
1324 reading = mc_opendir (s);
1326 if (!reading)
1327 return 1;
1329 while ((next = mc_readdir (reading)) && return_status == FILE_CONT) {
1330 if (!strcmp (next->d_name, "."))
1331 continue;
1332 if (!strcmp (next->d_name, ".."))
1333 continue;
1334 path = mhl_str_dir_plus_file (s, next->d_name);
1335 if (mc_lstat (path, &buf)) {
1336 g_free (path);
1337 mc_closedir (reading);
1338 return 1;
1340 if (S_ISDIR (buf.st_mode))
1341 return_status =
1342 (recursive_erase
1343 (ctx, path, progress_count, progress_bytes)
1344 != FILE_CONT);
1345 else
1346 return_status =
1347 erase_file (ctx, path, progress_count, progress_bytes, 0);
1348 g_free (path);
1350 mc_closedir (reading);
1351 if (return_status != FILE_CONT)
1352 return return_status;
1353 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1354 return FILE_ABORT;
1355 mc_refresh ();
1357 while (my_rmdir (s)) {
1358 return_status =
1359 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1360 if (return_status != FILE_RETRY)
1361 return return_status;
1364 return FILE_CONT;
1367 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1368 in the directory path points to, 0 else. */
1369 static int
1370 check_dir_is_empty (const char *path)
1372 DIR *dir;
1373 struct dirent *d;
1374 int i;
1376 dir = mc_opendir (path);
1377 if (!dir)
1378 return -1;
1380 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir)) {
1381 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1382 (d->d_name[1] == '.'
1383 && d->d_name[2] == '\0')))
1384 continue; /* "." or ".." */
1385 i = 0;
1386 break;
1389 mc_closedir (dir);
1390 return i;
1394 erase_dir (FileOpContext *ctx, const char *s, off_t *progress_count,
1395 double *progress_bytes)
1397 int error;
1399 if (strcmp (s, "..") == 0)
1400 return FILE_SKIP;
1402 if (strcmp (s, ".") == 0)
1403 return FILE_SKIP;
1405 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1406 return FILE_ABORT;
1407 mc_refresh ();
1409 /* The old way to detect a non empty directory was:
1410 error = my_rmdir (s);
1411 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1412 For the linux user space nfs server (nfs-server-2.2beta29-2)
1413 we would have to check also for EIO. I hope the new way is
1414 fool proof. (Norbert)
1416 error = check_dir_is_empty (s);
1417 if (error == 0) { /* not empty */
1418 error = query_recursive (ctx, s);
1419 if (error == FILE_CONT)
1420 return recursive_erase (ctx, s, progress_count,
1421 progress_bytes);
1422 else
1423 return error;
1426 while (my_rmdir (s) == -1) {
1427 error =
1428 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1429 if (error != FILE_RETRY)
1430 return error;
1433 return FILE_CONT;
1436 static int
1437 erase_dir_iff_empty (FileOpContext *ctx, const char *s)
1439 int error;
1441 if (strcmp (s, "..") == 0)
1442 return FILE_SKIP;
1444 if (strcmp (s, ".") == 0)
1445 return FILE_SKIP;
1447 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1448 return FILE_ABORT;
1449 mc_refresh ();
1451 if (1 != check_dir_is_empty (s)) /* not empty or error */
1452 return FILE_CONT;
1454 while (my_rmdir (s)) {
1455 error =
1456 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1457 if (error != FILE_RETRY)
1458 return error;
1461 return FILE_CONT;
1464 /* }}} */
1466 /* {{{ Panel operate routines */
1469 * Return currently selected entry name or the name of the first marked
1470 * entry if there is one.
1472 static char *
1473 panel_get_file (WPanel *panel, struct stat *stat_buf)
1475 int i;
1477 if (get_current_type () == view_tree) {
1478 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
1479 char *tree_name = tree_selected_name (tree);
1481 mc_stat (tree_name, stat_buf);
1482 return tree_name;
1485 if (panel->marked) {
1486 for (i = 0; i < panel->count; i++)
1487 if (panel->dir.list[i].f.marked) {
1488 *stat_buf = panel->dir.list[i].st;
1489 return panel->dir.list[i].fname;
1491 } else {
1492 *stat_buf = panel->dir.list[panel->selected].st;
1493 return panel->dir.list[panel->selected].fname;
1495 g_assert_not_reached ();
1496 return NULL;
1500 * compute_dir_size:
1502 * Computes the number of bytes used by the files in a directory
1504 void
1505 compute_dir_size (const char *dirname, off_t *ret_marked, double *ret_total)
1507 DIR *dir;
1508 struct dirent *dirent;
1510 dir = mc_opendir (dirname);
1512 if (!dir)
1513 return;
1515 while ((dirent = mc_readdir (dir)) != NULL) {
1516 struct stat s;
1517 char *fullname;
1518 int res;
1520 if (strcmp (dirent->d_name, ".") == 0)
1521 continue;
1522 if (strcmp (dirent->d_name, "..") == 0)
1523 continue;
1525 fullname = mhl_str_dir_plus_file (dirname, dirent->d_name);
1527 res = mc_lstat (fullname, &s);
1529 if (res != 0) {
1530 g_free (fullname);
1531 continue;
1534 if (S_ISDIR (s.st_mode)) {
1535 off_t subdir_count = 0;
1536 double subdir_bytes = 0;
1538 compute_dir_size (fullname, &subdir_count, &subdir_bytes);
1540 *ret_marked += subdir_count;
1541 *ret_total += subdir_bytes;
1542 } else {
1543 (*ret_marked)++;
1544 *ret_total += s.st_size;
1546 g_free (fullname);
1549 mc_closedir (dir);
1553 * panel_compute_totals:
1555 * compute the number of files and the number of bytes
1556 * used up by the whole selection, recursing directories
1557 * as required. In addition, it checks to see if it will
1558 * overwrite any files by doing the copy.
1560 static void
1561 panel_compute_totals (WPanel *panel, off_t *ret_marked, double *ret_total)
1563 int i;
1565 *ret_marked = 0;
1566 *ret_total = 0.0;
1568 for (i = 0; i < panel->count; i++) {
1569 struct stat *s;
1571 if (!panel->dir.list[i].f.marked)
1572 continue;
1574 s = &panel->dir.list[i].st;
1576 if (S_ISDIR (s->st_mode)) {
1577 char *dir_name;
1578 off_t subdir_count = 0;
1579 double subdir_bytes = 0;
1581 dir_name =
1582 mhl_str_dir_plus_file (panel->cwd, panel->dir.list[i].fname);
1583 compute_dir_size (dir_name, &subdir_count, &subdir_bytes);
1585 *ret_marked += subdir_count;
1586 *ret_total += subdir_bytes;
1587 g_free (dir_name);
1588 } else {
1589 (*ret_marked)++;
1590 *ret_total += s->st_size;
1596 * This array introduced to avoid translation problems. The former (op_names)
1597 * is assumed to be nouns, suitable in dialog box titles; this one should
1598 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1599 * Notice first symbol - it is to fool gettext and force these strings to
1600 * be different for it. First symbol is skipped while building a prompt.
1601 * (I don't use spaces around the words, because someday they could be
1602 * dropped, when widgets get smarter)
1604 static const char *op_names1[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1605 #define FMD_XLEN 64
1607 int fmd_xlen = FMD_XLEN;
1610 * These are formats for building a prompt. Parts encoded as follows:
1611 * %o - operation from op_names1
1612 * %f - file/files or files/directories, as appropriate
1613 * %m - "with source mask" or question mark for delete
1614 * %s - source name (truncated)
1615 * %d - number of marked files
1616 * %e - "to:" or question mark for delete
1618 * xgettext:no-c-format */
1619 static const char *one_format = N_("%o %f \"%s\"%m");
1620 /* xgettext:no-c-format */
1621 static const char *many_format = N_("%o %d %f%m");
1622 static const char *prompt_parts[] = {
1623 N_("file"), N_("files"), N_("directory"), N_("directories"),
1624 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1628 * Generate user prompt for panel operation.
1629 * single_source is the name if the source entry or NULL for multiple
1630 * entries.
1631 * src_stat is only used when single_source is not NULL.
1633 static void
1634 panel_operate_generate_prompt (const WPanel *panel, const int operation,
1635 const char *single_source,
1636 const struct stat *src_stat)
1638 register const char *sp, *cp;
1639 register int i;
1640 char format_string[BUF_MEDIUM];
1641 char *dp = format_string;
1643 #ifdef ENABLE_NLS
1644 static int i18n_flag = 0;
1645 if (!i18n_flag) {
1646 fmd_init_i18n (FALSE); /* to get proper fmd_xlen */
1648 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1649 op_names1[i] = _(op_names1[i]);
1651 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1652 prompt_parts[i] = _(prompt_parts[i]);
1654 one_format = _(one_format);
1655 many_format = _(many_format);
1656 i18n_flag = 1;
1658 #endif /* ENABLE_NLS */
1660 sp = single_source ? one_format : many_format;
1662 while (*sp) {
1663 switch (*sp) {
1664 case '%':
1665 cp = NULL;
1666 switch (sp[1]) {
1667 case 'o':
1668 cp = op_names1[operation] + 1;
1669 break;
1670 case 'm':
1671 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1672 break;
1673 case 'e':
1674 cp = operation == OP_DELETE ? "?" : prompt_parts[6];
1675 break;
1676 case 'f':
1677 if (single_source) {
1678 cp = S_ISDIR (src_stat->
1679 st_mode) ? prompt_parts[2] :
1680 prompt_parts[0];
1681 } else {
1682 cp = (panel->marked == panel->dirs_marked)
1683 ? prompt_parts[3]
1684 : (panel->dirs_marked ? prompt_parts[4]
1685 : prompt_parts[1]);
1687 break;
1688 default:
1689 *dp++ = *sp++;
1691 if (cp) {
1692 sp += 2;
1693 while (*cp)
1694 *dp++ = *cp++;
1696 break;
1697 default:
1698 *dp++ = *sp++;
1701 *dp = '\0';
1703 if (single_source) {
1704 i = fmd_xlen - strlen (format_string) - 4;
1705 snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1706 name_trunc (single_source, i));
1707 } else {
1708 snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1709 panel->marked);
1710 i = strlen (cmd_buf) + 6 - fmd_xlen;
1711 if (i > 0) {
1712 fmd_xlen += i;
1713 fmd_init_i18n (TRUE); /* to recalculate positions of child widgets */
1719 * panel_operate:
1721 * Performs one of the operations on the selection on the source_panel
1722 * (copy, delete, move).
1724 * Returns 1 if did change the directory
1725 * structure, Returns 0 if user aborted
1727 * force_single forces operation on the current entry and affects
1728 * default destination. Current filename is used as default.
1731 panel_operate (void *source_panel, FileOperation operation,
1732 int force_single)
1734 WPanel *panel = source_panel;
1735 #ifdef WITH_FULL_PATHS
1736 char *source_with_path = NULL;
1737 #else
1738 # define source_with_path source
1739 #endif /* !WITH_FULL_PATHS */
1740 char *source = NULL;
1741 char *dest = NULL;
1742 const char *temp = NULL;
1743 char *save_cwd = NULL, *save_dest = NULL;
1744 int single_entry = (get_current_type () == view_tree)
1745 || (panel->marked <= 1) || force_single;
1746 struct stat src_stat, dst_stat;
1747 int i, value;
1748 FileOpContext *ctx;
1750 off_t count = 0;
1751 double bytes = 0;
1753 int dst_result;
1754 int do_bg = 0; /* do background operation? */
1756 free_linklist (&linklist);
1757 free_linklist (&dest_dirs);
1759 if (single_entry) {
1760 if (force_single) {
1761 source = selection (panel)->fname;
1762 src_stat = selection (panel)->st;
1763 } else {
1764 source = panel_get_file (panel, &src_stat);
1767 if (!strcmp (source, "..")) {
1768 message (D_ERROR, MSG_ERROR, _(" Cannot operate on \"..\"! "));
1769 return 0;
1773 /* Generate confirmation prompt */
1774 panel_operate_generate_prompt (panel, operation, source, &src_stat);
1776 ctx = file_op_context_new (operation);
1778 /* Show confirmation dialog */
1779 if (operation == OP_DELETE && confirm_delete) {
1780 if (safe_delete)
1781 query_set_sel (1);
1783 i = query_dialog (_(op_names[operation]), cmd_buf, D_ERROR, 2,
1784 _("&Yes"), _("&No"));
1786 if (i != 0) {
1787 file_op_context_destroy (ctx);
1788 return 0;
1790 } else if (operation != OP_DELETE) {
1791 char *dest_dir;
1792 char *dest_dir_;
1794 /* Forced single operations default to the original name */
1795 if (force_single)
1796 dest_dir = source;
1797 else if (get_other_type () == view_listing)
1798 dest_dir = other_panel->cwd;
1799 else
1800 dest_dir = panel->cwd;
1803 * Add trailing backslash only when do non-locally ops.
1804 * It saves user from occasional file renames (when destination
1805 * dir is deleted)
1807 if (force_single)
1808 /* just copy */
1809 dest_dir_ = mhl_str_dup (dest_dir);
1810 else
1811 /* add trailing separator */
1812 if (*dest_dir && strcmp(&dest_dir[strlen(dest_dir)-1], PATH_SEP_STR)) {
1813 dest_dir_ = mhl_str_concat (dest_dir, PATH_SEP_STR);
1814 } else {
1815 dest_dir_ = mhl_str_dup (dest_dir);
1817 if (!dest_dir_) {
1818 file_op_context_destroy (ctx);
1819 return 0;
1822 dest =
1823 file_mask_dialog (ctx, operation, cmd_buf, dest_dir_,
1824 single_entry, &do_bg);
1825 g_free(dest_dir_);
1827 if (!dest) {
1828 file_op_context_destroy (ctx);
1829 return 0;
1831 if (!*dest) {
1832 file_op_context_destroy (ctx);
1833 g_free (dest);
1834 return 0;
1837 #ifdef WITH_BACKGROUND
1838 /* Did the user select to do a background operation? */
1839 if (do_bg) {
1840 int v;
1842 v = do_background (ctx,
1843 g_strconcat (op_names[operation], ": ",
1844 panel->cwd, NULL));
1845 if (v == -1) {
1846 message (D_ERROR, MSG_ERROR,
1847 _(" Sorry, I could not put the job in background "));
1850 /* If we are the parent */
1851 if (v == 1) {
1852 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
1853 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
1854 /* file_op_context_destroy (ctx); */
1855 return 0;
1858 #endif /* WITH_BACKGROUND */
1860 /* Initialize things */
1861 /* We do not want to trash cache every time file is
1862 created/touched. However, this will make our cache contain
1863 invalid data. */
1864 if (dest) {
1865 if (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1))
1866 save_dest = g_strdup (dest);
1868 if (panel->cwd) {
1869 if (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1))
1870 save_cwd = g_strdup (panel->cwd);
1873 /* Now, let's do the job */
1875 if (do_bg)
1876 ctx->ui = NULL;
1877 else
1878 file_op_context_create_ui (ctx, 1);
1880 /* This code is only called by the tree and panel code */
1881 if (single_entry) {
1882 /* We now have ETA in all cases */
1884 /* One file: FIXME mc_chdir will take user out of any vfs */
1885 if (operation != OP_COPY && get_current_type () == view_tree)
1886 mc_chdir (PATH_SEP_STR);
1888 /* The source and src_stat variables have been initialized before */
1889 #ifdef WITH_FULL_PATHS
1890 source_with_path = mhl_str_dir_plus_file (panel->cwd, source);
1891 #endif /* WITH_FULL_PATHS */
1893 if (operation == OP_DELETE) {
1894 if (S_ISDIR (src_stat.st_mode))
1895 value = erase_dir (ctx, source_with_path, &count, &bytes);
1896 else
1897 value =
1898 erase_file (ctx, source_with_path, &count, &bytes, 1);
1899 } else {
1900 temp = transform_source (ctx, source_with_path);
1902 if (temp == NULL) {
1903 value = transform_error;
1904 } else {
1905 char *temp2 = mhl_str_dir_plus_file (dest, temp);
1906 g_free (dest);
1907 dest = temp2;
1908 temp = NULL;
1910 switch (operation) {
1911 case OP_COPY:
1913 * we use file_mask_op_follow_links only with OP_COPY,
1915 (*ctx->stat_func) (source_with_path, &src_stat);
1917 if (S_ISDIR (src_stat.st_mode))
1918 value =
1919 copy_dir_dir (ctx, source_with_path, dest, 1,
1920 0, 0, 0, &count, &bytes);
1921 else
1922 value =
1923 copy_file_file (ctx, source_with_path, dest, 1,
1924 &count, &bytes, 1);
1925 break;
1927 case OP_MOVE:
1928 if (S_ISDIR (src_stat.st_mode))
1929 value =
1930 move_dir_dir (ctx, source_with_path, dest,
1931 &count, &bytes);
1932 else
1933 value =
1934 move_file_file (ctx, source_with_path, dest,
1935 &count, &bytes);
1936 break;
1938 default:
1939 /* Unknown file operation */
1940 abort ();
1943 } /* Copy or move operation */
1945 if ((value == FILE_CONT) && !force_single)
1946 unmark_files (panel);
1947 } else {
1948 /* Many files */
1949 /* Check destination for copy or move operation */
1950 if (operation != OP_DELETE) {
1951 retry_many_dst_stat:
1952 dst_result = mc_stat (dest, &dst_stat);
1953 if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)) {
1954 if (file_error
1955 (_(" Destination \"%s\" must be a directory \n %s "),
1956 dest) == FILE_RETRY)
1957 goto retry_many_dst_stat;
1958 goto clean_up;
1962 /* Initialize variables for progress bars */
1963 if (operation != OP_MOVE && verbose && file_op_compute_totals) {
1964 panel_compute_totals (panel, &ctx->progress_count,
1965 &ctx->progress_bytes);
1966 ctx->progress_totals_computed = 1;
1967 } else {
1968 ctx->progress_totals_computed = 0;
1969 ctx->progress_count = panel->marked;
1970 ctx->progress_bytes = panel->total;
1973 /* Loop for every file, perform the actual copy operation */
1974 for (i = 0; i < panel->count; i++) {
1975 if (!panel->dir.list[i].f.marked)
1976 continue; /* Skip the unmarked ones */
1978 source = panel->dir.list[i].fname;
1979 src_stat = panel->dir.list[i].st;
1981 #ifdef WITH_FULL_PATHS
1982 g_free (source_with_path);
1983 source_with_path = mhl_str_dir_plus_file (panel->cwd, source);
1984 #endif /* WITH_FULL_PATHS */
1986 if (operation == OP_DELETE) {
1987 if (S_ISDIR (src_stat.st_mode))
1988 value =
1989 erase_dir (ctx, source_with_path, &count, &bytes);
1990 else
1991 value =
1992 erase_file (ctx, source_with_path, &count, &bytes,
1994 } else {
1995 temp = transform_source (ctx, source_with_path);
1996 if (temp == NULL)
1997 value = transform_error;
1998 else {
1999 char *temp2 = mhl_str_dir_plus_file (dest, temp);
2001 source_with_path = mhl_shell_unescape_buf(source_with_path);
2002 temp2 = mhl_shell_unescape_buf(temp2);
2004 switch (operation) {
2005 case OP_COPY:
2007 * we use file_mask_op_follow_links only with OP_COPY
2009 (*ctx->stat_func) (source_with_path, &src_stat);
2010 if (S_ISDIR (src_stat.st_mode))
2011 value =
2012 copy_dir_dir (ctx, source_with_path, temp2,
2013 1, 0, 0, 0, &count, &bytes);
2014 else
2015 value =
2016 copy_file_file (ctx, source_with_path,
2017 temp2, 1, &count, &bytes,
2019 free_linklist (&dest_dirs);
2020 break;
2022 case OP_MOVE:
2023 if (S_ISDIR (src_stat.st_mode))
2024 value =
2025 move_dir_dir (ctx, source_with_path, temp2,
2026 &count, &bytes);
2027 else
2028 value =
2029 move_file_file (ctx, source_with_path,
2030 temp2, &count, &bytes);
2031 break;
2033 default:
2034 /* Unknown file operation */
2035 abort ();
2037 g_free (temp2);
2039 } /* Copy or move operation */
2041 if (value == FILE_ABORT)
2042 goto clean_up;
2044 if (value == FILE_CONT)
2045 do_file_mark (panel, i, 0);
2047 if (file_progress_show_count (ctx, count, ctx->progress_count)
2048 == FILE_ABORT)
2049 goto clean_up;
2051 if (verbose
2052 && file_progress_show_bytes (ctx, bytes,
2053 ctx->progress_bytes) ==
2054 FILE_ABORT)
2055 goto clean_up;
2057 if (operation != OP_DELETE && verbose
2058 && file_progress_show (ctx, 0, 0) == FILE_ABORT)
2059 goto clean_up;
2061 mc_refresh ();
2062 } /* Loop for every file */
2063 } /* Many entries */
2064 clean_up:
2065 /* Clean up */
2067 if (save_cwd) {
2068 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2069 g_free (save_cwd);
2071 if (save_dest) {
2072 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2073 g_free (save_dest);
2076 free_linklist (&linklist);
2077 free_linklist (&dest_dirs);
2078 #ifdef WITH_FULL_PATHS
2079 g_free (source_with_path);
2080 #endif /* WITH_FULL_PATHS */
2081 g_free (dest);
2082 g_free (ctx->dest_mask);
2083 ctx->dest_mask = NULL;
2084 #ifdef WITH_BACKGROUND
2085 /* Let our parent know we are saying bye bye */
2086 if (we_are_background) {
2087 vfs_shut ();
2088 _exit (0);
2090 #endif /* WITH_BACKGROUND */
2092 file_op_context_destroy (ctx);
2093 return 1;
2096 /* }}} */
2098 /* {{{ Query/status report routines */
2100 static int
2101 real_do_file_error (enum OperationMode mode, const char *error)
2103 int result;
2104 const char *msg;
2106 msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
2107 result =
2108 query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"),
2109 _("&Abort"));
2111 switch (result) {
2112 case 0:
2113 do_refresh ();
2114 return FILE_SKIP;
2116 case 1:
2117 do_refresh ();
2118 return FILE_RETRY;
2120 case 2:
2121 default:
2122 return FILE_ABORT;
2126 /* Report error with one file */
2128 file_error (const char *format, const char *file)
2130 snprintf (cmd_buf, sizeof (cmd_buf), format,
2131 path_trunc (file, 30), unix_error_string (errno));
2133 return do_file_error (cmd_buf);
2136 /* Report error with two files */
2137 static int
2138 files_error (const char *format, const char *file1, const char *file2)
2140 char nfile1[16];
2141 char nfile2[16];
2143 strcpy (nfile1, path_trunc (file1, 15));
2144 strcpy (nfile2, path_trunc (file2, 15));
2146 snprintf (cmd_buf, sizeof (cmd_buf), format, nfile1, nfile2,
2147 unix_error_string (errno));
2149 return do_file_error (cmd_buf);
2152 static int
2153 real_query_recursive (FileOpContext *ctx, enum OperationMode mode, const char *s)
2155 gchar *text;
2157 if (ctx->recursive_result < RECURSIVE_ALWAYS) {
2158 const char *msg =
2159 mode ==
2160 Foreground ?
2161 _("\n Directory not empty. \n"
2162 " Delete it recursively? ")
2163 : _("\n Background process: Directory not empty \n"
2164 " Delete it recursively? ");
2165 text = g_strconcat (_(" Delete: "), path_trunc (s, 30), " ", (char *) NULL);
2167 if (safe_delete)
2168 query_set_sel (1);
2169 ctx->recursive_result = query_dialog (text, msg, D_ERROR, 5,
2170 _("&Yes"), _("&No"),
2171 _("A&ll"), _("Non&e"),
2172 _("&Abort"));
2174 if (ctx->recursive_result != RECURSIVE_ABORT)
2175 do_refresh ();
2176 g_free (text);
2179 switch (ctx->recursive_result) {
2180 case RECURSIVE_YES:
2181 case RECURSIVE_ALWAYS:
2182 return FILE_CONT;
2184 case RECURSIVE_NO:
2185 case RECURSIVE_NEVER:
2186 return FILE_SKIP;
2188 case RECURSIVE_ABORT:
2190 default:
2191 return FILE_ABORT;
2195 #ifdef WITH_BACKGROUND
2196 static int
2197 do_file_error (const char *str)
2199 if (we_are_background)
2200 return parent_call (real_do_file_error, NULL, 1, strlen (str),
2201 str);
2202 else
2203 return real_do_file_error (Foreground, str);
2206 static int
2207 query_recursive (FileOpContext *ctx, const char *s)
2209 if (we_are_background)
2210 return parent_call (real_query_recursive, ctx, 1, strlen (s), s);
2211 else
2212 return real_query_recursive (ctx, Foreground, s);
2215 static int
2216 query_replace (FileOpContext *ctx, const char *destname, struct stat *_s_stat,
2217 struct stat *_d_stat)
2219 if (we_are_background)
2220 return parent_call ((void *) file_progress_real_query_replace,
2221 ctx,
2223 strlen (destname), destname,
2224 sizeof (struct stat), _s_stat,
2225 sizeof (struct stat), _d_stat);
2226 else
2227 return file_progress_real_query_replace (ctx, Foreground, destname,
2228 _s_stat, _d_stat);
2231 #else
2232 static int
2233 do_file_error (const char *str)
2235 return real_do_file_error (Foreground, str);
2238 static int
2239 query_recursive (FileOpContext *ctx, const char *s)
2241 return real_query_recursive (ctx, Foreground, s);
2244 static int
2245 query_replace (FileOpContext *ctx, const char *destname, struct stat *_s_stat,
2246 struct stat *_d_stat)
2248 return file_progress_real_query_replace (ctx, Foreground, destname,
2249 _s_stat, _d_stat);
2252 #endif /* !WITH_BACKGROUND */
2255 Cause emacs to enter folding mode for this file:
2256 Local variables:
2257 end: