Updated italian translation
[midnight-commander.git] / src / file.c
bloba3f77f7b570e09d10612618f2a8c3ef22f1d4b80
1 /* File management.
2 Copyright (C) 1994, 1995, 1996 The Free Software Foundation
4 Written by: 1994, 1995 Janne Kukonlehto
5 1994, 1995 Fred Leeflang
6 1994, 1995, 1996 Miguel de Icaza
7 1995, 1996 Jakub Jelinek
8 1997 Norbert Warmuth
9 1998 Pavel Machek
11 The copy code was based in GNU's cp, and was written by:
12 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
14 The move code was based in GNU's mv, and was written by:
15 Mike Parker and David MacKenzie.
17 Janne Kukonlehto added much error recovery to them for being used
18 in an interactive program.
20 This program is free software; you can redistribute it and/or modify
21 it under the terms of the GNU General Public License as published by
22 the Free Software Foundation; either version 2 of the License, or
23 (at your option) any later version.
25 This program is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program; if not, write to the Free Software
32 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
35 * Please note that all dialogs used here must be safe for background
36 * operations.
39 /* {{{ Include files */
41 #include <config.h>
42 /* Hack: the vfs code should not rely on this */
43 #define WITH_FULL_PATHS 1
45 #include <sys/types.h>
46 #include <stdio.h>
47 #include <errno.h>
48 #include <ctype.h>
49 #include <string.h>
50 #ifdef HAVE_UNISTD_H
51 # include <unistd.h>
52 #endif /* HAVE_UNISTD_H */
53 #include <sys/stat.h>
55 #include "global.h"
56 #include "tty.h"
57 #include "eregex.h"
58 #include "setup.h"
59 #include "color.h"
60 #include "win.h"
61 #include "dialog.h"
62 #include "widget.h"
63 #define WANT_WIDGETS
64 #include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
65 #include "layout.h"
66 #include "widget.h"
67 #include "wtools.h"
68 #include "background.h" /* we_are_background */
70 /* Needed for current_panel, other_panel and WTree */
71 #include "dir.h"
72 #include "panel.h"
73 #include "file.h"
74 #include "filegui.h"
75 #include "tree.h"
76 #include "key.h"
78 /* }}} */
80 int verbose = 1;
83 * Whether the Midnight Commander tries to provide more
84 * information about copy/move sizes and bytes transfered
85 * at the expense of some speed
87 int file_op_compute_totals = 1;
89 /* If on, default for "No" in delete operations */
90 int safe_delete = 0;
92 /* This is a hard link cache */
93 struct link {
94 struct link *next;
95 struct vfs_class *vfs;
96 dev_t dev;
97 ino_t ino;
98 short linkcount;
99 umode_t st_mode;
100 char name[1];
103 /* the hard link cache */
104 static struct link *linklist = NULL;
106 /* the files-to-be-erased list */
107 static struct link *erase_list;
110 * In copy_dir_dir we use two additional single linked lists: The first -
111 * variable name `parent_dirs' - holds information about already copied
112 * directories and is used to detect cyclic symbolic links.
113 * The second (`dest_dirs' below) holds information about just created
114 * target directories and is used to detect when an directory is copied
115 * into itself (we don't want to copy infinitly).
116 * Both lists don't use the linkcount and name structure members of struct
117 * link.
119 static struct link *dest_dirs = 0;
121 char *op_names[3] = {
122 N_(" Copy "),
123 N_(" Move "),
124 N_(" Delete ")
127 /* }}} */
129 static int query_replace (FileOpContext * ctx, char *destname,
130 struct stat *_s_stat, struct stat *_d_stat);
131 static int query_recursive (FileOpContext * ctx, char *s);
132 static int do_file_error (char *str);
133 static int erase_dir_iff_empty (FileOpContext *ctx, char *s);
134 static int erase_file (FileOpContext *ctx, char *s,
135 off_t *progress_count, double *progress_bytes,
136 int is_toplevel_file);
137 static int files_error (const char *format, const char *file1,
138 const char *file2);
141 enum CaseConvs { NO_CONV = 0, UP_CHAR = 1, LOW_CHAR = 2, UP_SECT =
142 4, LOW_SECT = 8 };
144 static int
145 convert_case (int c, enum CaseConvs *conversion)
147 if (*conversion & UP_CHAR) {
148 *conversion &= ~UP_CHAR;
149 return toupper (c);
150 } else if (*conversion & LOW_CHAR) {
151 *conversion &= ~LOW_CHAR;
152 return tolower (c);
153 } else if (*conversion & UP_SECT) {
154 return toupper (c);
155 } else if (*conversion & LOW_SECT) {
156 return tolower (c);
157 } else
158 return c;
161 static int transform_error = 0;
163 static unsigned char *
164 do_transform_source (FileOpContext *ctx, unsigned char *source)
166 int j, k, l, len;
167 unsigned char *fnsource = x_basename (source);
168 int next_reg;
169 enum CaseConvs case_conv = NO_CONV;
170 static unsigned char fntarget[MC_MAXPATHLEN];
172 len = strlen (fnsource);
173 j = re_match (&ctx->rx, fnsource, len, 0, &ctx->regs);
174 if (j != len) {
175 transform_error = FILE_SKIP;
176 return NULL;
178 for (next_reg = 1, j = 0, k = 0; j < strlen (ctx->dest_mask); j++) {
179 switch (ctx->dest_mask[j]) {
180 case '\\':
181 j++;
182 if (!isdigit ((unsigned char) ctx->dest_mask[j])) {
183 /* Backslash followed by non-digit */
184 switch (ctx->dest_mask[j]) {
185 case 'U':
186 case_conv |= UP_SECT;
187 case_conv &= ~LOW_SECT;
188 break;
189 case 'u':
190 case_conv |= UP_CHAR;
191 break;
192 case 'L':
193 case_conv |= LOW_SECT;
194 case_conv &= ~UP_SECT;
195 break;
196 case 'l':
197 case_conv |= LOW_CHAR;
198 break;
199 case 'E':
200 case_conv = NO_CONV;
201 break;
202 default:
203 /* Backslash as quote mark */
204 fntarget[k++] =
205 convert_case (ctx->dest_mask[j], &case_conv);
207 break;
208 } else {
209 /* Backslash followed by digit */
210 next_reg = ctx->dest_mask[j] - '0';
211 /* Fall through */
214 case '*':
215 if (next_reg < 0 || next_reg >= RE_NREGS
216 || ctx->regs.start[next_reg] < 0) {
217 message (1, MSG_ERROR, _(" Invalid target mask "));
218 transform_error = FILE_ABORT;
219 return NULL;
221 for (l = ctx->regs.start[next_reg];
222 l < ctx->regs.end[next_reg]; l++)
223 fntarget[k++] = convert_case (fnsource[l], &case_conv);
224 next_reg++;
225 break;
227 default:
228 fntarget[k++] = convert_case (ctx->dest_mask[j], &case_conv);
229 break;
232 fntarget[k] = 0;
233 return fntarget;
236 static unsigned char *
237 transform_source (FileOpContext *ctx, unsigned char *source)
239 unsigned char *s = g_strdup (source);
240 unsigned char *q;
242 /* We remove \n from the filename since regex routines would use \n as an anchor */
243 /* this is just to be allowed to maniupulate file names with \n on it */
244 for (q = s; *q; q++) {
245 if (*q == '\n')
246 *q = ' ';
248 q = do_transform_source (ctx, s);
249 g_free (s);
250 return q;
253 static void
254 free_linklist (struct link **linklist)
256 struct link *lp, *lp2;
258 for (lp = *linklist; lp != NULL; lp = lp2) {
259 lp2 = lp->next;
260 g_free (lp);
262 *linklist = NULL;
265 static int
266 is_in_linklist (struct link *lp, char *path, struct stat *sb)
268 ino_t ino = sb->st_ino;
269 dev_t dev = sb->st_dev;
270 #ifdef USE_VFS
271 struct vfs_class *vfs = vfs_get_class (path);
272 #endif /* USE_VFS */
274 while (lp) {
275 #ifdef USE_VFS
276 if (lp->vfs == vfs)
277 #endif /* USE_VFS */
278 if (lp->ino == ino && lp->dev == dev)
279 return 1;
280 lp = lp->next;
282 return 0;
286 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
287 * and a hardlink was succesfully made
289 static int
290 check_hardlinks (char *src_name, char *dst_name, struct stat *pstat)
292 struct link *lp;
293 struct vfs_class *my_vfs = vfs_get_class (src_name);
294 ino_t ino = pstat->st_ino;
295 dev_t dev = pstat->st_dev;
296 struct stat link_stat;
297 char *p;
299 if (vfs_file_class_flags (src_name) & VFSF_NOLINKS)
300 return 0;
302 for (lp = linklist; lp != NULL; lp = lp->next)
303 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev) {
304 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
305 && link_stat.st_dev == dev
306 && vfs_get_class (lp->name) == my_vfs) {
307 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
308 was copied to */
309 if (vfs_get_class (dst_name) == vfs_get_class (p)) {
310 if (!mc_stat (p, &link_stat)) {
311 if (!mc_link (p, dst_name))
312 return 1;
316 message (1, MSG_ERROR, _(" Cannot make the hardlink "));
317 return 0;
319 lp = (struct link *) g_malloc (sizeof (struct link) + strlen (src_name)
320 + strlen (dst_name) + 1);
321 if (lp) {
322 lp->vfs = my_vfs;
323 lp->ino = ino;
324 lp->dev = dev;
325 strcpy (lp->name, src_name);
326 p = strchr (lp->name, 0) + 1;
327 strcpy (p, dst_name);
328 lp->next = linklist;
329 linklist = lp;
331 return 0;
335 * Duplicate the contents of the symbolic link src_path in dst_path.
336 * Try to make a stable symlink if the option "stable symlink" was
337 * set in the file mask dialog.
338 * If dst_path is an existing symlink it will be deleted silently
339 * (upper levels take already care of existing files at dst_path).
341 static int
342 make_symlink (FileOpContext *ctx, char *src_path, char *dst_path)
344 char link_target[MC_MAXPATHLEN];
345 int len;
346 int return_status;
347 struct stat sb;
348 int dst_is_symlink;
350 if (mc_lstat (dst_path, &sb) == 0 && S_ISLNK (sb.st_mode))
351 dst_is_symlink = 1;
352 else
353 dst_is_symlink = 0;
355 retry_src_readlink:
356 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
357 if (len < 0) {
358 return_status =
359 file_error (_(" Cannot read source link \"%s\" \n %s "),
360 src_path);
361 if (return_status == FILE_RETRY)
362 goto retry_src_readlink;
363 return return_status;
365 link_target[len] = 0;
367 if (ctx->stable_symlinks)
368 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path)) {
369 message (1, MSG_ERROR,
370 _(" Cannot make stable symlinks across "
371 "non-local filesystems: \n\n"
372 " Option Stable Symlinks will be disabled "));
373 ctx->stable_symlinks = 0;
376 if (ctx->stable_symlinks && *link_target != PATH_SEP) {
377 char *p, *q, *r, *s;
379 p = g_strdup (src_path);
380 r = strrchr (p, PATH_SEP);
382 if (r) {
383 r[1] = 0;
384 if (*dst_path == PATH_SEP)
385 q = g_strdup (dst_path);
386 else
387 q = g_strconcat (p, dst_path, NULL);
388 r = strrchr (q, PATH_SEP);
389 if (r) {
390 r[1] = 0;
391 s = g_strconcat (p, link_target, NULL);
392 strcpy (link_target, s);
393 g_free (s);
394 s = diff_two_paths (q, link_target);
395 if (s) {
396 strcpy (link_target, s);
397 g_free (s);
400 g_free (q);
402 g_free (p);
404 retry_dst_symlink:
405 if (mc_symlink (link_target, dst_path) == 0)
406 /* Success */
407 return FILE_CONT;
409 * if dst_exists, it is obvious that this had failed.
410 * We can delete the old symlink and try again...
412 if (dst_is_symlink) {
413 if (!mc_unlink (dst_path))
414 if (mc_symlink (link_target, dst_path) == 0)
415 /* Success */
416 return FILE_CONT;
418 return_status =
419 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
420 dst_path);
421 if (return_status == FILE_RETRY)
422 goto retry_dst_symlink;
423 return return_status;
426 static int
427 progress_update_one (FileOpContext *ctx,
428 off_t *progress_count,
429 double *progress_bytes, int add, int is_toplevel_file)
431 int ret;
433 if (is_toplevel_file || ctx->progress_totals_computed) {
434 (*progress_count)++;
435 (*progress_bytes) += add;
438 /* Apply some heuristic here to not call the update stuff very often */
439 ret =
440 file_progress_show_count (ctx, *progress_count,
441 ctx->progress_count);
442 if (ret != FILE_CONT)
443 return ret;
444 ret =
445 file_progress_show_bytes (ctx, *progress_bytes,
446 ctx->progress_bytes);
448 return ret;
451 /* Status of the destination file */
452 enum {
453 DEST_NONE, /* Not created */
454 DEST_SHORT, /* Created, not fully copied */
455 DEST_FULL /* Created, fully copied */
459 copy_file_file (FileOpContext *ctx, char *src_path, char *dst_path,
460 int ask_overwrite, off_t *progress_count,
461 double *progress_bytes, int is_toplevel_file)
463 uid_t src_uid = (uid_t) - 1;
464 gid_t src_gid = (gid_t) - 1;
466 char *buf = NULL;
467 int buf_size = BUF_8K;
468 int src_desc, dest_desc = -1;
469 int n_read, n_written;
470 int src_mode = 0; /* The mode of the source file */
471 struct stat sb, sb2;
472 struct utimbuf utb;
473 int dst_exists = 0, appending = 0;
474 off_t n_read_total = 0, file_size = -1;
475 int return_status, temp_status;
476 struct timeval tv_transfer_start;
477 int dst_status = DEST_NONE; /* 1 if the file is not fully copied */
479 /* FIXME: We should not be using global variables! */
480 ctx->do_reget = 0;
481 return_status = FILE_RETRY;
483 if (file_progress_show_source (ctx, src_path) == FILE_ABORT ||
484 file_progress_show_target (ctx, dst_path) == FILE_ABORT)
485 return FILE_ABORT;
487 mc_refresh ();
489 retry_dst_stat:
490 if (mc_stat (dst_path, &sb2) == 0) {
491 if (S_ISDIR (sb2.st_mode)) {
492 return_status =
493 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
494 dst_path);
495 if (return_status == FILE_RETRY)
496 goto retry_dst_stat;
497 return return_status;
499 dst_exists = 1;
502 while ((*ctx->stat_func) (src_path, &sb)) {
503 return_status =
504 file_error (_(" Cannot stat source file \"%s\" \n %s "),
505 src_path);
506 if (return_status != FILE_RETRY)
507 return return_status;
510 if (dst_exists) {
511 /* Destination already exists */
512 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino) {
513 message (1, MSG_ERROR,
514 _(" `%s' and `%s' are the same file "), src_path,
515 dst_path);
516 do_refresh ();
517 return FILE_SKIP;
520 /* Should we replace destination? */
521 if (ask_overwrite) {
522 ctx->do_reget = 0;
523 return_status = query_replace (ctx, dst_path, &sb, &sb2);
524 if (return_status != FILE_CONT)
525 return return_status;
529 if (!ctx->do_append) {
530 /* Check the hardlinks */
531 if (!ctx->follow_links && sb.st_nlink > 1 &&
532 check_hardlinks (src_path, dst_path, &sb) == 1) {
533 /* We have made a hardlink - no more processing is necessary */
534 return return_status;
537 if (S_ISLNK (sb.st_mode)) {
538 int retval;
540 retval = make_symlink (ctx, src_path, dst_path);
541 return retval;
544 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode)
545 || S_ISFIFO (sb.st_mode)
546 || S_ISSOCK (sb.st_mode)) {
547 while (mc_mknod
548 (dst_path, sb.st_mode & ctx->umask_kill,
549 sb.st_rdev) < 0) {
550 return_status =
551 file_error (_
552 (" Cannot create special file \"%s\" \n %s "),
553 dst_path);
554 if (return_status == FILE_RETRY)
555 continue;
556 return return_status;
558 /* Success */
560 while (ctx->preserve_uidgid
561 && mc_chown (dst_path, sb.st_uid, sb.st_gid)) {
562 temp_status =
563 file_error (_
564 (" Cannot chown target file \"%s\" \n %s "),
565 dst_path);
566 if (temp_status == FILE_RETRY)
567 continue;
568 return temp_status;
570 while (ctx->preserve &&
571 (mc_chmod (dst_path, sb.st_mode & ctx->umask_kill) <
572 0)) {
573 temp_status =
574 file_error (_
575 (" Cannot chmod target file \"%s\" \n %s "),
576 dst_path);
577 if (temp_status == FILE_RETRY)
578 continue;
579 return temp_status;
581 return FILE_CONT;
585 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
587 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0) {
588 return_status =
589 file_error (_(" Cannot open source file \"%s\" \n %s "),
590 src_path);
591 if (return_status == FILE_RETRY)
592 continue;
593 ctx->do_append = 0;
594 return return_status;
597 if (ctx->do_reget) {
598 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget) {
599 message (1, _("Warning"),
600 _(" Reget failed, about to overwrite file "));
601 ctx->do_reget = ctx->do_append = 0;
605 while (mc_fstat (src_desc, &sb)) {
606 return_status =
607 file_error (_(" Cannot fstat source file \"%s\" \n %s "),
608 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 |
626 (ctx->
627 do_append ? O_APPEND : (O_CREAT |
628 O_TRUNC)),
629 0600)) < 0) {
630 return_status =
631 file_error (_(" Cannot create target file \"%s\" \n %s "),
632 dst_path);
633 if (return_status == FILE_RETRY)
634 continue;
635 ctx->do_append = 0;
636 goto ret;
638 dst_status = DEST_SHORT; /* file opened, but not fully copied */
640 appending = ctx->do_append;
641 ctx->do_append = 0;
643 /* Find out the optimal buffer size. */
644 while (mc_fstat (dest_desc, &sb)) {
645 return_status =
646 file_error (_(" Cannot fstat target file \"%s\" \n %s "),
647 dst_path);
648 if (return_status == FILE_RETRY)
649 continue;
650 goto ret;
652 buf = (char *) g_malloc (buf_size);
654 ctx->eta_secs = 0.0;
655 ctx->bps = 0;
657 return_status = file_progress_show (ctx, 0, file_size);
659 mc_refresh ();
661 if (return_status != FILE_CONT)
662 goto ret;
665 struct timeval tv_current, tv_last_update, tv_last_input;
666 int secs, update_secs;
667 long dt;
668 char *stalled_msg;
670 tv_last_update = tv_transfer_start;
672 for (;;) {
673 /* src_read */
674 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
675 n_read = -1;
676 else
677 while ((n_read = mc_read (src_desc, buf, buf_size)) < 0) {
678 return_status =
679 file_error (_
680 (" Cannot read source file \"%s\" \n %s "),
681 src_path);
682 if (return_status == FILE_RETRY)
683 continue;
684 goto ret;
686 if (n_read == 0)
687 break;
689 gettimeofday (&tv_current, NULL);
691 if (n_read > 0) {
692 char *t = buf;
693 n_read_total += n_read;
695 /* Windows NT ftp servers report that files have no
696 * permissions: -------, so if we happen to have actually
697 * read something, we should fix the permissions.
699 if (!(src_mode & ((S_IRUSR | S_IWUSR | S_IXUSR) /* user */
700 |(S_IXOTH | S_IWOTH | S_IROTH) /* other */
701 |(S_IXGRP | S_IWGRP | S_IRGRP)))) /* group */
702 src_mode = S_IRUSR | S_IWUSR | S_IROTH | S_IRGRP;
703 gettimeofday (&tv_last_input, NULL);
705 /* dst_write */
706 while ((n_written =
707 mc_write (dest_desc, t, n_read)) < n_read) {
708 if (n_written > 0) {
709 n_read -= n_written;
710 t += n_written;
711 continue;
713 return_status =
714 file_error (_
715 (" Cannot write target file \"%s\" \n %s "),
716 dst_path);
717 if (return_status != FILE_RETRY)
718 goto ret;
722 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
723 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
724 if (secs > 2) {
725 rotate_dash ();
726 tv_last_update = tv_current;
729 /* 2. Check for a stalled condition */
730 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
731 stalled_msg = "";
732 if (update_secs > 4) {
733 stalled_msg = _("(stalled)");
736 /* 3. Compute ETA */
737 if (secs > 2) {
738 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
740 if (n_read_total) {
741 ctx->eta_secs =
742 ((dt / (double) n_read_total) * file_size) - dt;
743 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
744 } else
745 ctx->eta_secs = 0.0;
748 /* 4. Compute BPS rate */
749 if (secs > 2) {
750 ctx->bps_time =
751 (tv_current.tv_sec - tv_transfer_start.tv_sec);
752 if (ctx->bps_time < 1)
753 ctx->bps_time = 1;
754 ctx->bps = n_read_total / ctx->bps_time;
757 file_progress_set_stalled_label (ctx, stalled_msg);
758 return_status =
759 file_progress_show_bytes (ctx,
760 *progress_bytes + n_read_total,
761 ctx->progress_bytes);
762 if (return_status == FILE_CONT) {
763 return_status =
764 file_progress_show (ctx, n_read_total, file_size);
766 mc_refresh ();
767 if (return_status != FILE_CONT)
768 goto ret;
772 dst_status = DEST_FULL; /* copy successful, don't remove target file */
774 ret:
775 if (buf)
776 g_free (buf);
778 while (src_desc != -1 && mc_close (src_desc) < 0) {
779 temp_status =
780 file_error (_(" Cannot close source file \"%s\" \n %s "),
781 src_path);
782 if (temp_status == FILE_RETRY)
783 continue;
784 if (temp_status == FILE_ABORT)
785 return_status = temp_status;
786 break;
789 while (dest_desc != -1 && mc_close (dest_desc) < 0) {
790 temp_status =
791 file_error (_(" Cannot close target file \"%s\" \n %s "),
792 dst_path);
793 if (temp_status == FILE_RETRY)
794 continue;
795 return_status = temp_status;
796 break;
799 if (dst_status == DEST_SHORT) {
800 /* Remove short file */
801 int result;
802 result =
803 query_dialog (_("Copy"),
804 _("Incomplete file was retrieved. Keep it?"),
805 D_ERROR, 2, _("&Delete"), _("&Keep"));
806 if (!result)
807 mc_unlink (dst_path);
808 } else if (dst_status == DEST_FULL) {
809 /* Copy has succeeded */
810 if (!appending && ctx->preserve_uidgid) {
811 while (mc_chown (dst_path, src_uid, src_gid)) {
812 temp_status = file_error
813 (_(" Cannot chown target file \"%s\" \n %s "),
814 dst_path);
815 if (temp_status == FILE_RETRY)
816 continue;
817 return_status = temp_status;
818 break;
823 * .ado: according to the XPG4 standard, the file must be closed before
824 * chmod can be invoked
826 if (!appending && ctx->preserve) {
827 while (mc_chmod (dst_path, src_mode & ctx->umask_kill)) {
828 temp_status =
829 file_error (_
830 (" Cannot chmod target file \"%s\" \n %s "),
831 dst_path);
832 if (temp_status != FILE_RETRY) {
833 return_status = temp_status;
834 break;
837 mc_utime (dst_path, &utb);
841 if (return_status == FILE_CONT)
842 return_status =
843 progress_update_one (ctx, progress_count, progress_bytes,
844 file_size, is_toplevel_file);
846 return return_status;
850 * I think these copy_*_* functions should have a return type.
851 * anyway, this function *must* have two directories as arguments.
853 /* FIXME: This function needs to check the return values of the
854 function calls */
856 copy_dir_dir (FileOpContext *ctx, char *s, char *d, int toplevel,
857 int move_over, int delete,
858 struct link *parent_dirs,
859 off_t *progress_count, double *progress_bytes)
861 struct dirent *next;
862 struct stat buf, cbuf;
863 DIR *reading;
864 char *path, *mdpath, *dest_file, *dest_dir;
865 int return_status = FILE_CONT;
866 struct utimbuf utb;
867 struct link *lp;
869 /* First get the mode of the source dir */
870 retry_src_stat:
871 if ((*ctx->stat_func) (s, &cbuf)) {
872 return_status =
873 file_error (_(" Cannot stat source directory \"%s\" \n %s "),
875 if (return_status == FILE_RETRY)
876 goto retry_src_stat;
877 return return_status;
880 if (is_in_linklist (dest_dirs, s, &cbuf)) {
881 /* Don't copy a directory we created before (we don't want to copy
882 infinitely if a directory is copied into itself) */
883 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
884 return FILE_CONT;
887 /* Hmm, hardlink to directory??? - Norbert */
888 /* FIXME: In this step we should do something
889 in case the destination already exist */
890 /* Check the hardlinks */
891 if (ctx->preserve && cbuf.st_nlink > 1
892 && check_hardlinks (s, d, &cbuf) == 1) {
893 /* We have made a hardlink - no more processing is necessary */
894 return return_status;
897 if (!S_ISDIR (cbuf.st_mode)) {
898 return_status =
899 file_error (_
900 (" Source directory \"%s\" is not a directory \n %s "),
902 if (return_status == FILE_RETRY)
903 goto retry_src_stat;
904 return return_status;
907 if (is_in_linklist (parent_dirs, s, &cbuf)) {
908 /* we found a cyclic symbolic link */
909 message (1, MSG_ERROR,
910 _(" Cannot copy cyclic symbolic link \n `%s' "), s);
911 return FILE_SKIP;
914 lp = g_new (struct link, 1);
915 lp->vfs = vfs_get_class (s);
916 lp->ino = cbuf.st_ino;
917 lp->dev = cbuf.st_dev;
918 lp->next = parent_dirs;
919 parent_dirs = lp;
921 retry_dst_stat:
922 /* Now, check if the dest dir exists, if not, create it. */
923 if (mc_stat (d, &buf)) {
924 /* Here the dir doesn't exist : make it ! */
926 if (move_over) {
927 if (mc_rename (s, d) == 0) {
928 g_free (parent_dirs);
929 return FILE_CONT;
932 dest_dir = g_strdup (d);
933 } else {
935 * If the destination directory exists, we want to copy the whole
936 * directory, but we only want this to happen once.
938 * Escape sequences added to the * to compiler warnings.
939 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
940 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
942 if (!S_ISDIR (buf.st_mode)) {
943 return_status =
944 file_error (_
945 (" Destination \"%s\" must be a directory \n %s "),
947 if (return_status == FILE_RETRY)
948 goto retry_dst_stat;
949 g_free (parent_dirs);
950 return return_status;
952 #if 1
953 /* Again, I'm getting curious. Is not d already what we wanted, incl.
954 * masked source basename? Is not this just a relict of the past versions?
955 * I'm afraid this will lead into a two level deep dive :(
957 * I think this is indeed the problem. I cannot remember any case where
958 * we actually would like that behavior -miguel
960 * It's a documented feature (option `Dive into subdir if exists' in the
961 * copy/move dialog). -Norbert
963 if (toplevel && ctx->dive_into_subdirs) {
964 dest_dir = concat_dir_and_file (d, x_basename (s));
965 } else
966 #endif
968 dest_dir = g_strdup (d);
969 goto dont_mkdir;
972 retry_dst_mkdir:
973 if (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU)) {
974 return_status =
975 file_error (_(" Cannot create target directory \"%s\" \n %s "),
976 dest_dir);
977 if (return_status == FILE_RETRY)
978 goto retry_dst_mkdir;
979 goto ret;
982 lp = g_new (struct link, 1);
983 mc_stat (dest_dir, &buf);
984 lp->vfs = vfs_get_class (dest_dir);
985 lp->ino = buf.st_ino;
986 lp->dev = buf.st_dev;
987 lp->next = dest_dirs;
988 dest_dirs = lp;
990 if (ctx->preserve_uidgid) {
991 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid)) {
992 return_status =
993 file_error (_
994 (" Cannot chown target directory \"%s\" \n %s "),
995 dest_dir);
996 if (return_status != FILE_RETRY)
997 goto ret;
1001 dont_mkdir:
1002 /* open the source dir for reading */
1003 if ((reading = mc_opendir (s)) == 0) {
1004 goto ret;
1007 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT) {
1009 * Now, we don't want '.' and '..' to be created / copied at any time
1011 if (!strcmp (next->d_name, "."))
1012 continue;
1013 if (!strcmp (next->d_name, ".."))
1014 continue;
1016 /* get the filename and add it to the src directory */
1017 path = concat_dir_and_file (s, next->d_name);
1019 (*ctx->stat_func) (path, &buf);
1020 if (S_ISDIR (buf.st_mode)) {
1021 mdpath = concat_dir_and_file (dest_dir, next->d_name);
1023 * From here, we just intend to recursively copy subdirs, not
1024 * the double functionality of copying different when the target
1025 * dir already exists. So, we give the recursive call the flag 0
1026 * meaning no toplevel.
1028 return_status = copy_dir_dir (ctx, path, mdpath, 0, 0,
1029 delete, parent_dirs,
1030 progress_count, progress_bytes);
1031 g_free (mdpath);
1032 } else {
1033 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
1034 return_status = copy_file_file (ctx, path, dest_file, 1,
1035 progress_count, progress_bytes,
1037 g_free (dest_file);
1039 if (delete && return_status == FILE_CONT) {
1040 if (ctx->erase_at_end) {
1041 static struct link *tail;
1042 lp = g_malloc (sizeof (struct link) + strlen (path));
1043 strcpy (lp->name, path);
1044 lp->st_mode = buf.st_mode;
1045 lp->next = 0;
1046 if (erase_list) {
1047 tail->next = lp;
1048 tail = lp;
1049 } else
1050 erase_list = tail = lp;
1051 } else {
1052 if (S_ISDIR (buf.st_mode)) {
1053 return_status = erase_dir_iff_empty (ctx, path);
1054 } else
1055 return_status = erase_file (ctx, path, 0, 0, 0);
1059 g_free (path);
1061 mc_closedir (reading);
1063 if (ctx->preserve) {
1064 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1065 utb.actime = cbuf.st_atime;
1066 utb.modtime = cbuf.st_mtime;
1067 mc_utime (dest_dir, &utb);
1070 ret:
1071 g_free (dest_dir);
1072 g_free (parent_dirs);
1073 return return_status;
1076 /* }}} */
1078 /* {{{ Move routines */
1080 static int
1081 move_file_file (FileOpContext *ctx, char *s, char *d,
1082 off_t *progress_count, double *progress_bytes)
1084 struct stat src_stats, dst_stats;
1085 int return_status = FILE_CONT;
1087 if (file_progress_show_source (ctx, s) == FILE_ABORT
1088 || file_progress_show_target (ctx, d) == FILE_ABORT)
1089 return FILE_ABORT;
1091 mc_refresh ();
1093 while (mc_lstat (s, &src_stats) != 0) {
1094 /* Source doesn't exist */
1095 return_status =
1096 file_error (_(" Cannot stat file \"%s\" \n %s "), s);
1097 if (return_status != FILE_RETRY)
1098 return return_status;
1101 if (mc_lstat (d, &dst_stats) == 0) {
1102 if (src_stats.st_dev == dst_stats.st_dev
1103 && src_stats.st_ino == dst_stats.st_ino) {
1104 int msize = COLS - 36;
1105 char st[MC_MAXPATHLEN];
1106 char dt[MC_MAXPATHLEN];
1108 if (msize < 0)
1109 msize = 40;
1110 msize /= 2;
1112 strcpy (st, name_trunc (s, msize));
1113 strcpy (dt, name_trunc (d, msize));
1114 message (1, MSG_ERROR,
1115 _(" `%s' and `%s' are the same file "), st, dt);
1116 do_refresh ();
1117 return FILE_SKIP;
1120 if (S_ISDIR (dst_stats.st_mode)) {
1121 message (1, MSG_ERROR,
1122 _(" Cannot overwrite directory `%s' "), d);
1123 do_refresh ();
1124 return FILE_SKIP;
1127 if (confirm_overwrite) {
1128 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1129 if (return_status != FILE_CONT)
1130 return return_status;
1132 /* Ok to overwrite */
1135 if (!ctx->do_append) {
1136 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks) {
1137 if ((return_status = make_symlink (ctx, s, d)) == FILE_CONT) {
1138 goto retry_src_remove;
1139 } else
1140 return return_status;
1143 if (mc_rename (s, d) == 0) {
1144 return FILE_CONT;
1147 #if 0
1148 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1149 one nfs to the same, but on the server it is on two different
1150 filesystems. Then nfs returns EIO instead of EXDEV.
1151 Hope it will not hurt if we always in case of error try to copy/delete. */
1152 else
1153 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1155 if (errno != EXDEV) {
1156 return_status =
1157 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s,
1159 if (return_status == FILE_RETRY)
1160 goto retry_rename;
1161 return return_status;
1163 #endif
1165 /* Failed because filesystem boundary -> copy the file instead */
1166 return_status =
1167 copy_file_file (ctx, s, d, 0, progress_count, progress_bytes, 1);
1168 if (return_status != FILE_CONT)
1169 return return_status;
1171 if ((return_status =
1172 file_progress_show_source (ctx, NULL)) != FILE_CONT
1173 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1174 return return_status;
1176 mc_refresh ();
1178 retry_src_remove:
1179 if (mc_unlink (s)) {
1180 return_status =
1181 file_error (_(" Cannot remove file \"%s\" \n %s "), s);
1182 if (return_status == FILE_RETRY)
1183 goto retry_src_remove;
1184 return return_status;
1187 if (return_status == FILE_CONT)
1188 return_status = progress_update_one (ctx,
1189 progress_count,
1190 progress_bytes,
1191 src_stats.st_size, 1);
1193 return return_status;
1197 move_dir_dir (FileOpContext *ctx, char *s, char *d,
1198 off_t *progress_count, double *progress_bytes)
1200 struct stat sbuf, dbuf, destbuf;
1201 struct link *lp;
1202 char *destdir;
1203 int return_status;
1204 int move_over = 0;
1206 if (file_progress_show_source (ctx, s) == FILE_ABORT ||
1207 file_progress_show_target (ctx, d) == FILE_ABORT)
1208 return FILE_ABORT;
1210 mc_refresh ();
1212 mc_stat (s, &sbuf);
1213 if (mc_stat (d, &dbuf))
1214 destdir = g_strdup (d); /* destination doesn't exist */
1215 else if (!ctx->dive_into_subdirs) {
1216 destdir = g_strdup (d);
1217 move_over = 1;
1218 } else
1219 destdir = concat_dir_and_file (d, x_basename (s));
1221 if (sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino) {
1222 int msize = COLS - 36;
1223 char st[MC_MAXPATHLEN];
1224 char dt[MC_MAXPATHLEN];
1226 if (msize < 0)
1227 msize = 40;
1228 msize /= 2;
1230 strcpy (st, name_trunc (s, msize));
1231 strcpy (dt, name_trunc (d, msize));
1232 message (1, MSG_ERROR,
1233 _(" `%s' and `%s' are the same directory "), st, dt);
1234 do_refresh ();
1235 return FILE_SKIP;
1238 /* Check if the user inputted an existing dir */
1239 retry_dst_stat:
1240 if (!mc_stat (destdir, &destbuf)) {
1241 if (move_over) {
1242 return_status = copy_dir_dir (ctx, s, destdir, 0, 1, 1, 0,
1243 progress_count, progress_bytes);
1245 if (return_status != FILE_CONT)
1246 goto ret;
1247 goto oktoret;
1248 } else {
1249 if (S_ISDIR (destbuf.st_mode))
1250 return_status =
1251 file_error (_
1252 (" Cannot overwrite directory \"%s\" %s "),
1253 destdir);
1254 else
1255 return_status =
1256 file_error (_(" Cannot overwrite file \"%s\" %s "),
1257 destdir);
1258 if (return_status == FILE_RETRY)
1259 goto retry_dst_stat;
1261 g_free (destdir);
1262 return return_status;
1265 retry_rename:
1266 if (mc_rename (s, destdir) == 0) {
1267 return_status = FILE_CONT;
1268 goto ret;
1270 /* .ado: Drive, Do we need this anymore? */
1271 #ifdef WIN32
1272 else {
1273 /* EXDEV: cross device; does not work everywhere */
1274 if (toupper (s[0]) != toupper (destdir[0]))
1275 goto w32try;
1277 #endif /* WIN32 */
1279 if (errno != EXDEV) {
1280 return_status =
1281 files_error (_
1282 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1283 s, d);
1284 if (return_status == FILE_RETRY)
1285 goto retry_rename;
1286 goto ret;
1288 #ifdef WIN32
1289 w32try:
1290 #endif /* WIN32 */
1291 /* Failed because of filesystem boundary -> copy dir instead */
1292 return_status =
1293 copy_dir_dir (ctx, s, destdir, 0, 0, 1, 0, progress_count,
1294 progress_bytes);
1296 if (return_status != FILE_CONT)
1297 goto ret;
1298 oktoret:
1299 if ((return_status =
1300 file_progress_show_source (ctx, NULL)) != FILE_CONT
1301 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1302 goto ret;
1304 mc_refresh ();
1305 if (ctx->erase_at_end) {
1306 for (; erase_list && return_status != FILE_ABORT;) {
1307 if (S_ISDIR (erase_list->st_mode)) {
1308 return_status =
1309 erase_dir_iff_empty (ctx, erase_list->name);
1310 } else
1311 return_status =
1312 erase_file (ctx, erase_list->name, 0, 0, 0);
1313 lp = erase_list;
1314 erase_list = erase_list->next;
1315 g_free (lp);
1318 erase_dir_iff_empty (ctx, s);
1320 ret:
1321 g_free (destdir);
1322 while (erase_list) {
1323 lp = erase_list;
1324 erase_list = erase_list->next;
1325 g_free (lp);
1327 return return_status;
1330 /* }}} */
1332 /* {{{ Erase routines */
1333 /* Don't update progress status if progress_count==NULL */
1334 static int
1335 erase_file (FileOpContext *ctx, char *s, off_t *progress_count,
1336 double *progress_bytes, int is_toplevel_file)
1338 int return_status;
1339 struct stat buf;
1341 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1342 return FILE_ABORT;
1343 mc_refresh ();
1345 if (progress_count && mc_lstat (s, &buf)) {
1346 /* ignore, most likely the mc_unlink fails, too */
1347 buf.st_size = 0;
1350 while (mc_unlink (s)) {
1351 return_status =
1352 file_error (_(" Cannot delete file \"%s\" \n %s "), s);
1353 if (return_status != FILE_RETRY)
1354 return return_status;
1357 if (progress_count)
1358 return progress_update_one (ctx, progress_count, progress_bytes,
1359 buf.st_size, is_toplevel_file);
1360 else
1361 return FILE_CONT;
1364 static int
1365 recursive_erase (FileOpContext *ctx, char *s, off_t *progress_count,
1366 double *progress_bytes)
1368 struct dirent *next;
1369 struct stat buf;
1370 DIR *reading;
1371 char *path;
1372 int return_status = FILE_CONT;
1374 if (!strcmp (s, ".."))
1375 return 1;
1377 reading = mc_opendir (s);
1379 if (!reading)
1380 return 1;
1382 while ((next = mc_readdir (reading)) && return_status == FILE_CONT) {
1383 if (!strcmp (next->d_name, "."))
1384 continue;
1385 if (!strcmp (next->d_name, ".."))
1386 continue;
1387 path = concat_dir_and_file (s, next->d_name);
1388 if (mc_lstat (path, &buf)) {
1389 g_free (path);
1390 mc_closedir (reading);
1391 return 1;
1393 if (S_ISDIR (buf.st_mode))
1394 return_status =
1395 (recursive_erase
1396 (ctx, path, progress_count, progress_bytes)
1397 != FILE_CONT);
1398 else
1399 return_status =
1400 erase_file (ctx, path, progress_count, progress_bytes, 0);
1401 g_free (path);
1403 mc_closedir (reading);
1404 if (return_status != FILE_CONT)
1405 return return_status;
1406 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1407 return FILE_ABORT;
1408 mc_refresh ();
1410 while (my_rmdir (s)) {
1411 return_status =
1412 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1413 if (return_status != FILE_RETRY)
1414 return return_status;
1417 return FILE_CONT;
1420 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1421 in the directory path points to, 0 else. */
1422 static int
1423 check_dir_is_empty (char *path)
1425 DIR *dir;
1426 struct dirent *d;
1427 int i;
1429 dir = mc_opendir (path);
1430 if (!dir)
1431 return -1;
1433 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir)) {
1434 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1435 (d->d_name[1] == '.'
1436 && d->d_name[2] == '\0')))
1437 continue; /* "." or ".." */
1438 i = 0;
1439 break;
1442 mc_closedir (dir);
1443 return i;
1447 erase_dir (FileOpContext *ctx, char *s, off_t *progress_count,
1448 double *progress_bytes)
1450 int error;
1452 if (strcmp (s, "..") == 0)
1453 return FILE_SKIP;
1455 if (strcmp (s, ".") == 0)
1456 return FILE_SKIP;
1458 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1459 return FILE_ABORT;
1460 mc_refresh ();
1462 /* The old way to detect a non empty directory was:
1463 error = my_rmdir (s);
1464 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1465 For the linux user space nfs server (nfs-server-2.2beta29-2)
1466 we would have to check also for EIO. I hope the new way is
1467 fool proof. (Norbert)
1469 error = check_dir_is_empty (s);
1470 if (error == 0) { /* not empty */
1471 error = query_recursive (ctx, s);
1472 if (error == FILE_CONT)
1473 return recursive_erase (ctx, s, progress_count,
1474 progress_bytes);
1475 else
1476 return error;
1479 while (my_rmdir (s) == -1) {
1480 error =
1481 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1482 if (error != FILE_RETRY)
1483 return error;
1486 return FILE_CONT;
1489 static int
1490 erase_dir_iff_empty (FileOpContext *ctx, char *s)
1492 int error;
1494 if (strcmp (s, "..") == 0)
1495 return FILE_SKIP;
1497 if (strcmp (s, ".") == 0)
1498 return FILE_SKIP;
1500 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1501 return FILE_ABORT;
1502 mc_refresh ();
1504 if (1 != check_dir_is_empty (s)) /* not empty or error */
1505 return FILE_CONT;
1507 while (my_rmdir (s)) {
1508 error =
1509 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1510 if (error != FILE_RETRY)
1511 return error;
1514 return FILE_CONT;
1517 /* }}} */
1519 /* {{{ Panel operate routines */
1522 * Return currently selected entry name or the name of the first marked
1523 * entry if there is one.
1525 static char *
1526 panel_get_file (WPanel *panel, struct stat *stat_buf)
1528 int i;
1530 if (get_current_type () == view_tree) {
1531 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
1532 char *tree_name = tree_selected_name (tree);
1534 mc_stat (tree_name, stat_buf);
1535 return tree_name;
1538 if (panel->marked) {
1539 for (i = 0; i < panel->count; i++)
1540 if (panel->dir.list[i].f.marked) {
1541 *stat_buf = panel->dir.list[i].st;
1542 return panel->dir.list[i].fname;
1544 } else {
1545 *stat_buf = panel->dir.list[panel->selected].st;
1546 return panel->dir.list[panel->selected].fname;
1548 g_assert_not_reached ();
1549 return NULL;
1553 * compute_dir_size:
1555 * Computes the number of bytes used by the files in a directory
1557 void
1558 compute_dir_size (char *dirname, off_t *ret_marked, double *ret_total)
1560 DIR *dir;
1561 struct dirent *dirent;
1563 dir = mc_opendir (dirname);
1565 if (!dir)
1566 return;
1568 while ((dirent = mc_readdir (dir)) != NULL) {
1569 struct stat s;
1570 char *fullname;
1571 int res;
1573 if (strcmp (dirent->d_name, ".") == 0)
1574 continue;
1575 if (strcmp (dirent->d_name, "..") == 0)
1576 continue;
1578 fullname = concat_dir_and_file (dirname, dirent->d_name);
1580 res = mc_lstat (fullname, &s);
1582 if (res != 0) {
1583 g_free (fullname);
1584 continue;
1587 if (S_ISDIR (s.st_mode)) {
1588 off_t subdir_count = 0;
1589 double subdir_bytes = 0;
1591 compute_dir_size (fullname, &subdir_count, &subdir_bytes);
1593 *ret_marked += subdir_count;
1594 *ret_total += subdir_bytes;
1595 } else {
1596 (*ret_marked)++;
1597 *ret_total += s.st_size;
1599 g_free (fullname);
1602 mc_closedir (dir);
1606 * panel_compute_totals:
1608 * compute the number of files and the number of bytes
1609 * used up by the whole selection, recursing directories
1610 * as required. In addition, it checks to see if it will
1611 * overwrite any files by doing the copy.
1613 static void
1614 panel_compute_totals (WPanel *panel, off_t *ret_marked, double *ret_total)
1616 int i;
1618 *ret_marked = 0;
1619 *ret_total = 0.0;
1621 for (i = 0; i < panel->count; i++) {
1622 struct stat *s;
1624 if (!panel->dir.list[i].f.marked)
1625 continue;
1627 s = &panel->dir.list[i].st;
1629 if (S_ISDIR (s->st_mode)) {
1630 char *dir_name;
1631 off_t subdir_count = 0;
1632 double subdir_bytes = 0;
1634 dir_name =
1635 concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1636 compute_dir_size (dir_name, &subdir_count, &subdir_bytes);
1638 *ret_marked += subdir_count;
1639 *ret_total += subdir_bytes;
1640 g_free (dir_name);
1641 } else {
1642 (*ret_marked)++;
1643 *ret_total += s->st_size;
1649 * This array introduced to avoid translation problems. The former (op_names)
1650 * is assumed to be nouns, suitable in dialog box titles; this one should
1651 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1652 * Notice first symbol - it is to fool gettext and force these strings to
1653 * be different for it. First symbol is skipped while building a prompt.
1654 * (I don't use spaces around the words, because someday they could be
1655 * dropped, when widgets get smarter)
1657 static char *op_names1[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1658 #define FMD_XLEN 64
1660 int fmd_xlen = FMD_XLEN;
1663 * These are formats for building a prompt. Parts encoded as follows:
1664 * %o - operation from op_names1
1665 * %f - file/files or files/directories, as appropriate
1666 * %m - "with source mask" or question mark for delete
1667 * %s - source name (truncated)
1668 * %d - number of marked files
1669 * %e - "to:" or question mark for delete
1671 * xgettext:no-c-format */
1672 static char *one_format = N_("%o %f \"%s\"%m");
1673 /* xgettext:no-c-format */
1674 static char *many_format = N_("%o %d %f%m");
1675 static char *prompt_parts[] = {
1676 N_("file"), N_("files"), N_("directory"), N_("directories"),
1677 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1681 * Generate user prompt for panel operation.
1682 * single_source is the name if the source entry or NULL for multiple
1683 * entries.
1684 * src_stat is only used when single_source is not NULL.
1686 static void
1687 panel_operate_generate_prompt (const WPanel *panel, const int operation,
1688 const char *single_source,
1689 const struct stat *src_stat)
1691 register char *sp, *cp;
1692 register int i;
1693 char format_string[BUF_MEDIUM];
1694 char *dp = format_string;
1696 #ifdef ENABLE_NLS
1697 static int i18n_flag = 0;
1698 if (!i18n_flag) {
1699 fmd_init_i18n (FALSE); /* to get proper fmd_xlen */
1701 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1702 op_names1[i] = _(op_names1[i]);
1704 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1705 prompt_parts[i] = _(prompt_parts[i]);
1707 one_format = _(one_format);
1708 many_format = _(many_format);
1709 i18n_flag = 1;
1711 #endif /* ENABLE_NLS */
1713 sp = single_source ? one_format : many_format;
1715 while (*sp) {
1716 switch (*sp) {
1717 case '%':
1718 cp = NULL;
1719 switch (sp[1]) {
1720 case 'o':
1721 cp = op_names1[operation] + 1;
1722 break;
1723 case 'm':
1724 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1725 break;
1726 case 'e':
1727 cp = operation == OP_DELETE ? "?" : prompt_parts[6];
1728 break;
1729 case 'f':
1730 if (single_source) {
1731 cp = S_ISDIR (src_stat->
1732 st_mode) ? prompt_parts[2] :
1733 prompt_parts[0];
1734 } else {
1735 cp = (panel->marked == panel->dirs_marked)
1736 ? prompt_parts[3]
1737 : (panel->dirs_marked ? prompt_parts[4]
1738 : prompt_parts[1]);
1740 break;
1741 default:
1742 *dp++ = *sp++;
1744 if (cp) {
1745 sp += 2;
1746 while (*cp)
1747 *dp++ = *cp++;
1749 break;
1750 default:
1751 *dp++ = *sp++;
1754 *dp = '\0';
1756 if (single_source) {
1757 i = fmd_xlen - strlen (format_string) - 4;
1758 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1759 name_trunc (single_source, i));
1760 } else {
1761 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1762 panel->marked);
1763 i = strlen (cmd_buf) + 6 - fmd_xlen;
1764 if (i > 0) {
1765 fmd_xlen += i;
1766 fmd_init_i18n (TRUE); /* to recalculate positions of child widgets */
1772 * panel_operate:
1774 * Performs one of the operations on the selection on the source_panel
1775 * (copy, delete, move).
1777 * Returns 1 if did change the directory
1778 * structure, Returns 0 if user aborted
1780 * force_single forces operation on the current entry and affects
1781 * default destination. Current filename is used as default.
1784 panel_operate (void *source_panel, FileOperation operation,
1785 int force_single)
1787 WPanel *panel = source_panel;
1788 #ifdef WITH_FULL_PATHS
1789 char *source_with_path = NULL;
1790 #else
1791 # define source_with_path source
1792 #endif /* !WITH_FULL_PATHS */
1793 char *source = NULL;
1794 char *dest = NULL;
1795 char *temp = NULL;
1796 char *save_cwd = NULL, *save_dest = NULL;
1797 int single_entry = (get_current_type () == view_tree)
1798 || (panel->marked <= 1) || force_single;
1799 struct stat src_stat, dst_stat;
1800 int i, value;
1801 FileOpContext *ctx;
1803 off_t count = 0;
1804 double bytes = 0;
1806 int dst_result;
1807 int do_bg = 0; /* do background operation? */
1809 free_linklist (&linklist);
1810 free_linklist (&dest_dirs);
1812 if (single_entry) {
1813 if (force_single) {
1814 source = selection (panel)->fname;
1815 src_stat = selection (panel)->st;
1816 } else {
1817 source = panel_get_file (panel, &src_stat);
1820 if (!strcmp (source, "..")) {
1821 message (1, MSG_ERROR, _(" Cannot operate on \"..\"! "));
1822 return 0;
1826 /* Generate confirmation prompt */
1827 panel_operate_generate_prompt (panel, operation, source, &src_stat);
1829 ctx = file_op_context_new (operation);
1831 /* Show confirmation dialog */
1832 if (operation == OP_DELETE && confirm_delete) {
1833 if (safe_delete)
1834 query_set_sel (1);
1836 i = query_dialog (_(op_names[operation]), cmd_buf, D_ERROR, 2,
1837 _("&Yes"), _("&No"));
1839 if (i != 0) {
1840 file_op_context_destroy (ctx);
1841 return 0;
1843 } else if (operation != OP_DELETE) {
1844 char *dest_dir;
1846 /* Forced single operations default to the original name */
1847 if (force_single)
1848 dest_dir = source;
1849 else if (get_other_type () == view_listing)
1850 dest_dir = other_panel->cwd;
1851 else
1852 dest_dir = panel->cwd;
1854 dest =
1855 file_mask_dialog (ctx, operation, cmd_buf, dest_dir,
1856 single_entry, &do_bg);
1857 if (!dest) {
1858 file_op_context_destroy (ctx);
1859 return 0;
1861 if (!*dest) {
1862 file_op_context_destroy (ctx);
1863 g_free (dest);
1864 return 0;
1867 #ifdef WITH_BACKGROUND
1868 /* Did the user select to do a background operation? */
1869 if (do_bg) {
1870 int v;
1872 v = do_background (ctx,
1873 g_strconcat (op_names[operation], ": ",
1874 panel->cwd, NULL));
1875 if (v == -1) {
1876 message (1, MSG_ERROR,
1877 _(" Sorry, I could not put the job in background "));
1880 /* If we are the parent */
1881 if (v == 1) {
1882 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
1883 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
1884 /* file_op_context_destroy (ctx); */
1885 return 0;
1888 #endif /* WITH_BACKGROUND */
1890 /* Initialize things */
1891 /* We do not want to trash cache every time file is
1892 created/touched. However, this will make our cache contain
1893 invalid data. */
1894 if (dest) {
1895 if (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1))
1896 save_dest = g_strdup (dest);
1898 if (panel->cwd) {
1899 if (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1))
1900 save_cwd = g_strdup (panel->cwd);
1903 /* Now, let's do the job */
1905 if (do_bg)
1906 ctx->ui = NULL;
1907 else
1908 file_op_context_create_ui (ctx, 1);
1910 /* This code is only called by the tree and panel code */
1911 if (single_entry) {
1912 /* We now have ETA in all cases */
1914 /* One file: FIXME mc_chdir will take user out of any vfs */
1915 if (operation != OP_COPY && get_current_type () == view_tree)
1916 mc_chdir (PATH_SEP_STR);
1918 /* The source and src_stat variables have been initialized before */
1919 #ifdef WITH_FULL_PATHS
1920 source_with_path = concat_dir_and_file (panel->cwd, source);
1921 #endif /* WITH_FULL_PATHS */
1923 if (operation == OP_DELETE) {
1924 if (S_ISDIR (src_stat.st_mode))
1925 value = erase_dir (ctx, source_with_path, &count, &bytes);
1926 else
1927 value =
1928 erase_file (ctx, source_with_path, &count, &bytes, 1);
1929 } else {
1930 temp = transform_source (ctx, source_with_path);
1932 if (temp == NULL) {
1933 value = transform_error;
1934 } else {
1935 temp = concat_dir_and_file (dest, temp);
1936 g_free (dest);
1937 dest = temp;
1938 temp = 0;
1940 switch (operation) {
1941 case OP_COPY:
1943 * we use file_mask_op_follow_links only with OP_COPY,
1945 (*ctx->stat_func) (source_with_path, &src_stat);
1947 if (S_ISDIR (src_stat.st_mode))
1948 value =
1949 copy_dir_dir (ctx, source_with_path, dest, 1,
1950 0, 0, 0, &count, &bytes);
1951 else
1952 value =
1953 copy_file_file (ctx, source_with_path, dest, 1,
1954 &count, &bytes, 1);
1955 break;
1957 case OP_MOVE:
1958 if (S_ISDIR (src_stat.st_mode))
1959 value =
1960 move_dir_dir (ctx, source_with_path, dest,
1961 &count, &bytes);
1962 else
1963 value =
1964 move_file_file (ctx, source_with_path, dest,
1965 &count, &bytes);
1966 break;
1968 default:
1969 /* Unknown file operation */
1970 abort ();
1973 } /* Copy or move operation */
1975 if ((value == FILE_CONT) && !force_single)
1976 unmark_files (panel);
1977 } else {
1978 /* Many files */
1979 /* Check destination for copy or move operation */
1980 if (operation != OP_DELETE) {
1981 retry_many_dst_stat:
1982 dst_result = mc_stat (dest, &dst_stat);
1983 if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)) {
1984 if (file_error
1985 (_(" Destination \"%s\" must be a directory \n %s "),
1986 dest) == FILE_RETRY)
1987 goto retry_many_dst_stat;
1988 goto clean_up;
1992 /* Initialize variables for progress bars */
1993 if (operation != OP_MOVE && verbose && file_op_compute_totals) {
1994 panel_compute_totals (panel, &ctx->progress_count,
1995 &ctx->progress_bytes);
1996 ctx->progress_totals_computed = 1;
1997 } else {
1998 ctx->progress_totals_computed = 0;
1999 ctx->progress_count = panel->marked;
2000 ctx->progress_bytes = panel->total;
2003 /* Loop for every file, perform the actual copy operation */
2004 for (i = 0; i < panel->count; i++) {
2005 if (!panel->dir.list[i].f.marked)
2006 continue; /* Skip the unmarked ones */
2008 source = panel->dir.list[i].fname;
2009 src_stat = panel->dir.list[i].st;
2011 #ifdef WITH_FULL_PATHS
2012 if (source_with_path)
2013 g_free (source_with_path);
2014 source_with_path = concat_dir_and_file (panel->cwd, source);
2015 #endif /* WITH_FULL_PATHS */
2017 if (operation == OP_DELETE) {
2018 if (S_ISDIR (src_stat.st_mode))
2019 value =
2020 erase_dir (ctx, source_with_path, &count, &bytes);
2021 else
2022 value =
2023 erase_file (ctx, source_with_path, &count, &bytes,
2025 } else {
2026 if (temp)
2027 g_free (temp);
2029 temp = transform_source (ctx, source_with_path);
2030 if (temp == NULL)
2031 value = transform_error;
2032 else {
2033 temp = concat_dir_and_file (dest, temp);
2035 switch (operation) {
2036 case OP_COPY:
2038 * we use file_mask_op_follow_links only with OP_COPY
2040 (*ctx->stat_func) (source_with_path, &src_stat);
2041 if (S_ISDIR (src_stat.st_mode))
2042 value =
2043 copy_dir_dir (ctx, source_with_path, temp,
2044 1, 0, 0, 0, &count, &bytes);
2045 else
2046 value =
2047 copy_file_file (ctx, source_with_path,
2048 temp, 1, &count, &bytes,
2050 free_linklist (&dest_dirs);
2051 break;
2053 case OP_MOVE:
2054 if (S_ISDIR (src_stat.st_mode))
2055 value =
2056 move_dir_dir (ctx, source_with_path, temp,
2057 &count, &bytes);
2058 else
2059 value =
2060 move_file_file (ctx, source_with_path,
2061 temp, &count, &bytes);
2062 break;
2064 default:
2065 /* Unknown file operation */
2066 abort ();
2069 } /* Copy or move operation */
2071 if (value == FILE_ABORT)
2072 goto clean_up;
2074 if (value == FILE_CONT)
2075 do_file_mark (panel, i, 0);
2077 if (file_progress_show_count (ctx, count, ctx->progress_count)
2078 == FILE_ABORT)
2079 goto clean_up;
2081 if (verbose
2082 && file_progress_show_bytes (ctx, bytes,
2083 ctx->progress_bytes) ==
2084 FILE_ABORT)
2085 goto clean_up;
2087 if (operation != OP_DELETE && verbose
2088 && file_progress_show (ctx, 0, 0) == FILE_ABORT)
2089 goto clean_up;
2091 mc_refresh ();
2092 } /* Loop for every file */
2093 } /* Many entries */
2094 clean_up:
2095 /* Clean up */
2097 if (save_cwd) {
2098 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2099 g_free (save_cwd);
2101 if (save_dest) {
2102 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2103 g_free (save_dest);
2106 free_linklist (&linklist);
2107 free_linklist (&dest_dirs);
2108 #ifdef WITH_FULL_PATHS
2109 if (source_with_path)
2110 g_free (source_with_path);
2111 #endif /* WITH_FULL_PATHS */
2113 if (dest)
2114 g_free (dest);
2116 if (temp)
2117 g_free (temp);
2119 if (ctx->dest_mask) {
2120 g_free (ctx->dest_mask);
2121 ctx->dest_mask = NULL;
2123 #ifdef WITH_BACKGROUND
2124 /* Let our parent know we are saying bye bye */
2125 if (we_are_background) {
2126 vfs_shut ();
2127 _exit (0);
2129 #endif /* WITH_BACKGROUND */
2131 file_op_context_destroy (ctx);
2132 return 1;
2135 /* }}} */
2137 /* {{{ Query/status report routines */
2139 static int
2140 real_do_file_error (enum OperationMode mode, char *error)
2142 int result;
2143 char *msg;
2145 msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
2146 result =
2147 query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"),
2148 _("&Abort"));
2150 switch (result) {
2151 case 0:
2152 do_refresh ();
2153 return FILE_SKIP;
2155 case 1:
2156 do_refresh ();
2157 return FILE_RETRY;
2159 case 2:
2160 default:
2161 return FILE_ABORT;
2165 /* Report error with one file */
2167 file_error (char *format, char *file)
2169 g_snprintf (cmd_buf, sizeof (cmd_buf), format,
2170 name_trunc (file, 30), unix_error_string (errno));
2172 return do_file_error (cmd_buf);
2175 /* Report error with two files */
2176 static int
2177 files_error (const char *format, const char *file1, const char *file2)
2179 char nfile1[16];
2180 char nfile2[16];
2182 strcpy (nfile1, name_trunc (file1, 15));
2183 strcpy (nfile2, name_trunc (file2, 15));
2185 g_snprintf (cmd_buf, sizeof (cmd_buf), format, nfile1, nfile2,
2186 unix_error_string (errno));
2188 return do_file_error (cmd_buf);
2191 static int
2192 real_query_recursive (FileOpContext *ctx, enum OperationMode mode, char *s)
2194 gchar *text;
2196 if (ctx->recursive_result < RECURSIVE_ALWAYS) {
2197 char *msg =
2198 mode ==
2199 Foreground ?
2200 _("\n Directory not empty. \n"
2201 " Delete it recursively? ")
2202 : _("\n Background process: Directory not empty \n"
2203 " Delete it recursively? ");
2204 text = g_strconcat (_(" Delete: "), name_trunc (s, 30), " ", NULL);
2206 if (safe_delete)
2207 query_set_sel (1);
2208 ctx->recursive_result = query_dialog (text, msg, D_ERROR, 5,
2209 _("&Yes"), _("&No"),
2210 _("A&ll"), _("Non&e"),
2211 _("&Abort"));
2213 if (ctx->recursive_result != RECURSIVE_ABORT)
2214 do_refresh ();
2215 g_free (text);
2218 switch (ctx->recursive_result) {
2219 case RECURSIVE_YES:
2220 case RECURSIVE_ALWAYS:
2221 return FILE_CONT;
2223 case RECURSIVE_NO:
2224 case RECURSIVE_NEVER:
2225 return FILE_SKIP;
2227 case RECURSIVE_ABORT:
2229 default:
2230 return FILE_ABORT;
2234 #ifdef WITH_BACKGROUND
2235 static int
2236 do_file_error (char *str)
2238 if (we_are_background)
2239 return parent_call (real_do_file_error, NULL, 1, strlen (str),
2240 str);
2241 else
2242 return real_do_file_error (Foreground, str);
2245 static int
2246 query_recursive (FileOpContext *ctx, char *s)
2248 if (we_are_background)
2249 return parent_call (real_query_recursive, ctx, 1, strlen (s), s);
2250 else
2251 return real_query_recursive (ctx, Foreground, s);
2254 static int
2255 query_replace (FileOpContext *ctx, char *destname, struct stat *_s_stat,
2256 struct stat *_d_stat)
2258 if (we_are_background)
2259 return parent_call ((void *) file_progress_real_query_replace,
2260 ctx,
2262 strlen (destname), destname,
2263 sizeof (struct stat), _s_stat,
2264 sizeof (struct stat), _d_stat);
2265 else
2266 return file_progress_real_query_replace (ctx, Foreground, destname,
2267 _s_stat, _d_stat);
2270 #else
2271 static int
2272 do_file_error (char *str)
2274 return real_do_file_error (Foreground, str);
2277 static int
2278 query_recursive (FileOpContext *ctx, char *s)
2280 return real_query_recursive (ctx, Foreground, s);
2283 static int
2284 query_replace (FileOpContext *ctx, char *destname, struct stat *_s_stat,
2285 struct stat *_d_stat)
2287 return file_progress_real_query_replace (ctx, Foreground, destname,
2288 _s_stat, _d_stat);
2291 #endif /* !WITH_BACKGROUND */
2294 Cause emacs to enter folding mode for this file:
2295 Local variables:
2296 end: