Merge branch '1617_history_fix'
[midnight-commander.git] / src / file.c
blob55e167e6512efdd391589a4c476760f2aab2194b
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 /** \file file.c
41 * \brief Source: file management
44 /* {{{ Include files */
46 #include <config.h>
48 #include <ctype.h>
49 #include <errno.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <unistd.h>
56 #include <fcntl.h>
57 #include <sys/time.h>
59 #include "global.h"
61 #include "../src/tty/tty.h"
62 #include "../src/tty/key.h"
64 #include "../src/search/search.h"
66 #include "setup.h"
67 #include "dialog.h"
68 #include "widget.h"
69 #include "main.h"
70 #include "layout.h"
71 #include "widget.h"
72 #include "wtools.h"
73 #include "background.h" /* we_are_background */
74 #include "../src/strescape.h"
75 #include "strutil.h"
77 /* Needed for current_panel, other_panel and WTree */
78 #include "dir.h"
79 #include "panel.h"
80 #include "file.h"
81 #include "filegui.h"
82 #include "tree.h"
83 #include "../vfs/vfs-impl.h"
85 /* }}} */
87 /* Hack: the vfs code should not rely on this */
88 #define WITH_FULL_PATHS 1
90 int verbose = 1;
93 * Whether the Midnight Commander tries to provide more
94 * information about copy/move sizes and bytes transfered
95 * at the expense of some speed
97 int file_op_compute_totals = 1;
99 /* This is a hard link cache */
100 struct link {
101 struct link *next;
102 struct vfs_class *vfs;
103 dev_t dev;
104 ino_t ino;
105 short linkcount;
106 mode_t st_mode;
107 char name[1];
110 /* the hard link cache */
111 static struct link *linklist = NULL;
113 /* the files-to-be-erased list */
114 static struct link *erase_list;
117 * In copy_dir_dir we use two additional single linked lists: The first -
118 * variable name `parent_dirs' - holds information about already copied
119 * directories and is used to detect cyclic symbolic links.
120 * The second (`dest_dirs' below) holds information about just created
121 * target directories and is used to detect when an directory is copied
122 * into itself (we don't want to copy infinitly).
123 * Both lists don't use the linkcount and name structure members of struct
124 * link.
126 static struct link *dest_dirs = NULL;
128 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
129 const char *op_names[3] = {
130 N_("DialogTitle|Copy"),
131 N_("DialogTitle|Move"),
132 N_("DialogTitle|Delete")
135 /* }}} */
137 static FileProgressStatus query_replace (FileOpContext * ctx, const char *destname,
138 struct stat *_s_stat, struct stat *_d_stat);
139 static FileProgressStatus query_recursive (FileOpContext * ctx, const char *s);
140 static FileProgressStatus do_file_error (const char *str);
141 static FileProgressStatus erase_dir_iff_empty (FileOpContext *ctx, const char *s);
142 static FileProgressStatus erase_file (FileOpContext *ctx, const char *s,
143 off_t *progress_count, double *progress_bytes,
144 int is_toplevel_file);
145 static FileProgressStatus files_error (const char *format, const char *file1,
146 const char *file2);
148 static FileProgressStatus transform_error = FILE_CONT;
150 static char *
151 transform_source (FileOpContext *ctx, const char *source)
153 char *s, *q;
154 char *fnsource;
156 s = g_strdup (source);
158 /* We remove \n from the filename since regex routines would use \n as an anchor */
159 /* this is just to be allowed to maniupulate file names with \n on it */
160 for (q = s; *q != '\0'; q++)
161 if (*q == '\n')
162 *q = ' ';
164 fnsource = (char *) x_basename (s);
166 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
167 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
168 else {
169 q = NULL;
170 transform_error = FILE_SKIP;
173 g_free (s);
174 return q;
177 static void
178 free_linklist (struct link **linklist)
180 struct link *lp, *lp2;
182 for (lp = *linklist; lp != NULL; lp = lp2) {
183 lp2 = lp->next;
184 g_free (lp);
186 *linklist = NULL;
189 static int
190 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
192 ino_t ino = sb->st_ino;
193 dev_t dev = sb->st_dev;
194 #ifdef USE_VFS
195 struct vfs_class *vfs = vfs_get_class (path);
196 #endif /* USE_VFS */
198 while (lp) {
199 #ifdef USE_VFS
200 if (lp->vfs == vfs)
201 #endif /* USE_VFS */
202 if (lp->ino == ino && lp->dev == dev)
203 return 1;
204 lp = lp->next;
206 return 0;
210 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
211 * and a hardlink was succesfully made
213 static int
214 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
216 struct link *lp;
217 struct vfs_class *my_vfs = vfs_get_class (src_name);
218 ino_t ino = pstat->st_ino;
219 dev_t dev = pstat->st_dev;
220 struct stat link_stat;
221 const char *p;
223 if (vfs_file_class_flags (src_name) & VFSF_NOLINKS)
224 return 0;
226 for (lp = linklist; lp != NULL; lp = lp->next)
227 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev) {
228 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
229 && link_stat.st_dev == dev
230 && vfs_get_class (lp->name) == my_vfs) {
231 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
232 was copied to */
233 if (vfs_get_class (dst_name) == vfs_get_class (p)) {
234 if (!mc_stat (p, &link_stat)) {
235 if (!mc_link (p, dst_name))
236 return 1;
240 message (D_ERROR, MSG_ERROR, _(" Cannot make the hardlink "));
241 return 0;
243 lp = (struct link *) g_malloc (sizeof (struct link) + strlen (src_name)
244 + strlen (dst_name) + 1);
245 if (lp) {
246 char *lpdstname;
247 lp->vfs = my_vfs;
248 lp->ino = ino;
249 lp->dev = dev;
250 strcpy (lp->name, src_name);
251 lpdstname = lp->name + strlen(lp->name) + 1;
252 strcpy (lpdstname, dst_name);
253 lp->next = linklist;
254 linklist = lp;
256 return 0;
260 * Duplicate the contents of the symbolic link src_path in dst_path.
261 * Try to make a stable symlink if the option "stable symlink" was
262 * set in the file mask dialog.
263 * If dst_path is an existing symlink it will be deleted silently
264 * (upper levels take already care of existing files at dst_path).
266 static FileProgressStatus
267 make_symlink (FileOpContext *ctx, const char *src_path, const char *dst_path)
269 char link_target[MC_MAXPATHLEN];
270 int len;
271 FileProgressStatus return_status;
272 struct stat sb;
273 int dst_is_symlink;
275 if (mc_lstat (dst_path, &sb) == 0 && S_ISLNK (sb.st_mode))
276 dst_is_symlink = 1;
277 else
278 dst_is_symlink = 0;
280 retry_src_readlink:
281 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
282 if (len < 0) {
283 return_status =
284 file_error (_(" Cannot read source link \"%s\" \n %s "),
285 src_path);
286 if (return_status == FILE_RETRY)
287 goto retry_src_readlink;
288 return return_status;
290 link_target[len] = 0;
292 if (ctx->stable_symlinks)
293 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path)) {
294 message (D_ERROR, MSG_ERROR,
295 _(" Cannot make stable symlinks across "
296 "non-local filesystems: \n\n"
297 " Option Stable Symlinks will be disabled "));
298 ctx->stable_symlinks = 0;
301 if (ctx->stable_symlinks && *link_target != PATH_SEP) {
302 char *p, *q, *s;
304 const char *r = strrchr (src_path, PATH_SEP);
306 if (r) {
307 p = g_strndup (src_path, r - src_path + 1);
308 if (*dst_path == PATH_SEP)
309 q = g_strdup (dst_path);
310 else
311 q = g_strconcat (p, dst_path, (char *) NULL);
312 s = strrchr (q, PATH_SEP);
313 if (s) {
314 s[1] = 0;
315 s = g_strconcat (p, link_target, (char *) NULL);
316 g_free (p);
317 g_strlcpy (link_target, s, sizeof (link_target));
318 g_free (s);
319 s = diff_two_paths (q, link_target);
320 if (s) {
321 g_strlcpy (link_target, s, sizeof (link_target));
322 g_free (s);
324 } else
325 g_free (p);
326 g_free (q);
329 retry_dst_symlink:
330 if (mc_symlink (link_target, dst_path) == 0)
331 /* Success */
332 return FILE_CONT;
334 * if dst_exists, it is obvious that this had failed.
335 * We can delete the old symlink and try again...
337 if (dst_is_symlink) {
338 if (!mc_unlink (dst_path))
339 if (mc_symlink (link_target, dst_path) == 0)
340 /* Success */
341 return FILE_CONT;
343 return_status =
344 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
345 dst_path);
346 if (return_status == FILE_RETRY)
347 goto retry_dst_symlink;
348 return return_status;
351 static int
352 progress_update_one (FileOpContext *ctx,
353 off_t *progress_count,
354 double *progress_bytes, off_t add, int is_toplevel_file)
356 int ret;
358 if (is_toplevel_file || ctx->progress_totals_computed) {
359 (*progress_count)++;
360 (*progress_bytes) += add;
363 /* Apply some heuristic here to not call the update stuff very often */
364 ret =
365 file_progress_show_count (ctx, *progress_count,
366 ctx->progress_count);
367 if (ret != FILE_CONT)
368 return ret;
369 ret =
370 file_progress_show_bytes (ctx, *progress_bytes,
371 ctx->progress_bytes);
373 return ret;
376 /* Status of the destination file */
377 enum {
378 DEST_NONE, /* Not created */
379 DEST_SHORT, /* Created, not fully copied */
380 DEST_FULL /* Created, fully copied */
383 static FileProgressStatus
384 warn_same_file (const char *fmt, const char *a, const char *b)
386 char *msg;
387 int result = 0;
388 msg = g_strdup_printf (fmt, a, b);
389 result = query_dialog (MSG_ERROR, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
390 g_free(msg);
391 do_refresh ();
392 if ( result ) { /* 1 == Abort */
393 return FILE_ABORT;
394 } else {
395 return FILE_SKIP;
399 FileProgressStatus
400 copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path,
401 int ask_overwrite, off_t *progress_count,
402 double *progress_bytes, int is_toplevel_file)
404 uid_t src_uid = (uid_t) - 1;
405 gid_t src_gid = (gid_t) - 1;
407 char *buf = NULL;
408 int buf_size = BUF_8K;
409 int src_desc, dest_desc = -1;
410 int n_read, n_written;
411 mode_t src_mode = 0; /* The mode of the source file */
412 struct stat sb, sb2;
413 struct utimbuf utb;
414 int dst_exists = 0, appending = 0;
415 off_t n_read_total = 0, file_size = -1;
416 FileProgressStatus return_status, temp_status;
417 struct timeval tv_transfer_start;
418 int dst_status = DEST_NONE; /* 1 if the file is not fully copied */
419 int open_flags;
421 /* FIXME: We should not be using global variables! */
422 ctx->do_reget = 0;
423 return_status = FILE_RETRY;
425 if (file_progress_show_source (ctx, src_path) == FILE_ABORT ||
426 file_progress_show_target (ctx, dst_path) == FILE_ABORT)
427 return FILE_ABORT;
429 mc_refresh ();
431 while (mc_stat (dst_path, &sb2) == 0) {
432 if (S_ISDIR (sb2.st_mode)) {
433 return_status =
434 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
435 dst_path);
436 if (return_status == FILE_RETRY)
437 continue;
438 return return_status;
440 dst_exists = 1;
441 break;
444 while ((*ctx->stat_func) (src_path, &sb)) {
445 return_status =
446 file_error (_(" Cannot stat source file \"%s\" \n %s "),
447 src_path);
448 if (return_status != FILE_RETRY)
449 return return_status;
452 if (dst_exists) {
453 /* Destination already exists */
454 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
455 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "),
456 src_path, dst_path);
457 /* Should we replace destination? */
458 if (ask_overwrite) {
459 ctx->do_reget = 0;
460 return_status = query_replace (ctx, dst_path, &sb, &sb2);
461 if (return_status != FILE_CONT)
462 return return_status;
466 if (!ctx->do_append) {
467 /* Check the hardlinks */
468 if (!ctx->follow_links && sb.st_nlink > 1 &&
469 check_hardlinks (src_path, dst_path, &sb) == 1) {
470 /* We have made a hardlink - no more processing is necessary */
471 return FILE_CONT;
474 if (S_ISLNK (sb.st_mode))
475 return make_symlink (ctx, src_path, dst_path);
477 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
478 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) ||
479 S_ISSOCK (sb.st_mode)) {
480 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill,
481 sb.st_rdev) < 0) {
482 return_status = file_error (
483 _(" Cannot create special file \"%s\" \n %s "), dst_path);
484 if (return_status == FILE_RETRY)
485 continue;
486 return return_status;
488 /* Success */
490 while (ctx->preserve_uidgid
491 && mc_chown (dst_path, sb.st_uid, sb.st_gid)) {
492 temp_status = file_error (
493 _(" Cannot chown target file \"%s\" \n %s "), dst_path);
494 if (temp_status == FILE_RETRY)
495 continue;
496 return temp_status;
498 while (ctx->preserve &&
499 mc_chmod (dst_path, sb.st_mode & ctx->umask_kill)) {
500 temp_status = file_error (
501 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
502 if (temp_status == FILE_RETRY)
503 continue;
504 return temp_status;
506 return FILE_CONT;
510 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
512 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0) {
513 return_status = file_error (
514 _(" Cannot open source file \"%s\" \n %s "), src_path);
515 if (return_status == FILE_RETRY)
516 continue;
517 ctx->do_append = 0;
518 return return_status;
521 if (ctx->do_reget) {
522 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget) {
523 message (D_ERROR, _("Warning"),
524 _(" Reget failed, about to overwrite file "));
525 ctx->do_reget = ctx->do_append = 0;
529 while (mc_fstat (src_desc, &sb)) {
530 return_status = file_error (
531 _(" Cannot fstat source file \"%s\" \n %s "), src_path);
532 if (return_status == FILE_RETRY)
533 continue;
534 ctx->do_append = 0;
535 goto ret;
537 src_mode = sb.st_mode;
538 src_uid = sb.st_uid;
539 src_gid = sb.st_gid;
540 utb.actime = sb.st_atime;
541 utb.modtime = sb.st_mtime;
542 file_size = sb.st_size;
544 open_flags = O_WRONLY;
545 if (dst_exists != 0) {
546 if (ctx->do_append != 0)
547 open_flags |= O_APPEND;
548 else
549 open_flags |= O_CREAT | O_TRUNC;
550 } else {
551 open_flags |= O_CREAT | O_EXCL;
553 while ((dest_desc = mc_open (dst_path, open_flags, src_mode)) < 0) {
554 if (errno == EEXIST) {
555 goto ret;
557 return_status = file_error (
558 _(" Cannot create target file \"%s\" \n %s "), dst_path);
559 if (return_status == FILE_RETRY)
560 continue;
561 ctx->do_append = 0;
562 goto ret;
564 dst_status = DEST_SHORT; /* file opened, but not fully copied */
566 appending = ctx->do_append;
567 ctx->do_append = 0;
569 /* Find out the optimal buffer size. */
570 while (mc_fstat (dest_desc, &sb)) {
571 return_status = file_error (
572 _(" Cannot fstat target file \"%s\" \n %s "), dst_path);
573 if (return_status == FILE_RETRY)
574 continue;
575 goto ret;
577 buf = g_malloc (buf_size);
579 ctx->eta_secs = 0.0;
580 ctx->bps = 0;
582 return_status = file_progress_show (ctx, 0, file_size);
584 mc_refresh ();
586 if (return_status != FILE_CONT)
587 goto ret;
590 struct timeval tv_current, tv_last_update, tv_last_input;
591 int secs, update_secs;
592 long dt;
593 const char *stalled_msg;
595 tv_last_update = tv_transfer_start;
597 for (;;) {
598 /* src_read */
599 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
600 n_read = -1;
601 else
602 while ((n_read = mc_read (src_desc, buf, buf_size)) < 0) {
603 return_status = file_error (
604 _(" Cannot read source file \"%s\" \n %s "), src_path);
605 if (return_status == FILE_RETRY)
606 continue;
607 goto ret;
609 if (n_read == 0)
610 break;
612 gettimeofday (&tv_current, NULL);
614 if (n_read > 0) {
615 char *t = buf;
616 n_read_total += n_read;
618 /* Windows NT ftp servers report that files have no
619 * permissions: -------, so if we happen to have actually
620 * read something, we should fix the permissions.
622 if (!(src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)))
623 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
624 gettimeofday (&tv_last_input, NULL);
626 /* dst_write */
627 while ((n_written =
628 mc_write (dest_desc, t, n_read)) < n_read) {
629 if (n_written > 0) {
630 n_read -= n_written;
631 t += n_written;
632 continue;
634 return_status =
635 file_error (_(" Cannot write target file \"%s\" \n %s "),
636 dst_path);
637 if (return_status != FILE_RETRY)
638 goto ret;
642 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
643 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
644 if (secs > 2) {
645 rotate_dash ();
646 tv_last_update = tv_current;
649 /* 2. Check for a stalled condition */
650 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
651 stalled_msg = "";
652 if (update_secs > 4) {
653 stalled_msg = _("(stalled)");
656 /* 3. Compute ETA */
657 if (secs > 2) {
658 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
660 if (n_read_total) {
661 ctx->eta_secs =
662 ((dt / (double) n_read_total) * file_size) - dt;
663 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
664 } else
665 ctx->eta_secs = 0.0;
668 /* 4. Compute BPS rate */
669 if (secs > 2) {
670 ctx->bps_time =
671 (tv_current.tv_sec - tv_transfer_start.tv_sec);
672 if (ctx->bps_time < 1)
673 ctx->bps_time = 1;
674 ctx->bps = n_read_total / ctx->bps_time;
677 file_progress_set_stalled_label (ctx, stalled_msg);
678 return_status = file_progress_show_bytes (ctx, *progress_bytes +
679 n_read_total + ctx->do_reget, ctx->progress_bytes);
680 if (return_status == FILE_CONT) {
681 return_status =
682 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size);
684 mc_refresh ();
685 if (return_status != FILE_CONT)
686 goto ret;
690 dst_status = DEST_FULL; /* copy successful, don't remove target file */
692 ret:
693 g_free (buf);
695 while (src_desc != -1 && mc_close (src_desc) < 0) {
696 temp_status = file_error (
697 _(" Cannot close source file \"%s\" \n %s "), src_path);
698 if (temp_status == FILE_RETRY)
699 continue;
700 if (temp_status == FILE_ABORT)
701 return_status = temp_status;
702 break;
705 while (dest_desc != -1 && mc_close (dest_desc) < 0) {
706 temp_status = file_error (
707 _(" Cannot close target file \"%s\" \n %s "), dst_path);
708 if (temp_status == FILE_RETRY)
709 continue;
710 return_status = temp_status;
711 break;
714 if (dst_status == DEST_SHORT) {
715 /* Remove short file */
716 int result;
717 result = query_dialog (Q_("DialogTitle|Copy"),
718 _("Incomplete file was retrieved. Keep it?"),
719 D_ERROR, 2, _("&Delete"), _("&Keep"));
720 if (!result)
721 mc_unlink (dst_path);
722 } else if (dst_status == DEST_FULL) {
723 /* Copy has succeeded */
724 if (!appending && ctx->preserve_uidgid) {
725 while (mc_chown (dst_path, src_uid, src_gid)) {
726 temp_status = file_error (
727 _(" Cannot chown target file \"%s\" \n %s "), dst_path);
728 if (temp_status == FILE_RETRY)
729 continue;
730 return_status = temp_status;
731 break;
735 if (!appending) {
736 if (ctx->preserve){
737 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill))) {
738 temp_status = file_error (
739 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
740 if (temp_status != FILE_RETRY) {
741 return_status = temp_status;
742 break;
745 } else {
746 src_mode = umask(-1);
747 umask(src_mode);
748 src_mode = 0100666 & ~src_mode;
749 mc_chmod (dst_path, (src_mode & ctx->umask_kill));
751 mc_utime (dst_path, &utb);
755 if (return_status == FILE_CONT)
756 return_status =
757 progress_update_one (ctx, progress_count, progress_bytes,
758 file_size, is_toplevel_file);
760 return return_status;
764 * I think these copy_*_* functions should have a return type.
765 * anyway, this function *must* have two directories as arguments.
767 /* FIXME: This function needs to check the return values of the
768 function calls */
769 FileProgressStatus
770 copy_dir_dir (FileOpContext *ctx, const char *s, const char *_d, int toplevel,
771 int move_over, int delete, struct link *parent_dirs,
772 off_t *progress_count, double *progress_bytes)
774 struct dirent *next;
775 struct stat buf, cbuf;
776 DIR *reading;
777 char *dest_dir = NULL;
778 FileProgressStatus return_status = FILE_CONT;
779 struct utimbuf utb;
780 struct link *lp;
781 char *d;
783 d = strutils_shell_unescape (_d);
785 /* First get the mode of the source dir */
786 retry_src_stat:
787 if ((*ctx->stat_func) (s, &cbuf)) {
788 return_status =
789 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s);
790 if (return_status == FILE_RETRY)
791 goto retry_src_stat;
792 goto ret_fast;
795 if (is_in_linklist (dest_dirs, s, &cbuf)) {
796 /* Don't copy a directory we created before (we don't want to copy
797 infinitely if a directory is copied into itself) */
798 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
799 return_status = FILE_CONT;
800 goto ret_fast;
803 /* Hmm, hardlink to directory??? - Norbert */
804 /* FIXME: In this step we should do something
805 in case the destination already exist */
806 /* Check the hardlinks */
807 if (ctx->preserve && cbuf.st_nlink > 1
808 && check_hardlinks (s, d, &cbuf) == 1) {
809 /* We have made a hardlink - no more processing is necessary */
810 goto ret_fast;
813 if (!S_ISDIR (cbuf.st_mode)) {
814 return_status =
815 file_error (_(" Source \"%s\" is not a directory \n %s "), s);
816 if (return_status == FILE_RETRY)
817 goto retry_src_stat;
818 goto ret_fast;
821 if (is_in_linklist (parent_dirs, s, &cbuf)) {
822 /* we found a cyclic symbolic link */
823 message (D_ERROR, MSG_ERROR,
824 _(" Cannot copy cyclic symbolic link \n `%s' "), s);
825 return_status = FILE_SKIP;
826 goto ret_fast;
829 lp = g_new (struct link, 1);
830 lp->vfs = vfs_get_class (s);
831 lp->ino = cbuf.st_ino;
832 lp->dev = cbuf.st_dev;
833 lp->next = parent_dirs;
834 parent_dirs = lp;
836 retry_dst_stat:
837 /* Now, check if the dest dir exists, if not, create it. */
838 if (mc_stat (d, &buf)) {
839 /* Here the dir doesn't exist : make it ! */
840 if (move_over) {
841 if (mc_rename (s, d) == 0) {
842 return_status = FILE_CONT;
843 goto ret;
846 dest_dir = d;
847 d = NULL;
848 } else {
850 * If the destination directory exists, we want to copy the whole
851 * directory, but we only want this to happen once.
853 * Escape sequences added to the * to compiler warnings.
854 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
855 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
857 if (!S_ISDIR (buf.st_mode)) {
858 return_status = file_error(
859 _(" Destination \"%s\" must be a directory \n %s "), d);
860 if (return_status == FILE_RETRY)
861 goto retry_dst_stat;
862 goto ret;
864 /* Dive into subdir if exists */
865 if (toplevel && ctx->dive_into_subdirs) {
866 dest_dir = concat_dir_and_file (d, x_basename (s));
867 } else {
868 dest_dir = d;
869 d = NULL;
870 goto dont_mkdir;
873 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU)) {
874 return_status = file_error (
875 _(" Cannot create target directory \"%s\" \n %s "), dest_dir);
876 if (return_status != FILE_RETRY)
877 goto ret;
880 lp = g_new (struct link, 1);
881 mc_stat (dest_dir, &buf);
882 lp->vfs = vfs_get_class (dest_dir);
883 lp->ino = buf.st_ino;
884 lp->dev = buf.st_dev;
885 lp->next = dest_dirs;
886 dest_dirs = lp;
888 if (ctx->preserve_uidgid) {
889 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid)) {
890 return_status = file_error (
891 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir);
892 if (return_status != FILE_RETRY)
893 goto ret;
897 dont_mkdir:
898 /* open the source dir for reading */
899 reading = mc_opendir (s);
900 if (reading == NULL)
901 goto ret;
903 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT) {
904 char *path;
906 * Now, we don't want '.' and '..' to be created / copied at any time
908 if (!strcmp (next->d_name, "."))
909 continue;
910 if (!strcmp (next->d_name, ".."))
911 continue;
913 /* get the filename and add it to the src directory */
914 path = concat_dir_and_file (s, next->d_name);
916 (*ctx->stat_func) (path, &buf);
917 if (S_ISDIR (buf.st_mode)) {
918 char *mdpath;
920 mdpath = concat_dir_and_file (dest_dir, next->d_name);
922 * From here, we just intend to recursively copy subdirs, not
923 * the double functionality of copying different when the target
924 * dir already exists. So, we give the recursive call the flag 0
925 * meaning no toplevel.
927 return_status = copy_dir_dir (ctx, path, mdpath, 0, 0, delete,
928 parent_dirs, progress_count, progress_bytes);
929 g_free (mdpath);
930 } else {
931 char *dest_file;
933 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
934 return_status = copy_file_file (ctx, path, dest_file, 1,
935 progress_count, progress_bytes, 0);
936 g_free (dest_file);
938 if (delete && return_status == FILE_CONT) {
939 if (ctx->erase_at_end) {
940 static struct link *tail;
941 size_t len = strlen (path);
942 lp = g_malloc (sizeof (struct link) + len);
943 strncpy (lp->name, path, len + 1);
944 lp->st_mode = buf.st_mode;
945 lp->next = NULL;
946 if (erase_list != NULL) {
947 tail->next = lp;
948 tail = lp;
949 } else
950 erase_list = tail = lp;
951 } else {
952 if (S_ISDIR (buf.st_mode)) {
953 return_status = erase_dir_iff_empty (ctx, path);
954 } else
955 return_status = erase_file (ctx, path, 0, 0, 0);
958 g_free (path);
960 mc_closedir (reading);
962 if (ctx->preserve) {
963 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
964 utb.actime = cbuf.st_atime;
965 utb.modtime = cbuf.st_mtime;
966 mc_utime (dest_dir, &utb);
967 } else {
968 cbuf.st_mode = umask(-1);
969 umask(cbuf.st_mode);
970 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
971 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
974 ret:
975 g_free (dest_dir);
976 g_free (parent_dirs);
977 ret_fast:
978 g_free (d);
979 return return_status;
982 /* }}} */
984 /* {{{ Move routines */
986 static FileProgressStatus
987 move_file_file (FileOpContext *ctx, const char *s, const char *d,
988 off_t *progress_count, double *progress_bytes)
990 struct stat src_stats, dst_stats;
991 FileProgressStatus return_status = FILE_CONT;
992 gboolean copy_done = FALSE;
994 if (file_progress_show_source (ctx, s) == FILE_ABORT
995 || file_progress_show_target (ctx, d) == FILE_ABORT)
996 return FILE_ABORT;
998 mc_refresh ();
1000 while (mc_lstat (s, &src_stats) != 0) {
1001 /* Source doesn't exist */
1002 return_status =
1003 file_error (_(" Cannot stat file \"%s\" \n %s "), s);
1004 if (return_status != FILE_RETRY)
1005 return return_status;
1008 if (mc_lstat (d, &dst_stats) == 0) {
1009 if (src_stats.st_dev == dst_stats.st_dev
1010 && src_stats.st_ino == dst_stats.st_ino)
1011 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "), s, d);
1013 if (S_ISDIR (dst_stats.st_mode)) {
1014 message (D_ERROR, MSG_ERROR,
1015 _(" Cannot overwrite directory `%s' "), d);
1016 do_refresh ();
1017 return FILE_SKIP;
1020 if (confirm_overwrite) {
1021 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1022 if (return_status != FILE_CONT)
1023 return return_status;
1025 /* Ok to overwrite */
1028 if (!ctx->do_append) {
1029 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks) {
1030 if ((return_status = make_symlink (ctx, s, d)) == FILE_CONT) {
1031 goto retry_src_remove;
1032 } else
1033 return return_status;
1036 if (mc_rename (s, d) == 0) {
1037 return progress_update_one (ctx, progress_count,
1038 progress_bytes,
1039 src_stats.st_size, 1);
1042 #if 0
1043 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1044 one nfs to the same, but on the server it is on two different
1045 filesystems. Then nfs returns EIO instead of EXDEV.
1046 Hope it will not hurt if we always in case of error try to copy/delete. */
1047 else
1048 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1050 if (errno != EXDEV) {
1051 return_status =
1052 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s,
1054 if (return_status == FILE_RETRY)
1055 goto retry_rename;
1056 return return_status;
1058 #endif
1060 /* Failed because filesystem boundary -> copy the file instead */
1061 return_status =
1062 copy_file_file (ctx, s, d, 0, progress_count, progress_bytes, 1);
1063 if (return_status != FILE_CONT)
1064 return return_status;
1066 copy_done = TRUE;
1068 if ((return_status =
1069 file_progress_show_source (ctx, NULL)) != FILE_CONT
1070 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1071 return return_status;
1073 mc_refresh ();
1075 retry_src_remove:
1076 if (mc_unlink (s)) {
1077 return_status =
1078 file_error (_(" Cannot remove file \"%s\" \n %s "), s);
1079 if (return_status == FILE_RETRY)
1080 goto retry_src_remove;
1081 return return_status;
1084 if (!copy_done) {
1085 return_status = progress_update_one (ctx,
1086 progress_count,
1087 progress_bytes,
1088 src_stats.st_size, 1);
1091 return return_status;
1094 FileProgressStatus
1095 move_dir_dir (FileOpContext *ctx, const char *s, const char *d,
1096 off_t *progress_count, double *progress_bytes)
1098 struct stat sbuf, dbuf, destbuf;
1099 struct link *lp;
1100 char *destdir;
1101 FileProgressStatus return_status;
1102 gboolean move_over = FALSE;
1103 gboolean dstat_ok;
1105 if (file_progress_show_source (ctx, s) == FILE_ABORT ||
1106 file_progress_show_target (ctx, d) == FILE_ABORT)
1107 return FILE_ABORT;
1109 mc_refresh ();
1111 mc_stat (s, &sbuf);
1112 dstat_ok = (mc_stat (d, &dbuf) == 0);
1114 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
1115 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same directory "), s, d);
1117 if (!dstat_ok)
1118 destdir = g_strdup (d); /* destination doesn't exist */
1119 else if (!ctx->dive_into_subdirs) {
1120 destdir = g_strdup (d);
1121 move_over = TRUE;
1122 } else
1123 destdir = concat_dir_and_file (d, x_basename (s));
1125 /* Check if the user inputted an existing dir */
1126 retry_dst_stat:
1127 if (!mc_stat (destdir, &destbuf)) {
1128 if (move_over) {
1129 return_status = copy_dir_dir (ctx, s, destdir, 0, 1, 1, 0,
1130 progress_count, progress_bytes);
1132 if (return_status != FILE_CONT)
1133 goto ret;
1134 goto oktoret;
1135 } else {
1136 if (S_ISDIR (destbuf.st_mode))
1137 return_status =
1138 file_error (_
1139 (" Cannot overwrite directory \"%s\" %s "),
1140 destdir);
1141 else
1142 return_status =
1143 file_error (_(" Cannot overwrite file \"%s\" %s "),
1144 destdir);
1145 if (return_status == FILE_RETRY)
1146 goto retry_dst_stat;
1148 g_free (destdir);
1149 return return_status;
1152 retry_rename:
1153 if (mc_rename (s, destdir) == 0) {
1154 return_status = FILE_CONT;
1155 goto ret;
1158 if (errno != EXDEV) {
1159 return_status =
1160 files_error (_
1161 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1162 s, d);
1163 if (return_status == FILE_RETRY)
1164 goto retry_rename;
1165 goto ret;
1167 /* Failed because of filesystem boundary -> copy dir instead */
1168 return_status =
1169 copy_dir_dir (ctx, s, destdir, 0, 0, 1, 0, progress_count,
1170 progress_bytes);
1172 if (return_status != FILE_CONT)
1173 goto ret;
1174 oktoret:
1175 if ((return_status =
1176 file_progress_show_source (ctx, NULL)) != FILE_CONT
1177 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1178 goto ret;
1180 mc_refresh ();
1181 if (ctx->erase_at_end) {
1182 for (; erase_list && return_status != FILE_ABORT;) {
1183 if (S_ISDIR (erase_list->st_mode)) {
1184 return_status =
1185 erase_dir_iff_empty (ctx, erase_list->name);
1186 } else
1187 return_status =
1188 erase_file (ctx, erase_list->name, 0, 0, 0);
1189 lp = erase_list;
1190 erase_list = erase_list->next;
1191 g_free (lp);
1194 erase_dir_iff_empty (ctx, s);
1196 ret:
1197 g_free (destdir);
1198 while (erase_list) {
1199 lp = erase_list;
1200 erase_list = erase_list->next;
1201 g_free (lp);
1203 return return_status;
1206 /* }}} */
1208 /* {{{ Erase routines */
1209 /* Don't update progress status if progress_count==NULL */
1210 static FileProgressStatus
1211 erase_file (FileOpContext *ctx, const char *s, off_t *progress_count,
1212 double *progress_bytes, int is_toplevel_file)
1214 int return_status;
1215 struct stat buf;
1217 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1218 return FILE_ABORT;
1219 mc_refresh ();
1221 if (progress_count && mc_lstat (s, &buf)) {
1222 /* ignore, most likely the mc_unlink fails, too */
1223 buf.st_size = 0;
1226 while (mc_unlink (s)) {
1227 return_status =
1228 file_error (_(" Cannot delete file \"%s\" \n %s "), s);
1229 if (return_status != FILE_RETRY)
1230 return return_status;
1233 if (progress_count)
1234 return progress_update_one (ctx, progress_count, progress_bytes,
1235 buf.st_size, is_toplevel_file);
1236 else
1237 return FILE_CONT;
1240 static FileProgressStatus
1241 recursive_erase (FileOpContext *ctx, const char *s, off_t *progress_count,
1242 double *progress_bytes)
1244 struct dirent *next;
1245 struct stat buf;
1246 DIR *reading;
1247 char *path;
1248 FileProgressStatus return_status = FILE_CONT;
1250 if (!strcmp (s, ".."))
1251 return FILE_RETRY;
1253 reading = mc_opendir (s);
1255 if (!reading)
1256 return FILE_RETRY;
1258 while ((next = mc_readdir (reading)) && return_status == FILE_CONT) {
1259 if (!strcmp (next->d_name, "."))
1260 continue;
1261 if (!strcmp (next->d_name, ".."))
1262 continue;
1263 path = concat_dir_and_file (s, next->d_name);
1264 if (mc_lstat (path, &buf)) {
1265 g_free (path);
1266 mc_closedir (reading);
1267 return FILE_RETRY;
1269 if (S_ISDIR (buf.st_mode))
1270 return_status =
1271 (recursive_erase
1272 (ctx, path, progress_count, progress_bytes)
1273 != FILE_CONT) ? FILE_RETRY : FILE_CONT;
1274 else
1275 return_status =
1276 erase_file (ctx, path, progress_count, progress_bytes, 0);
1277 g_free (path);
1279 mc_closedir (reading);
1280 if (return_status != FILE_CONT)
1281 return return_status;
1282 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1283 return FILE_ABORT;
1284 mc_refresh ();
1286 while (my_rmdir (s)) {
1287 return_status =
1288 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1289 if (return_status != FILE_RETRY)
1290 return return_status;
1293 return FILE_CONT;
1296 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1297 in the directory path points to, 0 else. */
1298 static int
1299 check_dir_is_empty (const char *path)
1301 DIR *dir;
1302 struct dirent *d;
1303 int i;
1305 dir = mc_opendir (path);
1306 if (!dir)
1307 return -1;
1309 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir)) {
1310 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1311 (d->d_name[1] == '.'
1312 && d->d_name[2] == '\0')))
1313 continue; /* "." or ".." */
1314 i = 0;
1315 break;
1318 mc_closedir (dir);
1319 return i;
1322 FileProgressStatus
1323 erase_dir (FileOpContext *ctx, const char *s, off_t *progress_count,
1324 double *progress_bytes)
1326 FileProgressStatus error;
1328 if (strcmp (s, "..") == 0)
1329 return FILE_SKIP;
1331 if (strcmp (s, ".") == 0)
1332 return FILE_SKIP;
1334 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1335 return FILE_ABORT;
1336 mc_refresh ();
1338 /* The old way to detect a non empty directory was:
1339 error = my_rmdir (s);
1340 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1341 For the linux user space nfs server (nfs-server-2.2beta29-2)
1342 we would have to check also for EIO. I hope the new way is
1343 fool proof. (Norbert)
1345 error = check_dir_is_empty (s);
1346 if (error == 0) { /* not empty */
1347 error = query_recursive (ctx, s);
1348 if (error == FILE_CONT)
1349 return recursive_erase (ctx, s, progress_count,
1350 progress_bytes);
1351 else
1352 return error;
1355 while (my_rmdir (s) == -1) {
1356 error =
1357 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1358 if (error != FILE_RETRY)
1359 return error;
1362 return FILE_CONT;
1365 static FileProgressStatus
1366 erase_dir_iff_empty (FileOpContext *ctx, const char *s)
1368 FileProgressStatus error;
1370 if (strcmp (s, "..") == 0)
1371 return FILE_SKIP;
1373 if (strcmp (s, ".") == 0)
1374 return FILE_SKIP;
1376 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1377 return FILE_ABORT;
1378 mc_refresh ();
1380 if (1 != check_dir_is_empty (s)) /* not empty or error */
1381 return FILE_CONT;
1383 while (my_rmdir (s)) {
1384 error =
1385 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1386 if (error != FILE_RETRY)
1387 return error;
1390 return FILE_CONT;
1393 /* }}} */
1395 /* {{{ Panel operate routines */
1398 * Return currently selected entry name or the name of the first marked
1399 * entry if there is one.
1401 static char *
1402 panel_get_file (WPanel *panel, struct stat *stat_buf)
1404 int i;
1406 if (get_current_type () == view_tree) {
1407 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
1408 char *tree_name = tree_selected_name (tree);
1410 mc_stat (tree_name, stat_buf);
1411 return tree_name;
1414 if (panel->marked) {
1415 for (i = 0; i < panel->count; i++)
1416 if (panel->dir.list[i].f.marked) {
1417 *stat_buf = panel->dir.list[i].st;
1418 return panel->dir.list[i].fname;
1420 } else {
1421 *stat_buf = panel->dir.list[panel->selected].st;
1422 return panel->dir.list[panel->selected].fname;
1424 g_assert_not_reached ();
1425 return NULL;
1429 ComputeDirSizeUI *
1430 compute_dir_size_create_ui (void)
1432 ComputeDirSizeUI *ui;
1434 const char *b_name = N_("&Abort");
1436 #ifdef ENABLE_NLS
1437 b_name = _(b_name);
1438 #endif
1440 ui = g_new (ComputeDirSizeUI, 1);
1442 ui->dlg = create_dlg (0, 0, 8, COLS/2, dialog_colors, NULL,
1443 NULL, _("Directory scanning"), DLG_CENTER);
1444 ui->dirname = label_new (3, 3, "");
1445 add_widget (ui->dlg, ui->dirname);
1447 add_widget (ui->dlg,
1448 button_new (5, (ui->dlg->cols - strlen (b_name))/2,
1449 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
1451 /* We will manage the dialog without any help,
1452 that's why we have to call init_dlg */
1453 init_dlg (ui->dlg);
1455 return ui;
1458 void
1459 compute_dir_size_destroy_ui (ComputeDirSizeUI *ui)
1461 if (ui != NULL) {
1462 /* schedule to update passive panel */
1463 other_panel->dirty = 1;
1465 /* close and destroy dialog */
1466 dlg_run_done (ui->dlg);
1467 destroy_dlg (ui->dlg);
1468 g_free (ui);
1472 FileProgressStatus
1473 compute_dir_size_update_ui (const void *ui, const char *dirname)
1475 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
1476 int c;
1477 Gpm_Event event;
1479 if (ui == NULL)
1480 return FILE_CONT;
1482 label_set_text (this->dirname, name_trunc (dirname, this->dlg->cols - 6));
1484 event.x = -1; /* Don't show the GPM cursor */
1485 c = tty_get_event (&event, FALSE, FALSE);
1486 if (c == EV_NONE)
1487 return FILE_CONT;
1489 /* Reinitialize to avoid old values after events other than
1490 selecting a button */
1491 this->dlg->ret_value = FILE_CONT;
1493 dlg_process_event (this->dlg, c, &event);
1495 switch (this->dlg->ret_value) {
1496 case B_CANCEL:
1497 case FILE_ABORT:
1498 return FILE_ABORT;
1499 default:
1500 return FILE_CONT;
1505 * compute_dir_size:
1507 * Computes the number of bytes used by the files in a directory
1509 FileProgressStatus
1510 compute_dir_size (const char *dirname, const void *ui,
1511 compute_dir_size_callback cback,
1512 off_t *ret_marked, double *ret_total)
1514 DIR *dir;
1515 struct dirent *dirent;
1516 FileProgressStatus ret = FILE_CONT;
1518 dir = mc_opendir (dirname);
1520 if (dir == NULL)
1521 return ret;
1523 while ((dirent = mc_readdir (dir)) != NULL) {
1524 char *fullname;
1525 int res;
1526 struct stat s;
1528 ret = (cback != NULL) ? cback (ui, dirname) : FILE_CONT;
1530 if (ret != FILE_CONT)
1531 break;
1533 if (strcmp (dirent->d_name, ".") == 0)
1534 continue;
1535 if (strcmp (dirent->d_name, "..") == 0)
1536 continue;
1538 fullname = concat_dir_and_file (dirname, dirent->d_name);
1539 res = mc_lstat (fullname, &s);
1541 if (res != 0) {
1542 g_free (fullname);
1543 continue;
1546 if (S_ISDIR (s.st_mode)) {
1547 off_t subdir_count = 0;
1548 double subdir_bytes = 0;
1550 ret = compute_dir_size (fullname, ui, cback, &subdir_count, &subdir_bytes);
1552 if (ret != FILE_CONT) {
1553 g_free (fullname);
1554 break;
1557 *ret_marked += subdir_count;
1558 *ret_total += subdir_bytes;
1559 } else {
1560 (*ret_marked)++;
1561 *ret_total += s.st_size;
1564 g_free (fullname);
1567 mc_closedir (dir);
1569 return ret;
1573 * panel_compute_totals:
1575 * compute the number of files and the number of bytes
1576 * used up by the whole selection, recursing directories
1577 * as required. In addition, it checks to see if it will
1578 * overwrite any files by doing the copy.
1580 static FileProgressStatus
1581 panel_compute_totals (WPanel *panel, const void *ui,
1582 compute_dir_size_callback cback,
1583 off_t *ret_marked, double *ret_total)
1585 int i;
1587 *ret_marked = 0;
1588 *ret_total = 0.0;
1590 for (i = 0; i < panel->count; i++) {
1591 struct stat *s;
1593 if (!panel->dir.list[i].f.marked)
1594 continue;
1596 s = &panel->dir.list[i].st;
1598 if (S_ISDIR (s->st_mode)) {
1599 char *dir_name;
1600 off_t subdir_count = 0;
1601 double subdir_bytes = 0;
1602 FileProgressStatus status;
1604 dir_name =
1605 concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1607 status = compute_dir_size (dir_name, ui, cback,
1608 &subdir_count, &subdir_bytes);
1609 g_free (dir_name);
1611 if (status != FILE_CONT)
1612 return FILE_ABORT;
1614 *ret_marked += subdir_count;
1615 *ret_total += subdir_bytes;
1616 } else {
1617 (*ret_marked)++;
1618 *ret_total += s->st_size;
1622 return FILE_CONT;
1626 * This array introduced to avoid translation problems. The former (op_names)
1627 * is assumed to be nouns, suitable in dialog box titles; this one should
1628 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1629 * (I don't use spaces around the words, because someday they could be
1630 * dropped, when widgets get smarter)
1633 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
1634 static const char *op_names1[] = {
1635 N_("FileOperation|Copy"),
1636 N_("FileOperation|Move"),
1637 N_("FileOperation|Delete")
1641 * These are formats for building a prompt. Parts encoded as follows:
1642 * %o - operation from op_names1
1643 * %f - file/files or files/directories, as appropriate
1644 * %m - "with source mask" or question mark for delete
1645 * %s - source name (truncated)
1646 * %d - number of marked files
1647 * %e - "to:" or question mark for delete
1649 * xgettext:no-c-format */
1650 static const char *one_format = N_("%o %f \"%s\"%m");
1651 /* xgettext:no-c-format */
1652 static const char *many_format = N_("%o %d %f%m");
1654 static const char *prompt_parts[] = {
1655 N_("file"),
1656 N_("files"),
1657 N_("directory"),
1658 N_("directories"),
1659 N_("files/directories"),
1660 N_(" with source mask:"),
1661 N_(" to:")
1664 static const char *question_format = N_("%s?");
1667 * Generate user prompt for panel operation.
1668 * single_source is the name if the source entry or NULL for multiple
1669 * entries.
1670 * src_stat is only used when single_source is not NULL.
1672 static char *
1673 panel_operate_generate_prompt (const WPanel *panel, const int operation,
1674 gboolean single_source,
1675 const struct stat *src_stat)
1677 const char *sp, *cp;
1678 int i;
1679 char format_string[BUF_MEDIUM];
1680 char *dp = format_string;
1681 gboolean build_question = FALSE;
1683 #ifdef ENABLE_NLS
1684 static gboolean i18n_flag = FALSE;
1685 if (!i18n_flag) {
1686 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1687 op_names1[i] = Q_(op_names1[i]);
1689 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1690 prompt_parts[i] = _(prompt_parts[i]);
1692 one_format = _(one_format);
1693 many_format = _(many_format);
1694 question_format = _(question_format);
1695 i18n_flag = TRUE;
1697 #endif /* ENABLE_NLS */
1699 sp = single_source ? one_format : many_format;
1701 while (*sp != '\0') {
1702 switch (*sp) {
1703 case '%':
1704 cp = NULL;
1705 switch (sp[1]) {
1706 case 'o':
1707 cp = op_names1[operation];
1708 break;
1709 case 'm':
1710 if (operation == OP_DELETE) {
1711 cp = "";
1712 build_question = TRUE;
1713 } else
1714 cp = prompt_parts[5];
1715 break;
1716 case 'e':
1717 if (operation == OP_DELETE) {
1718 cp = "";
1719 build_question = TRUE;
1720 } else
1721 cp = prompt_parts[6];
1722 break;
1723 case 'f':
1724 if (single_source) {
1725 cp = S_ISDIR (src_stat->
1726 st_mode) ? prompt_parts[2] : prompt_parts[0];
1727 } else {
1728 cp = (panel->marked == panel->dirs_marked)
1729 ? prompt_parts[3]
1730 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1732 break;
1733 default:
1734 *dp++ = *sp++;
1736 if (cp != NULL) {
1737 sp += 2;
1738 while (*cp != '\0')
1739 *dp++ = *cp++;
1741 break;
1742 default:
1743 *dp++ = *sp++;
1746 *dp = '\0';
1748 if (build_question) {
1749 char tmp[BUF_MEDIUM];
1751 memmove (tmp, format_string, sizeof (tmp));
1752 g_snprintf (format_string, sizeof (format_string),
1753 question_format, tmp);
1756 return g_strdup (format_string);
1760 * panel_operate:
1762 * Performs one of the operations on the selection on the source_panel
1763 * (copy, delete, move).
1765 * Returns 1 if did change the directory
1766 * structure, Returns 0 if user aborted
1768 * force_single forces operation on the current entry and affects
1769 * default destination. Current filename is used as default.
1772 panel_operate (void *source_panel, FileOperation operation,
1773 int force_single)
1775 WPanel *panel = source_panel;
1776 char *source = NULL;
1777 #ifdef WITH_FULL_PATHS
1778 char *source_with_path = NULL;
1779 #else
1780 # define source_with_path source
1781 #endif /* !WITH_FULL_PATHS */
1782 char *dest = NULL;
1783 char *temp = NULL;
1784 char *save_cwd = NULL, *save_dest = NULL;
1785 int single_entry = (get_current_type () == view_tree)
1786 || (panel->marked <= 1) || force_single;
1787 struct stat src_stat, dst_stat;
1788 int i;
1789 FileProgressStatus value;
1790 FileOpContext *ctx;
1792 off_t count = 0;
1793 double bytes = 0;
1795 int dst_result;
1796 int do_bg = 0; /* do background operation? */
1798 free_linklist (&linklist);
1799 free_linklist (&dest_dirs);
1801 /* Update panel contents to avoid actions on deleted files */
1802 if (!panel->is_panelized) {
1803 update_panels (UP_RELOAD, UP_KEEPSEL);
1804 repaint_screen ();
1807 if (single_entry) {
1808 if (force_single) {
1809 source = selection (panel)->fname;
1810 src_stat = selection (panel)->st;
1811 } else {
1812 source = panel_get_file (panel, &src_stat);
1815 if (!strcmp (source, "..")) {
1816 message (D_ERROR, MSG_ERROR, _(" Cannot operate on \"..\"! "));
1817 return 0;
1821 ctx = file_op_context_new (operation);
1823 /* Show confirmation dialog */
1824 if (operation != OP_DELETE) {
1825 char *dest_dir;
1826 char *dest_dir_;
1827 char *format;
1829 /* Forced single operations default to the original name */
1830 if (force_single)
1831 dest_dir = source;
1832 else if (get_other_type () == view_listing)
1833 dest_dir = other_panel->cwd;
1834 else
1835 dest_dir = panel->cwd;
1837 * Add trailing backslash only when do non-local ops.
1838 * It saves user from occasional file renames (when destination
1839 * dir is deleted)
1841 if (!force_single
1842 && dest_dir[0] != '\0'
1843 && dest_dir[strlen (dest_dir) - 1] != PATH_SEP) {
1844 /* add trailing separator */
1845 dest_dir_ = g_strconcat (dest_dir, PATH_SEP_STR, (char *) NULL);
1846 } else {
1847 /* just copy */
1848 dest_dir_ = g_strdup (dest_dir);
1850 if (dest_dir_ == NULL) {
1851 file_op_context_destroy (ctx);
1852 return 0;
1855 /* Generate confirmation prompt */
1856 format = panel_operate_generate_prompt (panel, operation,
1857 source != NULL, &src_stat);
1859 dest = file_mask_dialog (ctx, operation, source != NULL, format,
1860 source != NULL ? (void *) source
1861 : (void *) &panel->marked,
1862 dest_dir_, &do_bg);
1864 g_free (format);
1865 g_free (dest_dir_);
1867 if (dest == NULL || dest[0] == '\0') {
1868 file_op_context_destroy (ctx);
1869 g_free (dest);
1870 return 0;
1872 } else if (confirm_delete) {
1873 char *format;
1874 char fmd_buf [BUF_MEDIUM];
1876 /* Generate confirmation prompt */
1877 format = panel_operate_generate_prompt (panel, OP_DELETE,
1878 source != NULL, &src_stat);
1880 if (source == NULL)
1881 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
1882 else {
1883 const int fmd_xlen = 64;
1884 i = fmd_xlen - str_term_width1 (format) - 4;
1885 g_snprintf (fmd_buf, sizeof (fmd_buf),
1886 format, str_trunc (source, i));
1889 g_free (format);
1891 if (safe_delete)
1892 query_set_sel (1);
1894 i = query_dialog (Q_(op_names[operation]), fmd_buf, D_ERROR, 2,
1895 _("&Yes"), _("&No"));
1897 if (i != 0) {
1898 file_op_context_destroy (ctx);
1899 return 0;
1903 #ifdef WITH_BACKGROUND
1904 /* Did the user select to do a background operation? */
1905 if (do_bg) {
1906 int v;
1908 v = do_background (ctx,
1909 g_strconcat (op_names[operation], ": ",
1910 panel->cwd, NULL));
1911 if (v == -1) {
1912 message (D_ERROR, MSG_ERROR,
1913 _(" Sorry, I could not put the job in background "));
1916 /* If we are the parent */
1917 if (v == 1) {
1918 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
1919 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
1920 /* file_op_context_destroy (ctx); */
1921 return 0;
1924 #endif /* WITH_BACKGROUND */
1926 /* Initialize things */
1927 /* We do not want to trash cache every time file is
1928 created/touched. However, this will make our cache contain
1929 invalid data. */
1930 if (dest) {
1931 if (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1))
1932 save_dest = g_strdup (dest);
1934 if (panel->cwd) {
1935 if (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1))
1936 save_cwd = g_strdup (panel->cwd);
1939 /* Now, let's do the job */
1941 if (do_bg)
1942 ctx->ui = NULL;
1943 else
1944 file_op_context_create_ui (ctx, 1);
1946 /* This code is only called by the tree and panel code */
1947 if (single_entry) {
1948 /* We now have ETA in all cases */
1950 /* One file: FIXME mc_chdir will take user out of any vfs */
1951 if (operation != OP_COPY && get_current_type () == view_tree)
1952 mc_chdir (PATH_SEP_STR);
1954 /* The source and src_stat variables have been initialized before */
1955 #ifdef WITH_FULL_PATHS
1956 source_with_path = concat_dir_and_file (panel->cwd, source);
1957 #endif /* WITH_FULL_PATHS */
1959 if (operation == OP_DELETE) {
1960 if (S_ISDIR (src_stat.st_mode))
1961 value = erase_dir (ctx, source_with_path, &count, &bytes);
1962 else
1963 value =
1964 erase_file (ctx, source_with_path, &count, &bytes, 1);
1965 } else {
1966 temp = transform_source (ctx, source_with_path);
1967 if (temp == NULL) {
1968 value = transform_error;
1969 } else {
1970 char *repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
1971 char *temp2 = concat_dir_and_file (repl_dest, temp);
1972 g_free (repl_dest);
1973 g_free (temp);
1974 g_free(dest);
1975 dest = temp2;
1977 switch (operation) {
1978 case OP_COPY:
1980 * we use file_mask_op_follow_links only with OP_COPY,
1982 (*ctx->stat_func) (source_with_path, &src_stat);
1984 if (S_ISDIR (src_stat.st_mode)) {
1985 value =
1986 copy_dir_dir (ctx, source_with_path, dest, 1,
1987 0, 0, 0, &count, &bytes);
1988 } else {
1989 value =
1990 copy_file_file (ctx, source_with_path, dest, 1,
1991 &count, &bytes, 1);
1993 break;
1995 case OP_MOVE:
1996 if (S_ISDIR (src_stat.st_mode))
1997 value =
1998 move_dir_dir (ctx, source_with_path, dest,
1999 &count, &bytes);
2000 else
2001 value =
2002 move_file_file (ctx, source_with_path, dest,
2003 &count, &bytes);
2004 break;
2006 default:
2007 /* Unknown file operation */
2008 abort ();
2011 } /* Copy or move operation */
2013 if ((value == FILE_CONT) && !force_single)
2014 unmark_files (panel);
2015 } else {
2016 /* Many files */
2017 /* Check destination for copy or move operation */
2018 if (operation != OP_DELETE) {
2019 retry_many_dst_stat:
2020 dst_result = mc_stat (dest, &dst_stat);
2021 if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)) {
2022 if (file_error
2023 (_(" Destination \"%s\" must be a directory \n %s "),
2024 dest) == FILE_RETRY)
2025 goto retry_many_dst_stat;
2026 goto clean_up;
2030 /* Initialize variables for progress bars */
2031 if (operation != OP_MOVE && verbose && file_op_compute_totals) {
2032 ComputeDirSizeUI *ui;
2033 FileProgressStatus status;
2035 ui = compute_dir_size_create_ui ();
2036 status = panel_compute_totals (panel,
2037 ui, compute_dir_size_update_ui,
2038 &ctx->progress_count, &ctx->progress_bytes);
2039 compute_dir_size_destroy_ui (ui);
2041 if (status != FILE_CONT)
2042 goto clean_up;
2044 ctx->progress_totals_computed = 1;
2045 } else {
2046 ctx->progress_totals_computed = 0;
2047 ctx->progress_count = panel->marked;
2048 ctx->progress_bytes = panel->total;
2051 /* Loop for every file, perform the actual copy operation */
2052 for (i = 0; i < panel->count; i++) {
2053 if (!panel->dir.list[i].f.marked)
2054 continue; /* Skip the unmarked ones */
2056 source = panel->dir.list[i].fname;
2057 src_stat = panel->dir.list[i].st;
2059 #ifdef WITH_FULL_PATHS
2060 g_free (source_with_path);
2061 source_with_path = concat_dir_and_file (panel->cwd, source);
2062 #endif /* WITH_FULL_PATHS */
2064 if (operation == OP_DELETE) {
2065 if (S_ISDIR (src_stat.st_mode))
2066 value =
2067 erase_dir (ctx, source_with_path, &count, &bytes);
2068 else
2069 value =
2070 erase_file (ctx, source_with_path, &count, &bytes,
2072 } else {
2073 temp = transform_source (ctx, source_with_path);
2074 if (temp == NULL)
2075 value = transform_error;
2076 else {
2077 char *temp3;
2078 char *repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2079 char *temp2 = concat_dir_and_file (repl_dest, temp);
2080 g_free(repl_dest);
2082 g_free(temp);
2083 temp3 = source_with_path;
2084 source_with_path = strutils_shell_unescape(source_with_path);
2085 g_free(temp3);
2086 temp3 = temp2;
2087 temp2 = strutils_shell_unescape(temp2);
2088 g_free(temp3);
2090 switch (operation) {
2091 case OP_COPY:
2093 * we use file_mask_op_follow_links only with OP_COPY
2095 (*ctx->stat_func) (source_with_path, &src_stat);
2096 if (S_ISDIR (src_stat.st_mode))
2097 value =
2098 copy_dir_dir (ctx, source_with_path, temp2,
2099 1, 0, 0, 0, &count, &bytes);
2100 else
2101 value =
2102 copy_file_file (ctx, source_with_path,
2103 temp2, 1, &count, &bytes,
2105 free_linklist (&dest_dirs);
2106 break;
2108 case OP_MOVE:
2109 if (S_ISDIR (src_stat.st_mode))
2110 value =
2111 move_dir_dir (ctx, source_with_path, temp2,
2112 &count, &bytes);
2113 else
2114 value =
2115 move_file_file (ctx, source_with_path,
2116 temp2, &count, &bytes);
2117 break;
2119 default:
2120 /* Unknown file operation */
2121 abort ();
2123 g_free (temp2);
2125 } /* Copy or move operation */
2127 if (value == FILE_ABORT)
2128 goto clean_up;
2130 if (value == FILE_CONT)
2131 do_file_mark (panel, i, 0);
2133 if (file_progress_show_count (ctx, count, ctx->progress_count)
2134 == FILE_ABORT)
2135 goto clean_up;
2137 if (verbose
2138 && file_progress_show_bytes (ctx, bytes,
2139 ctx->progress_bytes) ==
2140 FILE_ABORT)
2141 goto clean_up;
2143 if (operation != OP_DELETE && verbose
2144 && file_progress_show (ctx, 0, 0) == FILE_ABORT)
2145 goto clean_up;
2147 mc_refresh ();
2148 } /* Loop for every file */
2149 } /* Many entries */
2150 clean_up:
2151 /* Clean up */
2153 if (save_cwd) {
2154 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2155 g_free (save_cwd);
2157 if (save_dest) {
2158 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2159 g_free (save_dest);
2162 free_linklist (&linklist);
2163 free_linklist (&dest_dirs);
2164 #ifdef WITH_FULL_PATHS
2165 g_free (source_with_path);
2166 #endif /* WITH_FULL_PATHS */
2167 g_free (dest);
2168 g_free (ctx->dest_mask);
2169 ctx->dest_mask = NULL;
2170 #ifdef WITH_BACKGROUND
2171 /* Let our parent know we are saying bye bye */
2172 if (we_are_background) {
2173 vfs_shut ();
2174 _exit (0);
2176 #endif /* WITH_BACKGROUND */
2178 file_op_context_destroy (ctx);
2179 return 1;
2182 /* }}} */
2184 /* {{{ Query/status report routines */
2186 static FileProgressStatus
2187 real_do_file_error (enum OperationMode mode, const char *error)
2189 int result;
2190 const char *msg;
2192 msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
2193 result =
2194 query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"),
2195 _("&Abort"));
2197 switch (result) {
2198 case 0:
2199 do_refresh ();
2200 return FILE_SKIP;
2202 case 1:
2203 do_refresh ();
2204 return FILE_RETRY;
2206 case 2:
2207 default:
2208 return FILE_ABORT;
2212 /* Report error with one file */
2213 FileProgressStatus
2214 file_error (const char *format, const char *file)
2216 char buf [BUF_MEDIUM];
2218 g_snprintf (buf, sizeof (buf), format,
2219 path_trunc (file, 30), unix_error_string (errno));
2221 return do_file_error (buf);
2224 /* Report error with two files */
2225 static FileProgressStatus
2226 files_error (const char *format, const char *file1, const char *file2)
2228 char buf [BUF_MEDIUM];
2229 char *nfile1 = g_strdup (path_trunc (file1, 15));
2230 char *nfile2 = g_strdup (path_trunc (file2, 15));
2232 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2,
2233 unix_error_string (errno));
2235 g_free (nfile1);
2236 g_free (nfile2);
2238 return do_file_error (buf);
2241 static FileProgressStatus
2242 real_query_recursive (FileOpContext *ctx, enum OperationMode mode, const char *s)
2244 gchar *text;
2246 if (ctx->recursive_result < RECURSIVE_ALWAYS) {
2247 const char *msg =
2248 mode ==
2249 Foreground ?
2250 _("\n Directory not empty. \n"
2251 " Delete it recursively? ")
2252 : _("\n Background process: Directory not empty \n"
2253 " Delete it recursively? ");
2254 text = g_strconcat (_(" Delete: "), path_trunc (s, 30), " ", (char *) NULL);
2256 if (safe_delete)
2257 query_set_sel (1);
2258 ctx->recursive_result = query_dialog (text, msg, D_ERROR, 5,
2259 _("&Yes"), _("&No"),
2260 _("A&ll"), _("Non&e"),
2261 _("&Abort"));
2263 if (ctx->recursive_result != RECURSIVE_ABORT)
2264 do_refresh ();
2265 g_free (text);
2268 switch (ctx->recursive_result) {
2269 case RECURSIVE_YES:
2270 case RECURSIVE_ALWAYS:
2271 return FILE_CONT;
2273 case RECURSIVE_NO:
2274 case RECURSIVE_NEVER:
2275 return FILE_SKIP;
2277 case RECURSIVE_ABORT:
2279 default:
2280 return FILE_ABORT;
2284 #ifdef WITH_BACKGROUND
2285 static FileProgressStatus
2286 do_file_error (const char *str)
2288 union {
2289 void *p;
2290 FileProgressStatus (*f) (enum OperationMode, const char *);
2291 } pntr;
2292 pntr.f = real_do_file_error;
2294 if (we_are_background)
2295 return parent_call (pntr.p, NULL, 1, strlen (str),
2296 str);
2297 else
2298 return real_do_file_error (Foreground, str);
2301 static FileProgressStatus
2302 query_recursive (FileOpContext *ctx, const char *s)
2304 union {
2305 void *p;
2306 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
2307 } pntr;
2308 pntr.f = real_query_recursive;
2310 if (we_are_background)
2311 return parent_call (pntr.p, ctx, 1, strlen (s), s);
2312 else
2313 return real_query_recursive (ctx, Foreground, s);
2316 static FileProgressStatus
2317 query_replace (FileOpContext *ctx, const char *destname, struct stat *_s_stat,
2318 struct stat *_d_stat)
2320 union {
2321 void *p;
2322 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
2323 struct stat *, struct stat *);
2324 } pntr;
2325 pntr.f = file_progress_real_query_replace;
2327 if (we_are_background)
2328 return parent_call (pntr.p,
2329 ctx,
2331 strlen (destname), destname,
2332 sizeof (struct stat), _s_stat,
2333 sizeof (struct stat), _d_stat);
2334 else
2335 return file_progress_real_query_replace (ctx, Foreground, destname,
2336 _s_stat, _d_stat);
2339 #else
2340 static FileProgressStatus
2341 do_file_error (const char *str)
2343 return real_do_file_error (Foreground, str);
2346 static FileProgressStatus
2347 query_recursive (FileOpContext *ctx, const char *s)
2349 return real_query_recursive (ctx, Foreground, s);
2352 static FileProgressStatus
2353 query_replace (FileOpContext *ctx, const char *destname, struct stat *_s_stat,
2354 struct stat *_d_stat)
2356 return file_progress_real_query_replace (ctx, Foreground, destname,
2357 _s_stat, _d_stat);
2360 #endif /* !WITH_BACKGROUND */
2363 Cause emacs to enter folding mode for this file:
2364 Local variables:
2365 end: