Handle hard link creation error.
[midnight-commander.git] / src / filemanager / file.c
blob288dab0989611b06603a886eaec129f5d27710b9
1 /*
2 File management.
4 Copyright (C) 1994-2018
5 Free Software Foundation, Inc.
7 Written by:
8 Janne Kukonlehto, 1994, 1995
9 Fred Leeflang, 1994, 1995
10 Miguel de Icaza, 1994, 1995, 1996
11 Jakub Jelinek, 1995, 1996
12 Norbert Warmuth, 1997
13 Pavel Machek, 1998
14 Andrew Borodin <aborodin@vmail.ru>, 2011-2014
16 The copy code was based in GNU's cp, and was written by:
17 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
19 The move code was based in GNU's mv, and was written by:
20 Mike Parker and David MacKenzie.
22 Janne Kukonlehto added much error recovery to them for being used
23 in an interactive program.
25 This file is part of the Midnight Commander.
27 The Midnight Commander is free software: you can redistribute it
28 and/or modify it under the terms of the GNU General Public License as
29 published by the Free Software Foundation, either version 3 of the License,
30 or (at your option) any later version.
32 The Midnight Commander is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program. If not, see <http://www.gnu.org/licenses/>.
42 * Please note that all dialogs used here must be safe for background
43 * operations.
46 /** \file src/filemanager/file.c
47 * \brief Source: file management
50 /* {{{ Include files */
52 #include <config.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
63 #include "lib/global.h"
64 #include "lib/tty/tty.h"
65 #include "lib/tty/key.h"
66 #include "lib/search.h"
67 #include "lib/strescape.h"
68 #include "lib/strutil.h"
69 #include "lib/util.h"
70 #include "lib/vfs/vfs.h"
71 #include "lib/widget.h"
73 #include "src/setup.h"
74 #ifdef ENABLE_BACKGROUND
75 #include "src/background.h" /* do_background() */
76 #endif
78 /* Needed for current_panel, other_panel and WTree */
79 #include "dir.h"
80 #include "filegui.h"
81 #include "filenot.h"
82 #include "tree.h"
83 #include "midnight.h" /* current_panel */
84 #include "layout.h" /* rotate_dash() */
85 #include "ioblksize.h" /* io_blksize() */
87 #include "file.h"
89 /* }}} */
91 /*** global variables ****************************************************************************/
93 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
94 const char *op_names[3] = {
95 N_("DialogTitle|Copy"),
96 N_("DialogTitle|Move"),
97 N_("DialogTitle|Delete")
100 /*** file scope macro definitions ****************************************************************/
102 #define FILEOP_UPDATE_INTERVAL 2
103 #define FILEOP_STALLING_INTERVAL 4
105 /*** file scope type declarations ****************************************************************/
107 /* This is a hard link cache */
108 struct link
110 const struct vfs_class *vfs;
111 dev_t dev;
112 ino_t ino;
113 short linkcount;
114 mode_t st_mode;
115 vfs_path_t *src_vpath;
116 vfs_path_t *dst_vpath;
119 /* Status of the destination file */
120 typedef enum
122 DEST_NONE = 0, /* Not created */
123 DEST_SHORT = 1, /* Created, not fully copied */
124 DEST_FULL = 2 /* Created, fully copied */
125 } dest_status_t;
127 /* Status of hard link creation */
128 typedef enum
130 HARDLINK_OK = 0, /**< Hardlink was created successfully */
131 HARDLINK_CACHED, /**< Hardlink was added to the cache */
132 HARDLINK_NOTLINK, /**< This is not a hard link */
133 HARDLINK_UNSUPPORTED, /**< VFS doesn't support hard links */
134 HARDLINK_ERROR, /**< Hard link creation error */
135 HARDLINK_ABORT /**< Stop file operation after hardlink creation error */
136 } hardlink_status_t;
139 * This array introduced to avoid translation problems. The former (op_names)
140 * is assumed to be nouns, suitable in dialog box titles; this one should
141 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
142 * (I don't use spaces around the words, because someday they could be
143 * dropped, when widgets get smarter)
146 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
147 static const char *op_names1[] = {
148 N_("FileOperation|Copy"),
149 N_("FileOperation|Move"),
150 N_("FileOperation|Delete")
154 * These are formats for building a prompt. Parts encoded as follows:
155 * %o - operation from op_names1
156 * %f - file/files or files/directories, as appropriate
157 * %m - "with source mask" or question mark for delete
158 * %s - source name (truncated)
159 * %d - number of marked files
160 * %n - the '\n' symbol to form two-line prompt for delete or space for other operations
162 /* xgettext:no-c-format */
163 static const char *one_format = N_("%o %f%n\"%s\"%m");
164 /* xgettext:no-c-format */
165 static const char *many_format = N_("%o %d %f%m");
167 static const char *prompt_parts[] = {
168 N_("file"),
169 N_("files"),
170 N_("directory"),
171 N_("directories"),
172 N_("files/directories"),
173 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
174 N_(" with source mask:")
177 /*** file scope variables ************************************************************************/
179 /* the hard link cache */
180 static GSList *linklist = NULL;
182 /* the files-to-be-erased list */
183 static GSList *erase_list = NULL;
186 * In copy_dir_dir we use two additional single linked lists: The first -
187 * variable name 'parent_dirs' - holds information about already copied
188 * directories and is used to detect cyclic symbolic links.
189 * The second ('dest_dirs' below) holds information about just created
190 * target directories and is used to detect when an directory is copied
191 * into itself (we don't want to copy infinitly).
192 * Both lists don't use the linkcount and name structure members of struct
193 * link.
195 static GSList *dest_dirs = NULL;
197 /* --------------------------------------------------------------------------------------------- */
198 /*** file scope functions ************************************************************************/
199 /* --------------------------------------------------------------------------------------------- */
201 static void
202 dirsize_status_locate_buttons (dirsize_status_msg_t * dsm)
204 status_msg_t *sm = STATUS_MSG (dsm);
205 Widget *wd = WIDGET (sm->dlg);
206 int y, x;
208 y = wd->y + 5;
209 x = wd->x;
211 if (!dsm->allow_skip)
213 /* single button: "Abort" */
214 x += (wd->cols - dsm->abort_button->cols) / 2;
215 widget_set_size (dsm->abort_button, y, x,
216 dsm->abort_button->lines, dsm->abort_button->cols);
218 else
220 /* two buttons: "Abort" and "Skip" */
221 int cols;
223 cols = dsm->abort_button->cols + dsm->skip_button->cols + 1;
224 x += (wd->cols - cols) / 2;
225 widget_set_size (dsm->abort_button, y, x, dsm->abort_button->lines,
226 dsm->abort_button->cols);
227 x += dsm->abort_button->cols + 1;
228 widget_set_size (dsm->skip_button, y, x, dsm->skip_button->lines, dsm->skip_button->cols);
232 /* --------------------------------------------------------------------------------------------- */
234 static char *
235 build_dest (file_op_context_t * ctx, const char *src, const char *dest, FileProgressStatus * status)
237 char *s, *q;
238 const char *fnsource;
240 *status = FILE_CONT;
242 s = g_strdup (src);
244 /* We remove \n from the filename since regex routines would use \n as an anchor */
245 /* this is just to be allowed to maniupulate file names with \n on it */
246 for (q = s; *q != '\0'; q++)
247 if (*q == '\n')
248 *q = ' ';
250 fnsource = x_basename (s);
252 if (!mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
254 q = NULL;
255 *status = FILE_SKIP;
257 else
259 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
260 if (ctx->search_handle->error != MC_SEARCH_E_OK)
262 if (ctx->search_handle->error_str != NULL)
263 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
265 *status = FILE_ABORT;
269 MC_PTR_FREE (s);
271 if (*status == FILE_CONT)
273 char *repl_dest;
275 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
276 if (ctx->search_handle->error == MC_SEARCH_E_OK)
277 s = mc_build_filename (repl_dest, q, (char *) NULL);
278 else
280 if (ctx->search_handle->error_str != NULL)
281 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
283 *status = FILE_ABORT;
286 g_free (repl_dest);
289 g_free (q);
291 return s;
294 /* --------------------------------------------------------------------------------------------- */
296 static void
297 free_link (void *data)
299 struct link *lp = (struct link *) data;
301 vfs_path_free (lp->src_vpath);
302 vfs_path_free (lp->dst_vpath);
303 g_free (lp);
306 /* --------------------------------------------------------------------------------------------- */
308 static inline void *
309 free_linklist (GSList * lp)
311 g_slist_free_full (lp, free_link);
313 return NULL;
316 /* --------------------------------------------------------------------------------------------- */
318 static const struct link *
319 is_in_linklist (const GSList * lp, const vfs_path_t * vpath, const struct stat *sb)
321 const struct vfs_class *class;
322 ino_t ino = sb->st_ino;
323 dev_t dev = sb->st_dev;
325 class = vfs_path_get_last_path_vfs (vpath);
327 for (; lp != NULL; lp = (const GSList *) g_slist_next (lp))
329 const struct link *lnk = (const struct link *) lp->data;
331 if (lnk->vfs == class && lnk->ino == ino && lnk->dev == dev)
332 return lnk;
335 return NULL;
338 /* --------------------------------------------------------------------------------------------- */
340 * Check and made hardlink
342 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
343 * and a hardlink was successfully made
346 static hardlink_status_t
347 check_hardlinks (const vfs_path_t * src_vpath, const struct stat *src_stat,
348 const vfs_path_t * dst_vpath, gboolean * skip_all)
350 struct link *lnk;
351 ino_t ino = src_stat->st_ino;
352 dev_t dev = src_stat->st_dev;
354 if (src_stat->st_nlink < 2)
355 return HARDLINK_NOTLINK;
356 if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
357 return HARDLINK_UNSUPPORTED;
359 lnk = (struct link *) is_in_linklist (linklist, src_vpath, src_stat);
360 if (lnk != NULL)
362 int stat_result;
363 struct stat link_stat;
365 stat_result = mc_stat (lnk->src_vpath, &link_stat);
367 if (stat_result == 0 && link_stat.st_ino == ino && link_stat.st_dev == dev)
369 const struct vfs_class *lp_name_class;
370 const struct vfs_class *my_vfs;
372 lp_name_class = vfs_path_get_last_path_vfs (lnk->src_vpath);
373 my_vfs = vfs_path_get_last_path_vfs (src_vpath);
375 if (lp_name_class == my_vfs)
377 const struct vfs_class *p_class, *dst_name_class;
379 dst_name_class = vfs_path_get_last_path_vfs (dst_vpath);
380 p_class = vfs_path_get_last_path_vfs (lnk->dst_vpath);
382 if (dst_name_class == p_class)
384 gboolean ok;
386 while (!(ok = mc_stat (lnk->dst_vpath, &link_stat) == 0) && !*skip_all)
388 FileProgressStatus status;
390 status =
391 file_error (TRUE, _("Cannot stat hardlink source file \"%s\"\n%s"),
392 vfs_path_as_str (lnk->dst_vpath));
393 if (status == FILE_ABORT)
394 return HARDLINK_ABORT;
395 if (status == FILE_RETRY)
396 continue;
397 if (status == FILE_SKIPALL)
398 *skip_all = TRUE;
399 break;
402 /* if stat() finished unsuccessfully, don't try to create link */
403 if (!ok)
404 return HARDLINK_ERROR;
406 while (!(ok = mc_link (lnk->dst_vpath, dst_vpath) == 0) && !*skip_all)
408 FileProgressStatus status;
410 status =
411 file_error (TRUE, _("Cannot create target hardlink \"%s\"\n%s"),
412 vfs_path_as_str (dst_vpath));
413 if (status == FILE_ABORT)
414 return HARDLINK_ABORT;
415 if (status == FILE_RETRY)
416 continue;
417 if (status == FILE_SKIPALL)
418 *skip_all = TRUE;
419 break;
422 /* Success? */
423 return (ok ? HARDLINK_OK : HARDLINK_ERROR);
428 if (!*skip_all)
430 FileProgressStatus status;
432 /* Message w/o "Retry" action.
434 * FIXME: Can't say what errno is here. Define it and don't display.
436 * file_error() displays a message with text representation of errno
437 * and the string passed to file_error() should provide the format "%s"
438 * for that at end (see previous file_error() call for the reference).
439 * But if format for errno isn't provided, it is safe, because C standard says:
440 * "If the format is exhausted while arguments remain, the excess arguments
441 * are evaluated (as always) but are otherwise ignored" (ISO/IEC 9899:1999,
442 * section 7.19.6.1, paragraph 2).
445 errno = 0;
446 status =
447 file_error (FALSE, _("Cannot create target hardlink \"%s\""),
448 vfs_path_as_str (dst_vpath));
450 if (status == FILE_ABORT)
451 return HARDLINK_ABORT;
453 if (status == FILE_SKIPALL)
454 *skip_all = TRUE;
457 return HARDLINK_ERROR;
460 lnk = g_try_new (struct link, 1);
461 if (lnk != NULL)
463 lnk->vfs = vfs_path_get_last_path_vfs (src_vpath);
464 lnk->ino = ino;
465 lnk->dev = dev;
466 lnk->linkcount = 0;
467 lnk->st_mode = 0;
468 lnk->src_vpath = vfs_path_clone (src_vpath);
469 lnk->dst_vpath = vfs_path_clone (dst_vpath);
471 linklist = g_slist_prepend (linklist, lnk);
474 return HARDLINK_CACHED;
477 /* --------------------------------------------------------------------------------------------- */
479 * Duplicate the contents of the symbolic link src_path in dst_path.
480 * Try to make a stable symlink if the option "stable symlink" was
481 * set in the file mask dialog.
482 * If dst_path is an existing symlink it will be deleted silently
483 * (upper levels take already care of existing files at dst_path).
486 static FileProgressStatus
487 make_symlink (file_op_context_t * ctx, const char *src_path, const char *dst_path)
489 char link_target[MC_MAXPATHLEN];
490 int len;
491 FileProgressStatus return_status;
492 struct stat sb;
493 vfs_path_t *src_vpath;
494 vfs_path_t *dst_vpath;
495 gboolean dst_is_symlink;
496 vfs_path_t *link_target_vpath = NULL;
498 src_vpath = vfs_path_from_str (src_path);
499 dst_vpath = vfs_path_from_str (dst_path);
500 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
502 retry_src_readlink:
503 len = mc_readlink (src_vpath, link_target, sizeof (link_target) - 1);
504 if (len < 0)
506 if (ctx->skip_all)
507 return_status = FILE_SKIPALL;
508 else
510 return_status = file_error (TRUE, _("Cannot read source link \"%s\"\n%s"), src_path);
511 if (return_status == FILE_SKIPALL)
512 ctx->skip_all = TRUE;
513 if (return_status == FILE_RETRY)
514 goto retry_src_readlink;
516 goto ret;
519 link_target[len] = '\0';
521 if (ctx->stable_symlinks && !(vfs_file_is_local (src_vpath) && vfs_file_is_local (dst_vpath)))
523 message (D_ERROR, MSG_ERROR,
524 _("Cannot make stable symlinks across "
525 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
526 ctx->stable_symlinks = FALSE;
529 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
531 const char *r;
533 r = strrchr (src_path, PATH_SEP);
534 if (r != NULL)
536 char *p;
537 vfs_path_t *q;
539 p = g_strndup (src_path, r - src_path + 1);
540 if (g_path_is_absolute (dst_path))
541 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
542 else
543 q = vfs_path_build_filename (p, dst_path, (char *) NULL);
545 if (vfs_path_tokens_count (q) > 1)
547 char *s;
548 vfs_path_t *tmp_vpath1, *tmp_vpath2;
550 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
551 s = g_strconcat (p, link_target, (char *) NULL);
552 g_strlcpy (link_target, s, sizeof (link_target));
553 g_free (s);
554 tmp_vpath2 = vfs_path_from_str (link_target);
555 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
556 vfs_path_free (tmp_vpath1);
557 vfs_path_free (tmp_vpath2);
558 if (s != NULL)
560 g_strlcpy (link_target, s, sizeof (link_target));
561 g_free (s);
564 g_free (p);
565 vfs_path_free (q);
568 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
570 retry_dst_symlink:
571 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
573 /* Success */
574 return_status = FILE_CONT;
575 goto ret;
578 * if dst_exists, it is obvious that this had failed.
579 * We can delete the old symlink and try again...
581 if (dst_is_symlink && mc_unlink (dst_vpath) == 0
582 && mc_symlink (link_target_vpath, dst_vpath) == 0)
584 /* Success */
585 return_status = FILE_CONT;
586 goto ret;
589 if (ctx->skip_all)
590 return_status = FILE_SKIPALL;
591 else
593 return_status = file_error (TRUE, _("Cannot create target symlink \"%s\"\n%s"), dst_path);
594 if (return_status == FILE_SKIPALL)
595 ctx->skip_all = TRUE;
596 if (return_status == FILE_RETRY)
597 goto retry_dst_symlink;
600 ret:
601 vfs_path_free (src_vpath);
602 vfs_path_free (dst_vpath);
603 vfs_path_free (link_target_vpath);
604 return return_status;
607 /* --------------------------------------------------------------------------------------------- */
609 * do_compute_dir_size:
611 * Computes the number of bytes used by the files in a directory
614 static FileProgressStatus
615 do_compute_dir_size (const vfs_path_t * dirname_vpath, dirsize_status_msg_t * dsm,
616 size_t * dir_count, size_t * ret_marked, uintmax_t * ret_total,
617 gboolean compute_symlinks)
619 static guint64 timestamp = 0;
620 /* update with 25 FPS rate */
621 static const guint64 delay = G_USEC_PER_SEC / 25;
623 status_msg_t *sm = STATUS_MSG (dsm);
624 int res;
625 struct stat s;
626 DIR *dir;
627 struct dirent *dirent;
628 FileProgressStatus ret = FILE_CONT;
630 if (!compute_symlinks)
632 res = mc_lstat (dirname_vpath, &s);
633 if (res != 0)
634 return ret;
636 /* don't scan symlink to directory */
637 if (S_ISLNK (s.st_mode))
639 (*ret_marked)++;
640 *ret_total += (uintmax_t) s.st_size;
641 return ret;
645 (*dir_count)++;
647 dir = mc_opendir (dirname_vpath);
648 if (dir == NULL)
649 return ret;
651 while (ret == FILE_CONT && (dirent = mc_readdir (dir)) != NULL)
653 vfs_path_t *tmp_vpath;
655 if (DIR_IS_DOT (dirent->d_name) || DIR_IS_DOTDOT (dirent->d_name))
656 continue;
658 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, (char *) NULL);
660 res = mc_lstat (tmp_vpath, &s);
661 if (res == 0)
663 if (S_ISDIR (s.st_mode))
664 ret =
665 do_compute_dir_size (tmp_vpath, dsm, dir_count, ret_marked, ret_total,
666 compute_symlinks);
667 else
669 ret = FILE_CONT;
671 (*ret_marked)++;
672 *ret_total += (uintmax_t) s.st_size;
675 if (ret == FILE_CONT && sm->update != NULL && mc_time_elapsed (&timestamp, delay))
677 dsm->dirname_vpath = tmp_vpath;
678 dsm->dir_count = *dir_count;
679 dsm->total_size = *ret_total;
680 ret = sm->update (sm);
684 vfs_path_free (tmp_vpath);
687 mc_closedir (dir);
688 return ret;
691 /* --------------------------------------------------------------------------------------------- */
693 * panel_compute_totals:
695 * compute the number of files and the number of bytes
696 * used up by the whole selection, recursing directories
697 * as required. In addition, it checks to see if it will
698 * overwrite any files by doing the copy.
701 static FileProgressStatus
702 panel_compute_totals (const WPanel * panel, dirsize_status_msg_t * sm, size_t * ret_count,
703 uintmax_t * ret_total, gboolean compute_symlinks)
705 int i;
706 size_t dir_count = 0;
708 for (i = 0; i < panel->dir.len; i++)
710 struct stat *s;
712 if (!panel->dir.list[i].f.marked)
713 continue;
715 s = &panel->dir.list[i].st;
717 if (S_ISDIR (s->st_mode))
719 vfs_path_t *p;
720 FileProgressStatus status;
722 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, (char *) NULL);
723 status = compute_dir_size (p, sm, &dir_count, ret_count, ret_total, compute_symlinks);
724 vfs_path_free (p);
726 if (status != FILE_CONT)
727 return status;
729 else
731 (*ret_count)++;
732 *ret_total += (uintmax_t) s->st_size;
736 return FILE_CONT;
739 /* --------------------------------------------------------------------------------------------- */
741 /** Initialize variables for progress bars */
742 static FileProgressStatus
743 panel_operate_init_totals (const WPanel * panel, const vfs_path_t * source,
744 const struct stat *source_stat, file_op_context_t * ctx,
745 gboolean compute_totals, filegui_dialog_type_t dialog_type)
747 FileProgressStatus status;
749 #ifdef ENABLE_BACKGROUND
750 if (mc_global.we_are_background)
751 return FILE_CONT;
752 #endif
754 if (verbose && compute_totals)
756 dirsize_status_msg_t dsm;
758 memset (&dsm, 0, sizeof (dsm));
759 dsm.allow_skip = TRUE;
760 status_msg_init (STATUS_MSG (&dsm), _("Directory scanning"), 0, dirsize_status_init_cb,
761 dirsize_status_update_cb, dirsize_status_deinit_cb);
763 ctx->progress_count = 0;
764 ctx->progress_bytes = 0;
766 if (source == NULL)
767 status = panel_compute_totals (panel, &dsm, &ctx->progress_count, &ctx->progress_bytes,
768 ctx->follow_links);
769 else if (S_ISDIR (source_stat->st_mode))
771 size_t dir_count = 0;
773 status = compute_dir_size (source, &dsm, &dir_count, &ctx->progress_count,
774 &ctx->progress_bytes, ctx->follow_links);
776 else
778 ctx->progress_count++;
779 ctx->progress_bytes += (uintmax_t) source_stat->st_size;
780 status = FILE_CONT;
783 status_msg_deinit (STATUS_MSG (&dsm));
785 ctx->progress_totals_computed = (status == FILE_CONT);
787 if (status == FILE_SKIP)
788 status = FILE_CONT;
790 else
792 status = FILE_CONT;
793 ctx->progress_count = panel->marked;
794 ctx->progress_bytes = panel->total;
795 ctx->progress_totals_computed = FALSE;
798 /* destroy already created UI for single file rename operation */
799 file_op_context_destroy_ui (ctx);
801 file_op_context_create_ui (ctx, TRUE, dialog_type);
803 return status;
806 /* --------------------------------------------------------------------------------------------- */
808 static FileProgressStatus
809 progress_update_one (file_op_total_context_t * tctx, file_op_context_t * ctx, off_t add)
811 struct timeval tv_current;
812 static struct timeval tv_start = { 0, 0 };
814 tctx->progress_count++;
815 tctx->progress_bytes += (uintmax_t) add;
817 if (tv_start.tv_sec == 0)
819 gettimeofday (&tv_start, (struct timezone *) NULL);
821 gettimeofday (&tv_current, (struct timezone *) NULL);
822 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
824 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
826 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
827 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
829 tv_start.tv_sec = tv_current.tv_sec;
832 return check_progress_buttons (ctx);
835 /* --------------------------------------------------------------------------------------------- */
837 static FileProgressStatus
838 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
840 char *msg;
841 int result = 0;
842 const char *head_msg;
844 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
846 msg = g_strdup_printf (fmt, a, b);
847 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
848 g_free (msg);
849 do_refresh ();
851 return (result == 1) ? FILE_ABORT : FILE_SKIP;
854 /* --------------------------------------------------------------------------------------------- */
856 static FileProgressStatus
857 warn_same_file (const char *fmt, const char *a, const char *b)
859 #ifdef ENABLE_BACKGROUND
860 /* *INDENT-OFF* */
861 union
863 void *p;
864 FileProgressStatus (*f) (enum OperationMode, const char *fmt, const char *a, const char *b);
865 } pntr;
866 /* *INDENT-ON* */
868 pntr.f = real_warn_same_file;
870 if (mc_global.we_are_background)
871 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
872 #endif
873 return real_warn_same_file (Foreground, fmt, a, b);
876 /* --------------------------------------------------------------------------------------------- */
878 static gboolean
879 check_same_file (const char *a, const struct stat *ast, const char *b, const struct stat *bst,
880 FileProgressStatus * status)
882 if (ast->st_dev != bst->st_dev || ast->st_ino != bst->st_ino)
883 return FALSE;
885 if (S_ISDIR (ast->st_mode))
886 *status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), a, b);
887 else
888 *status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), a, b);
890 return TRUE;
893 /* --------------------------------------------------------------------------------------------- */
895 static void
896 get_times (const struct stat *sb, mc_timesbuf_t * times)
898 #ifdef HAVE_UTIMENSAT
899 (*times)[0] = sb->st_atim;
900 (*times)[1] = sb->st_mtim;
901 #else
902 times->actime = sb->st_atime;
903 times->modtime = sb->st_mtime;
904 #endif
907 /* --------------------------------------------------------------------------------------------- */
908 /* {{{ Query/status report routines */
910 static FileProgressStatus
911 real_do_file_error (enum OperationMode mode, gboolean allow_retry, const char *error)
913 int result;
914 const char *msg;
916 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
918 if (allow_retry)
919 result =
920 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"),
921 _("&Abort"));
922 else
923 result = query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Abort"));
925 switch (result)
927 case 0:
928 do_refresh ();
929 return FILE_SKIP;
931 case 1:
932 do_refresh ();
933 return FILE_SKIPALL;
935 case 2:
936 if (allow_retry)
938 do_refresh ();
939 return FILE_RETRY;
941 MC_FALLTHROUGH;
943 case 3:
944 default:
945 return FILE_ABORT;
949 /* --------------------------------------------------------------------------------------------- */
951 static FileProgressStatus
952 real_query_recursive (file_op_context_t * ctx, enum OperationMode mode, const char *s)
954 if (ctx->recursive_result < RECURSIVE_ALWAYS)
956 const char *msg;
957 char *text;
959 msg = mode == Foreground
960 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
961 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
962 text = g_strdup_printf (msg, path_trunc (s, 30));
964 if (safe_delete)
965 query_set_sel (1);
967 ctx->recursive_result =
968 query_dialog (op_names[OP_DELETE], text, D_ERROR, 5, _("&Yes"), _("&No"), _("A&ll"),
969 _("Non&e"), _("&Abort"));
970 g_free (text);
972 if (ctx->recursive_result != RECURSIVE_ABORT)
973 do_refresh ();
976 switch (ctx->recursive_result)
978 case RECURSIVE_YES:
979 case RECURSIVE_ALWAYS:
980 return FILE_CONT;
982 case RECURSIVE_NO:
983 case RECURSIVE_NEVER:
984 return FILE_SKIP;
986 case RECURSIVE_ABORT:
987 default:
988 return FILE_ABORT;
992 /* --------------------------------------------------------------------------------------------- */
994 #ifdef ENABLE_BACKGROUND
995 static FileProgressStatus
996 do_file_error (gboolean allow_retry, const char *str)
998 /* *INDENT-OFF* */
999 union
1001 void *p;
1002 FileProgressStatus (*f) (enum OperationMode, gboolean, const char *);
1003 } pntr;
1004 /* *INDENT-ON* */
1006 pntr.f = real_do_file_error;
1008 if (mc_global.we_are_background)
1009 return parent_call (pntr.p, NULL, 2, sizeof (allow_retry), allow_retry, strlen (str), str);
1010 else
1011 return real_do_file_error (Foreground, allow_retry, str);
1014 /* --------------------------------------------------------------------------------------------- */
1016 static FileProgressStatus
1017 query_recursive (file_op_context_t * ctx, const char *s)
1019 /* *INDENT-OFF* */
1020 union
1022 void *p;
1023 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *);
1024 } pntr;
1025 /* *INDENT-ON* */
1027 pntr.f = real_query_recursive;
1029 if (mc_global.we_are_background)
1030 return parent_call (pntr.p, ctx, 1, strlen (s), s);
1031 else
1032 return real_query_recursive (ctx, Foreground, s);
1035 /* --------------------------------------------------------------------------------------------- */
1037 static FileProgressStatus
1038 query_replace (file_op_context_t * ctx, const char *destname, struct stat *_s_stat,
1039 struct stat *_d_stat)
1041 /* *INDENT-OFF* */
1042 union
1044 void *p;
1045 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *,
1046 struct stat *, struct stat *);
1047 } pntr;
1048 /* *INDENT-ON* */
1050 pntr.f = file_progress_real_query_replace;
1052 if (mc_global.we_are_background)
1053 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
1054 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
1055 else
1056 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
1059 #else
1060 /* --------------------------------------------------------------------------------------------- */
1062 static FileProgressStatus
1063 do_file_error (gboolean allow_retry, const char *str)
1065 return real_do_file_error (Foreground, allow_retry, str);
1068 /* --------------------------------------------------------------------------------------------- */
1070 static FileProgressStatus
1071 query_recursive (file_op_context_t * ctx, const char *s)
1073 return real_query_recursive (ctx, Foreground, s);
1076 /* --------------------------------------------------------------------------------------------- */
1078 static FileProgressStatus
1079 query_replace (file_op_context_t * ctx, const char *destname, struct stat *_s_stat,
1080 struct stat *_d_stat)
1082 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
1085 #endif /* !ENABLE_BACKGROUND */
1087 /* --------------------------------------------------------------------------------------------- */
1088 /** Report error with two files */
1090 static FileProgressStatus
1091 files_error (const char *format, const char *file1, const char *file2)
1093 char buf[BUF_MEDIUM];
1094 char *nfile1 = g_strdup (path_trunc (file1, 15));
1095 char *nfile2 = g_strdup (path_trunc (file2, 15));
1097 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
1099 g_free (nfile1);
1100 g_free (nfile2);
1102 return do_file_error (TRUE, buf);
1105 /* }}} */
1107 /* --------------------------------------------------------------------------------------------- */
1109 static void
1110 copy_file_file_display_progress (file_op_total_context_t * tctx, file_op_context_t * ctx,
1111 struct timeval tv_current, struct timeval tv_transfer_start,
1112 off_t file_size, off_t n_read_total)
1114 long dt;
1116 /* 1. Update rotating dash after some time */
1117 rotate_dash (TRUE);
1119 /* 3. Compute ETA */
1120 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
1122 if (n_read_total == 0)
1123 ctx->eta_secs = 0.0;
1124 else
1126 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
1127 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
1130 /* 4. Compute BPS rate */
1131 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
1132 if (ctx->bps_time < 1)
1133 ctx->bps_time = 1;
1134 ctx->bps = n_read_total / ctx->bps_time;
1136 /* 5. Compute total ETA and BPS */
1137 if (ctx->progress_bytes != 0)
1139 uintmax_t remain_bytes;
1141 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
1142 #if 1
1144 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
1146 if (total_secs < 1)
1147 total_secs = 1;
1149 tctx->bps = tctx->copied_bytes / total_secs;
1150 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
1152 #else
1153 /* broken on lot of little files */
1154 tctx->bps_count++;
1155 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
1156 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
1157 #endif
1161 /* --------------------------------------------------------------------------------------------- */
1163 static gboolean
1164 try_remove_file (file_op_context_t * ctx, const vfs_path_t * vpath, FileProgressStatus * status)
1166 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
1168 *status = file_error (TRUE, _("Cannot remove file \"%s\"\n%s"), vfs_path_as_str (vpath));
1169 if (*status == FILE_RETRY)
1170 continue;
1171 if (*status == FILE_SKIPALL)
1172 ctx->skip_all = TRUE;
1173 return FALSE;
1176 return TRUE;
1179 /* --------------------------------------------------------------------------------------------- */
1181 /* {{{ Move routines */
1184 * Move single file or one of many files from one location to another.
1186 * @panel pointer to panel in case of single file, NULL otherwise
1187 * @tctx file operation total context object
1188 * @ctx file operation context object
1189 * @s source file name
1190 * @d destination file name
1192 * @return operation result
1194 static FileProgressStatus
1195 move_file_file (const WPanel * panel, file_op_total_context_t * tctx, file_op_context_t * ctx,
1196 const char *s, const char *d)
1198 struct stat src_stats, dst_stats;
1199 FileProgressStatus return_status = FILE_CONT;
1200 gboolean copy_done = FALSE;
1201 gboolean old_ask_overwrite;
1202 vfs_path_t *src_vpath, *dst_vpath;
1204 src_vpath = vfs_path_from_str (s);
1205 dst_vpath = vfs_path_from_str (d);
1207 file_progress_show_source (ctx, src_vpath);
1208 file_progress_show_target (ctx, dst_vpath);
1210 /* FIXME: do we really need to check buttons in case of single file? */
1211 if (check_progress_buttons (ctx) == FILE_ABORT)
1213 return_status = FILE_ABORT;
1214 goto ret;
1217 mc_refresh ();
1219 while (mc_lstat (src_vpath, &src_stats) != 0)
1221 /* Source doesn't exist */
1222 if (ctx->skip_all)
1223 return_status = FILE_SKIPALL;
1224 else
1226 return_status = file_error (TRUE, _("Cannot stat file \"%s\"\n%s"), s);
1227 if (return_status == FILE_SKIPALL)
1228 ctx->skip_all = TRUE;
1231 if (return_status != FILE_RETRY)
1232 goto ret;
1235 if (mc_lstat (dst_vpath, &dst_stats) == 0)
1237 if (check_same_file (s, &src_stats, d, &dst_stats, &return_status))
1238 goto ret;
1240 if (S_ISDIR (dst_stats.st_mode))
1242 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
1243 do_refresh ();
1244 return_status = FILE_SKIP;
1245 goto ret;
1248 if (confirm_overwrite)
1250 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1251 if (return_status != FILE_CONT)
1252 goto ret;
1254 /* Ok to overwrite */
1257 if (!ctx->do_append)
1259 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
1261 return_status = make_symlink (ctx, s, d);
1262 if (return_status == FILE_CONT)
1263 goto retry_src_remove;
1264 goto ret;
1267 if (mc_rename (src_vpath, dst_vpath) == 0)
1269 /* FIXME: do we really need to update progress in case of single file? */
1270 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
1271 goto ret;
1274 #if 0
1275 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1276 one nfs to the same, but on the server it is on two different
1277 filesystems. Then nfs returns EIO instead of EXDEV.
1278 Hope it will not hurt if we always in case of error try to copy/delete. */
1279 else
1280 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1282 if (errno != EXDEV)
1284 if (ctx->skip_all)
1285 return_status = FILE_SKIPALL;
1286 else
1288 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
1289 if (return_status == FILE_SKIPALL)
1290 ctx->skip_all = TRUE;
1291 if (return_status == FILE_RETRY)
1292 goto retry_rename;
1295 goto ret;
1297 #endif
1299 /* Failed rename -> copy the file instead */
1300 if (panel != NULL)
1302 /* In case of single file, calculate totals. In case of many files,
1303 totals are calcuated already. */
1304 return_status =
1305 panel_operate_init_totals (panel, src_vpath, &src_stats, ctx, TRUE,
1306 FILEGUI_DIALOG_ONE_ITEM);
1307 if (return_status != FILE_CONT)
1308 goto ret;
1311 old_ask_overwrite = tctx->ask_overwrite;
1312 tctx->ask_overwrite = FALSE;
1313 return_status = copy_file_file (tctx, ctx, s, d);
1314 tctx->ask_overwrite = old_ask_overwrite;
1315 if (return_status != FILE_CONT)
1316 goto ret;
1318 copy_done = TRUE;
1320 /* FIXME: there is no need to update progress and check buttons
1321 at the finish of single file operation. */
1322 if (panel == NULL)
1324 file_progress_show_source (ctx, NULL);
1325 file_progress_show (ctx, 0, 0, "", FALSE);
1327 return_status = check_progress_buttons (ctx);
1328 if (return_status != FILE_CONT)
1329 goto ret;
1332 mc_refresh ();
1334 retry_src_remove:
1335 if (!try_remove_file (ctx, src_vpath, &return_status) && panel == NULL)
1336 goto ret;
1338 if (!copy_done)
1339 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
1341 ret:
1342 vfs_path_free (src_vpath);
1343 vfs_path_free (dst_vpath);
1345 return return_status;
1348 /* }}} */
1350 /* --------------------------------------------------------------------------------------------- */
1351 /* {{{ Erase routines */
1352 /** Don't update progress status if progress_count==NULL */
1354 static FileProgressStatus
1355 erase_file (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * vpath)
1357 struct stat buf;
1358 FileProgressStatus return_status;
1360 file_progress_show_deleting (ctx, vfs_path_as_str (vpath), &tctx->progress_count);
1361 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1362 if (check_progress_buttons (ctx) == FILE_ABORT)
1363 return FILE_ABORT;
1365 mc_refresh ();
1367 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
1369 /* ignore, most likely the mc_unlink fails, too */
1370 buf.st_size = 0;
1373 if (!try_remove_file (ctx, vpath, &return_status) && return_status == FILE_ABORT)
1374 return FILE_ABORT;
1376 if (tctx->progress_count == 0)
1377 return FILE_CONT;
1379 return check_progress_buttons (ctx);
1382 /* --------------------------------------------------------------------------------------------- */
1384 static FileProgressStatus
1385 try_erase_dir (file_op_context_t * ctx, const char *dir)
1387 FileProgressStatus return_status = FILE_CONT;
1389 while (my_rmdir (dir) != 0 && !ctx->skip_all)
1391 return_status = file_error (TRUE, _("Cannot remove directory \"%s\"\n%s"), dir);
1392 if (return_status == FILE_SKIPALL)
1393 ctx->skip_all = TRUE;
1394 if (return_status != FILE_RETRY)
1395 break;
1398 return return_status;
1401 /* --------------------------------------------------------------------------------------------- */
1404 Recursive remove of files
1405 abort->cancel stack
1406 skip ->warn every level, gets default
1407 skipall->remove as much as possible
1409 static FileProgressStatus
1410 recursive_erase (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * vpath)
1412 struct dirent *next;
1413 DIR *reading;
1414 const char *s;
1415 FileProgressStatus return_status = FILE_CONT;
1417 reading = mc_opendir (vpath);
1418 if (reading == NULL)
1419 return FILE_RETRY;
1421 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1423 vfs_path_t *tmp_vpath;
1424 struct stat buf;
1426 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
1427 continue;
1429 tmp_vpath = vfs_path_append_new (vpath, next->d_name, (char *) NULL);
1430 if (mc_lstat (tmp_vpath, &buf) != 0)
1432 mc_closedir (reading);
1433 vfs_path_free (tmp_vpath);
1434 return FILE_RETRY;
1436 if (S_ISDIR (buf.st_mode))
1437 return_status = recursive_erase (tctx, ctx, tmp_vpath);
1438 else
1439 return_status = erase_file (tctx, ctx, tmp_vpath);
1440 vfs_path_free (tmp_vpath);
1442 mc_closedir (reading);
1444 if (return_status == FILE_ABORT)
1445 return FILE_ABORT;
1447 s = vfs_path_as_str (vpath);
1449 file_progress_show_deleting (ctx, s, NULL);
1450 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1451 if (check_progress_buttons (ctx) == FILE_ABORT)
1452 return FILE_ABORT;
1454 mc_refresh ();
1456 return try_erase_dir (ctx, s);
1459 /* --------------------------------------------------------------------------------------------- */
1460 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1461 in the directory path points to, 0 else. */
1463 static int
1464 check_dir_is_empty (const vfs_path_t * vpath)
1466 DIR *dir;
1467 struct dirent *d;
1468 int i = 1;
1470 dir = mc_opendir (vpath);
1471 if (dir == NULL)
1472 return -1;
1474 for (d = mc_readdir (dir); d != NULL; d = mc_readdir (dir))
1475 if (!DIR_IS_DOT (d->d_name) && !DIR_IS_DOTDOT (d->d_name))
1477 i = 0;
1478 break;
1481 mc_closedir (dir);
1482 return i;
1485 /* --------------------------------------------------------------------------------------------- */
1487 static FileProgressStatus
1488 erase_dir_iff_empty (file_op_context_t * ctx, const vfs_path_t * vpath, size_t count)
1490 const char *s;
1492 s = vfs_path_as_str (vpath);
1494 file_progress_show_deleting (ctx, s, NULL);
1495 file_progress_show_count (ctx, count, ctx->progress_count);
1496 if (check_progress_buttons (ctx) == FILE_ABORT)
1497 return FILE_ABORT;
1499 mc_refresh ();
1501 if (check_dir_is_empty (vpath) != 1)
1502 return FILE_CONT;
1504 /* not empty or error */
1505 return try_erase_dir (ctx, s);
1509 /* --------------------------------------------------------------------------------------------- */
1511 static void
1512 erase_dir_after_copy (file_op_total_context_t * tctx, file_op_context_t * ctx,
1513 const vfs_path_t * vpath, FileProgressStatus * status)
1515 if (ctx->erase_at_end)
1517 /* Reset progress count before delete to avoid counting files twice */
1518 tctx->progress_count = tctx->prev_progress_count;
1520 while (erase_list != NULL && *status != FILE_ABORT)
1522 struct link *lp = (struct link *) erase_list->data;
1524 if (S_ISDIR (lp->st_mode))
1525 *status = erase_dir_iff_empty (ctx, lp->src_vpath, tctx->progress_count);
1526 else
1527 *status = erase_file (tctx, ctx, lp->src_vpath);
1529 erase_list = g_slist_remove (erase_list, lp);
1530 free_link (lp);
1533 /* Save progress counter before move next directory */
1534 tctx->prev_progress_count = tctx->progress_count;
1537 erase_dir_iff_empty (ctx, vpath, tctx->progress_count);
1540 /* }}} */
1542 /* --------------------------------------------------------------------------------------------- */
1545 * Move single directory or one of many directories from one location to another.
1547 * @panel pointer to panel in case of single directory, NULL otherwise
1548 * @tctx file operation total context object
1549 * @ctx file operation context object
1550 * @s source directory name
1551 * @d destination directory name
1553 * @return operation result
1555 static FileProgressStatus
1556 do_move_dir_dir (const WPanel * panel, file_op_total_context_t * tctx, file_op_context_t * ctx,
1557 const char *s, const char *d)
1559 struct stat sbuf, dbuf;
1560 FileProgressStatus return_status = FILE_CONT;
1561 gboolean move_over = FALSE;
1562 gboolean dstat_ok;
1563 vfs_path_t *src_vpath, *dst_vpath;
1565 src_vpath = vfs_path_from_str (s);
1566 dst_vpath = vfs_path_from_str (d);
1568 file_progress_show_source (ctx, src_vpath);
1569 file_progress_show_target (ctx, dst_vpath);
1571 /* FIXME: do we really need to check buttons in case of single directory? */
1572 if (panel != NULL && check_progress_buttons (ctx) == FILE_ABORT)
1574 return_status = FILE_ABORT;
1575 goto ret_fast;
1578 mc_refresh ();
1580 mc_stat (src_vpath, &sbuf);
1582 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
1584 if (dstat_ok && check_same_file (s, &sbuf, d, &dbuf, &return_status))
1585 goto ret_fast;
1587 if (!dstat_ok)
1588 ; /* destination doesn't exist */
1589 else if (!ctx->dive_into_subdirs)
1590 move_over = TRUE;
1591 else
1593 vfs_path_t *tmp;
1595 tmp = dst_vpath;
1596 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
1597 vfs_path_free (tmp);
1600 d = vfs_path_as_str (dst_vpath);
1602 /* Check if the user inputted an existing dir */
1603 retry_dst_stat:
1604 if (mc_stat (dst_vpath, &dbuf) == 0)
1606 if (move_over)
1608 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, TRUE, TRUE, NULL);
1610 if (return_status != FILE_CONT)
1611 goto ret;
1612 goto oktoret;
1614 else if (ctx->skip_all)
1615 return_status = FILE_SKIPALL;
1616 else
1618 if (S_ISDIR (dbuf.st_mode))
1619 return_status = file_error (TRUE, _("Cannot overwrite directory \"%s\"\n%s"), d);
1620 else
1621 return_status = file_error (TRUE, _("Cannot overwrite file \"%s\"\n%s"), d);
1622 if (return_status == FILE_SKIPALL)
1623 ctx->skip_all = TRUE;
1624 if (return_status == FILE_RETRY)
1625 goto retry_dst_stat;
1628 goto ret_fast;
1631 retry_rename:
1632 if (mc_rename (src_vpath, dst_vpath) == 0)
1634 return_status = FILE_CONT;
1635 goto ret;
1638 if (errno != EXDEV)
1640 if (!ctx->skip_all)
1642 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
1643 if (return_status == FILE_SKIPALL)
1644 ctx->skip_all = TRUE;
1645 if (return_status == FILE_RETRY)
1646 goto retry_rename;
1648 goto ret;
1651 /* Failed because of filesystem boundary -> copy dir instead */
1652 if (panel != NULL)
1654 /* In case of single directory, calculate totals. In case of many directories,
1655 totals are calcuated already. */
1656 return_status =
1657 panel_operate_init_totals (panel, src_vpath, &sbuf, ctx, FALSE,
1658 FILEGUI_DIALOG_ONE_ITEM);
1659 if (return_status != FILE_CONT)
1660 goto ret;
1663 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, FALSE, TRUE, NULL);
1665 if (return_status != FILE_CONT)
1666 goto ret;
1668 oktoret:
1669 /* FIXME: there is no need to update progress and check buttons
1670 at the finish of single directory operation. */
1671 if (panel == NULL)
1673 file_progress_show_source (ctx, NULL);
1674 file_progress_show_target (ctx, NULL);
1675 file_progress_show (ctx, 0, 0, "", FALSE);
1677 return_status = check_progress_buttons (ctx);
1678 if (return_status != FILE_CONT)
1679 goto ret;
1682 mc_refresh ();
1684 erase_dir_after_copy (tctx, ctx, src_vpath, &return_status);
1686 ret:
1687 erase_list = free_linklist (erase_list);
1688 ret_fast:
1689 vfs_path_free (src_vpath);
1690 vfs_path_free (dst_vpath);
1691 return return_status;
1694 /* --------------------------------------------------------------------------------------------- */
1696 /* {{{ Panel operate routines */
1699 * Return currently selected entry name or the name of the first marked
1700 * entry if there is one.
1703 static const char *
1704 panel_get_file (const WPanel * panel)
1706 if (get_current_type () == view_tree)
1708 WTree *tree;
1709 const vfs_path_t *selected_name;
1711 tree = (WTree *) get_panel_widget (get_current_index ());
1712 selected_name = tree_selected_name (tree);
1713 return vfs_path_as_str (selected_name);
1716 if (panel->marked != 0)
1718 int i;
1720 for (i = 0; i < panel->dir.len; i++)
1721 if (panel->dir.list[i].f.marked)
1722 return panel->dir.list[i].fname;
1725 return panel->dir.list[panel->selected].fname;
1728 /* --------------------------------------------------------------------------------------------- */
1730 static const char *
1731 check_single_entry (const WPanel * panel, gboolean force_single, struct stat *src_stat)
1733 const char *source;
1734 gboolean ok;
1736 if (force_single)
1737 source = selection (panel)->fname;
1738 else
1739 source = panel_get_file (panel);
1741 ok = !DIR_IS_DOTDOT (source);
1743 if (!ok)
1744 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
1745 else
1747 vfs_path_t *source_vpath;
1749 source_vpath = vfs_path_from_str (source);
1751 /* Update stat to get actual info */
1752 ok = mc_lstat (source_vpath, src_stat) == 0;
1753 if (!ok)
1755 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
1756 path_trunc (source, 30), unix_error_string (errno));
1758 /* Directory was changed outside MC. Reload it forced */
1759 if (!panel->is_panelized)
1761 panel_update_flags_t flags = UP_RELOAD;
1763 /* don't update panelized panel */
1764 if (get_other_type () == view_listing && other_panel->is_panelized)
1765 flags |= UP_ONLY_CURRENT;
1767 update_panels (flags, UP_KEEPSEL);
1771 vfs_path_free (source_vpath);
1774 return ok ? source : NULL;
1777 /* --------------------------------------------------------------------------------------------- */
1779 * Generate user prompt for panel operation.
1780 * src_stat must be not NULL for single source, and NULL for multiple sources
1783 static char *
1784 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1785 const struct stat *src_stat)
1787 char *sp;
1788 char *format_string;
1789 const char *cp;
1791 static gboolean i18n_flag = FALSE;
1792 if (!i18n_flag)
1794 size_t i;
1796 for (i = G_N_ELEMENTS (op_names1); i-- != 0;)
1797 op_names1[i] = Q_ (op_names1[i]);
1799 #ifdef ENABLE_NLS
1800 for (i = G_N_ELEMENTS (prompt_parts); i-- != 0;)
1801 prompt_parts[i] = _(prompt_parts[i]);
1803 one_format = _(one_format);
1804 many_format = _(many_format);
1805 #endif /* ENABLE_NLS */
1806 i18n_flag = TRUE;
1809 /* Possible prompts:
1810 * OP_COPY:
1811 * "Copy file \"%s\" with source mask:"
1812 * "Copy %d files with source mask:"
1813 * "Copy directory \"%s\" with source mask:"
1814 * "Copy %d directories with source mask:"
1815 * "Copy %d files/directories with source mask:"
1816 * OP_MOVE:
1817 * "Move file \"%s\" with source mask:"
1818 * "Move %d files with source mask:"
1819 * "Move directory \"%s\" with source mask:"
1820 * "Move %d directories with source mask:"
1821 * "Move %d files/directories with source mask:"
1822 * OP_DELETE:
1823 * "Delete file \"%s\"?"
1824 * "Delete %d files?"
1825 * "Delete directory \"%s\"?"
1826 * "Delete %d directories?"
1827 * "Delete %d files/directories?"
1830 cp = (src_stat != NULL ? one_format : many_format);
1832 /* 1. Substitute %o */
1833 format_string = str_replace_all (cp, "%o", op_names1[(int) operation]);
1835 /* 2. Substitute %n */
1836 cp = operation == OP_DELETE ? "\n" : " ";
1837 sp = format_string;
1838 format_string = str_replace_all (sp, "%n", cp);
1839 g_free (sp);
1841 /* 3. Substitute %f */
1842 if (src_stat != NULL)
1843 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1844 else if (panel->marked == panel->dirs_marked)
1845 cp = prompt_parts[3];
1846 else
1847 cp = panel->dirs_marked != 0 ? prompt_parts[4] : prompt_parts[1];
1849 sp = format_string;
1850 format_string = str_replace_all (sp, "%f", cp);
1851 g_free (sp);
1853 /* 4. Substitute %m */
1854 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1855 sp = format_string;
1856 format_string = str_replace_all (sp, "%m", cp);
1857 g_free (sp);
1859 return format_string;
1862 /* --------------------------------------------------------------------------------------------- */
1864 static char *
1865 do_confirm_copy_move (const WPanel * panel, FileOperation operation, gboolean force_single,
1866 const char *source, struct stat *src_stat, file_op_context_t * ctx,
1867 gboolean * do_bg)
1869 const char *tmp_dest_dir;
1870 char *dest_dir;
1871 char *format;
1872 char *ret;
1874 /* Forced single operations default to the original name */
1875 if (force_single)
1876 tmp_dest_dir = source;
1877 else if (get_other_type () == view_listing)
1878 tmp_dest_dir = vfs_path_as_str (other_panel->cwd_vpath);
1879 else
1880 tmp_dest_dir = vfs_path_as_str (panel->cwd_vpath);
1883 * Add trailing backslash only when do non-local ops.
1884 * It saves user from occasional file renames (when destination
1885 * dir is deleted)
1887 if (!force_single && tmp_dest_dir != NULL && tmp_dest_dir[0] != '\0'
1888 && !IS_PATH_SEP (tmp_dest_dir[strlen (tmp_dest_dir) - 1]))
1890 /* add trailing separator */
1891 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
1893 else
1895 /* just copy */
1896 dest_dir = g_strdup (tmp_dest_dir);
1899 if (dest_dir == NULL)
1900 return NULL;
1902 if (source == NULL)
1903 src_stat = NULL;
1905 /* Generate confirmation prompt */
1906 format = panel_operate_generate_prompt (panel, operation, src_stat);
1908 ret = file_mask_dialog (ctx, operation, source != NULL, format,
1909 source != NULL ? source : (const void *) &panel->marked, dest_dir,
1910 do_bg);
1912 g_free (format);
1913 g_free (dest_dir);
1915 return ret;
1918 /* --------------------------------------------------------------------------------------------- */
1920 static gboolean
1921 do_confirm_erase (const WPanel * panel, const char *source, struct stat *src_stat)
1923 int i;
1924 char *format;
1925 char fmd_buf[BUF_MEDIUM];
1927 if (source == NULL)
1928 src_stat = NULL;
1930 /* Generate confirmation prompt */
1931 format = panel_operate_generate_prompt (panel, OP_DELETE, src_stat);
1933 if (source == NULL)
1934 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
1935 else
1937 const int fmd_xlen = 64;
1939 i = fmd_xlen - str_term_width1 (format) - 4;
1940 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
1943 g_free (format);
1945 if (safe_delete)
1946 query_set_sel (1);
1948 i = query_dialog (op_names[OP_DELETE], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
1950 return (i == 0);
1953 /* --------------------------------------------------------------------------------------------- */
1955 static FileProgressStatus
1956 operate_single_file (const WPanel * panel, FileOperation operation, file_op_total_context_t * tctx,
1957 file_op_context_t * ctx, const char *src, struct stat *src_stat,
1958 const char *dest, filegui_dialog_type_t dialog_type)
1960 FileProgressStatus value;
1961 vfs_path_t *src_vpath;
1962 gboolean is_file;
1964 if (g_path_is_absolute (src))
1965 src_vpath = vfs_path_from_str (src);
1966 else
1967 src_vpath = vfs_path_append_new (panel->cwd_vpath, src, (char *) NULL);
1969 is_file = !S_ISDIR (src_stat->st_mode);
1970 /* Is link to directory? */
1971 if (is_file)
1973 gboolean is_link;
1975 is_link = file_is_symlink_to_dir (src_vpath, src_stat, NULL);
1976 is_file = !(is_link && ctx->follow_links);
1980 if (operation == OP_DELETE)
1982 value = panel_operate_init_totals (panel, src_vpath, src_stat, ctx, !is_file, dialog_type);
1983 if (value == FILE_CONT)
1985 if (is_file)
1986 value = erase_file (tctx, ctx, src_vpath);
1987 else
1988 value = erase_dir (tctx, ctx, src_vpath);
1991 else
1993 char *temp;
1995 src = vfs_path_as_str (src_vpath);
1997 temp = build_dest (ctx, src, dest, &value);
1998 if (temp != NULL)
2000 dest = temp;
2002 switch (operation)
2004 case OP_COPY:
2005 /* we use file_mask_op_follow_links only with OP_COPY */
2006 ctx->stat_func (src_vpath, src_stat);
2008 value =
2009 panel_operate_init_totals (panel, src_vpath, src_stat, ctx, !is_file,
2010 dialog_type);
2011 if (value == FILE_CONT)
2013 is_file = !S_ISDIR (src_stat->st_mode);
2014 /* Is link to directory? */
2015 if (is_file)
2017 gboolean is_link;
2019 is_link = file_is_symlink_to_dir (src_vpath, src_stat, NULL);
2020 is_file = !(is_link && ctx->follow_links);
2023 if (is_file)
2024 value = copy_file_file (tctx, ctx, src, dest);
2025 else
2026 value = copy_dir_dir (tctx, ctx, src, dest, TRUE, FALSE, FALSE, NULL);
2028 break;
2030 case OP_MOVE:
2031 #ifdef ENABLE_BACKGROUND
2032 /* create UI to show confirmation dialog */
2033 if (!mc_global.we_are_background)
2034 file_op_context_create_ui (ctx, TRUE, FILEGUI_DIALOG_ONE_ITEM);
2035 #endif
2036 if (is_file)
2037 value = move_file_file (panel, tctx, ctx, src, dest);
2038 else
2039 value = do_move_dir_dir (panel, tctx, ctx, src, dest);
2040 break;
2042 default:
2043 /* Unknown file operation */
2044 abort ();
2047 g_free (temp);
2051 vfs_path_free (src_vpath);
2053 return value;
2056 /* --------------------------------------------------------------------------------------------- */
2058 static FileProgressStatus
2059 operate_one_file (const WPanel * panel, FileOperation operation, file_op_total_context_t * tctx,
2060 file_op_context_t * ctx, const char *src, struct stat *src_stat, const char *dest)
2062 FileProgressStatus value = FILE_CONT;
2063 vfs_path_t *src_vpath;
2064 gboolean is_file;
2066 if (g_path_is_absolute (src))
2067 src_vpath = vfs_path_from_str (src);
2068 else
2069 src_vpath = vfs_path_append_new (panel->cwd_vpath, src, (char *) NULL);
2071 is_file = !S_ISDIR (src_stat->st_mode);
2073 if (operation == OP_DELETE)
2075 if (is_file)
2076 value = erase_file (tctx, ctx, src_vpath);
2077 else
2078 value = erase_dir (tctx, ctx, src_vpath);
2080 else
2082 char *temp;
2084 src = vfs_path_as_str (src_vpath);
2086 temp = build_dest (ctx, src, dest, &value);
2087 if (temp != NULL)
2089 dest = temp;
2091 switch (operation)
2093 case OP_COPY:
2094 /* we use file_mask_op_follow_links only with OP_COPY */
2095 ctx->stat_func (src_vpath, src_stat);
2096 is_file = !S_ISDIR (src_stat->st_mode);
2098 if (is_file)
2099 value = copy_file_file (tctx, ctx, src, dest);
2100 else
2101 value = copy_dir_dir (tctx, ctx, src, dest, TRUE, FALSE, FALSE, NULL);
2102 dest_dirs = free_linklist (dest_dirs);
2103 break;
2105 case OP_MOVE:
2106 if (is_file)
2107 value = move_file_file (NULL, tctx, ctx, src, dest);
2108 else
2109 value = do_move_dir_dir (NULL, tctx, ctx, src, dest);
2110 break;
2112 default:
2113 /* Unknown file operation */
2114 abort ();
2117 g_free (temp);
2121 vfs_path_free (src_vpath);
2123 return value;
2126 /* --------------------------------------------------------------------------------------------- */
2128 #ifdef ENABLE_BACKGROUND
2129 static int
2130 end_bg_process (file_op_context_t * ctx, enum OperationMode mode)
2132 int pid = ctx->pid;
2134 (void) mode;
2135 ctx->pid = 0;
2137 unregister_task_with_pid (pid);
2138 /* file_op_context_destroy(ctx); */
2139 return 1;
2141 #endif
2142 /* }}} */
2144 /* --------------------------------------------------------------------------------------------- */
2145 /*** public functions ****************************************************************************/
2146 /* --------------------------------------------------------------------------------------------- */
2148 /* Is file symlink to directory or not.
2150 * @param path file or directory
2151 * @param st result of mc_lstat(vpath). If NULL, mc_lstat(vpath) is performed here
2152 * @param stale_link TRUE if file is stale link to directory
2154 * @return TRUE if file symlink to directory, ELSE otherwise.
2156 gboolean
2157 file_is_symlink_to_dir (const vfs_path_t * vpath, struct stat * st, gboolean * stale_link)
2159 struct stat st2;
2160 gboolean stale = FALSE;
2161 gboolean res = FALSE;
2163 if (st == NULL)
2165 st = &st2;
2167 if (mc_lstat (vpath, st) != 0)
2168 goto ret;
2171 if (S_ISLNK (st->st_mode))
2173 struct stat st3;
2175 stale = (mc_stat (vpath, &st3) != 0);
2177 if (!stale)
2178 res = (S_ISDIR (st3.st_mode) != 0);
2181 ret:
2182 if (stale_link != NULL)
2183 *stale_link = stale;
2185 return res;
2188 /* --------------------------------------------------------------------------------------------- */
2190 FileProgressStatus
2191 copy_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx,
2192 const char *src_path, const char *dst_path)
2194 uid_t src_uid = (uid_t) (-1);
2195 gid_t src_gid = (gid_t) (-1);
2197 int src_desc, dest_desc = -1;
2198 mode_t src_mode = 0; /* The mode of the source file */
2199 struct stat src_stat, dst_stat;
2200 mc_timesbuf_t times;
2201 gboolean dst_exists = FALSE, appending = FALSE;
2202 off_t file_size = -1;
2203 FileProgressStatus return_status, temp_status;
2204 struct timeval tv_transfer_start;
2205 dest_status_t dst_status = DEST_NONE;
2206 int open_flags;
2207 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
2208 char *buf = NULL;
2210 /* FIXME: We should not be using global variables! */
2211 ctx->do_reget = 0;
2212 return_status = FILE_RETRY;
2214 dst_vpath = vfs_path_from_str (dst_path);
2215 src_vpath = vfs_path_from_str (src_path);
2217 file_progress_show_source (ctx, src_vpath);
2218 file_progress_show_target (ctx, dst_vpath);
2220 if (check_progress_buttons (ctx) == FILE_ABORT)
2222 return_status = FILE_ABORT;
2223 goto ret_fast;
2226 mc_refresh ();
2228 while (mc_stat (dst_vpath, &dst_stat) == 0)
2230 if (S_ISDIR (dst_stat.st_mode))
2232 if (ctx->skip_all)
2233 return_status = FILE_SKIPALL;
2234 else
2236 return_status =
2237 file_error (TRUE, _("Cannot overwrite directory \"%s\"\n%s"), dst_path);
2238 if (return_status == FILE_SKIPALL)
2239 ctx->skip_all = TRUE;
2240 if (return_status == FILE_RETRY)
2241 continue;
2243 goto ret_fast;
2246 dst_exists = TRUE;
2247 break;
2250 while ((*ctx->stat_func) (src_vpath, &src_stat) != 0)
2252 if (ctx->skip_all)
2253 return_status = FILE_SKIPALL;
2254 else
2256 return_status = file_error (TRUE, _("Cannot stat source file \"%s\"\n%s"), src_path);
2257 if (return_status == FILE_SKIPALL)
2258 ctx->skip_all = TRUE;
2261 if (return_status != FILE_RETRY)
2262 goto ret_fast;
2265 if (dst_exists)
2267 /* Destination already exists */
2268 if (check_same_file (src_path, &src_stat, dst_path, &dst_stat, &return_status))
2269 goto ret_fast;
2271 /* Should we replace destination? */
2272 if (tctx->ask_overwrite)
2274 ctx->do_reget = 0;
2275 return_status = query_replace (ctx, dst_path, &src_stat, &dst_stat);
2276 if (return_status != FILE_CONT)
2277 goto ret_fast;
2281 if (!ctx->do_append)
2283 /* Check the hardlinks */
2284 if (!ctx->follow_links)
2286 switch (check_hardlinks (src_vpath, &src_stat, dst_vpath, &ctx->skip_all))
2288 case HARDLINK_OK:
2289 /* We have made a hardlink - no more processing is necessary */
2290 return_status = FILE_CONT;
2291 goto ret_fast;
2293 case HARDLINK_ABORT:
2294 return_status = FILE_ABORT;
2295 goto ret_fast;
2297 default:
2298 break;
2302 if (S_ISLNK (src_stat.st_mode))
2304 return_status = make_symlink (ctx, src_path, dst_path);
2305 goto ret_fast;
2308 if (S_ISCHR (src_stat.st_mode) || S_ISBLK (src_stat.st_mode) || S_ISFIFO (src_stat.st_mode)
2309 || S_ISNAM (src_stat.st_mode) || S_ISSOCK (src_stat.st_mode))
2311 dev_t rdev = 0;
2313 #ifdef HAVE_STRUCT_STAT_ST_RDEV
2314 rdev = src_stat.st_rdev;
2315 #endif
2317 while (mc_mknod (dst_vpath, src_stat.st_mode & ctx->umask_kill, rdev) < 0
2318 && !ctx->skip_all)
2320 return_status =
2321 file_error (TRUE, _("Cannot create special file \"%s\"\n%s"), dst_path);
2322 if (return_status == FILE_RETRY)
2323 continue;
2324 if (return_status == FILE_SKIPALL)
2325 ctx->skip_all = TRUE;
2326 goto ret_fast;
2328 /* Success */
2330 while (ctx->preserve_uidgid
2331 && mc_chown (dst_vpath, src_stat.st_uid, src_stat.st_gid) != 0 && !ctx->skip_all)
2333 temp_status = file_error (TRUE, _("Cannot chown target file \"%s\"\n%s"), dst_path);
2334 if (temp_status == FILE_SKIP)
2335 break;
2336 if (temp_status == FILE_SKIPALL)
2337 ctx->skip_all = TRUE;
2338 if (temp_status != FILE_RETRY)
2340 return_status = temp_status;
2341 goto ret_fast;
2345 while (ctx->preserve && mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill) != 0
2346 && !ctx->skip_all)
2348 temp_status = file_error (TRUE, _("Cannot chmod target file \"%s\"\n%s"), dst_path);
2349 if (temp_status == FILE_SKIP)
2350 break;
2351 if (temp_status == FILE_SKIPALL)
2352 ctx->skip_all = TRUE;
2353 if (temp_status != FILE_RETRY)
2355 return_status = temp_status;
2356 goto ret_fast;
2360 return_status = FILE_CONT;
2361 goto ret_fast;
2365 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
2367 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
2369 return_status = file_error (TRUE, _("Cannot open source file \"%s\"\n%s"), src_path);
2370 if (return_status == FILE_RETRY)
2371 continue;
2372 if (return_status == FILE_SKIPALL)
2373 ctx->skip_all = TRUE;
2374 if (return_status == FILE_SKIP)
2375 break;
2376 ctx->do_append = FALSE;
2377 goto ret_fast;
2380 if (ctx->do_reget != 0)
2382 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
2384 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
2385 ctx->do_reget = 0;
2386 ctx->do_append = FALSE;
2390 while (mc_fstat (src_desc, &src_stat) != 0)
2392 if (ctx->skip_all)
2393 return_status = FILE_SKIPALL;
2394 else
2396 return_status = file_error (TRUE, _("Cannot fstat source file \"%s\"\n%s"), src_path);
2397 if (return_status == FILE_RETRY)
2398 continue;
2399 if (return_status == FILE_SKIPALL)
2400 ctx->skip_all = TRUE;
2401 ctx->do_append = FALSE;
2403 goto ret;
2406 src_mode = src_stat.st_mode;
2407 src_uid = src_stat.st_uid;
2408 src_gid = src_stat.st_gid;
2409 get_times (&src_stat, &times);
2410 file_size = src_stat.st_size;
2412 open_flags = O_WRONLY;
2413 if (dst_exists)
2415 if (ctx->do_append)
2416 open_flags |= O_APPEND;
2417 else
2418 open_flags |= O_CREAT | O_TRUNC;
2420 else
2422 open_flags |= O_CREAT | O_EXCL;
2425 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
2427 if (errno != EEXIST)
2429 if (ctx->skip_all)
2430 return_status = FILE_SKIPALL;
2431 else
2433 return_status =
2434 file_error (TRUE, _("Cannot create target file \"%s\"\n%s"), dst_path);
2435 if (return_status == FILE_RETRY)
2436 continue;
2437 if (return_status == FILE_SKIPALL)
2438 ctx->skip_all = TRUE;
2439 ctx->do_append = FALSE;
2442 goto ret;
2444 dst_status = DEST_SHORT; /* file opened, but not fully copied */
2446 appending = ctx->do_append;
2447 ctx->do_append = FALSE;
2449 /* Try clone the file first. */
2450 if (vfs_clone_file (dest_desc, src_desc) == 0)
2452 dst_status = DEST_FULL;
2453 return_status = FILE_CONT;
2454 goto ret;
2457 /* Find out the optimal buffer size. */
2458 while (mc_fstat (dest_desc, &dst_stat) != 0)
2460 if (ctx->skip_all)
2461 return_status = FILE_SKIPALL;
2462 else
2464 return_status = file_error (TRUE, _("Cannot fstat target file \"%s\"\n%s"), dst_path);
2465 if (return_status == FILE_RETRY)
2466 continue;
2467 if (return_status == FILE_SKIPALL)
2468 ctx->skip_all = TRUE;
2470 goto ret;
2473 /* try preallocate space; if fail, try copy anyway */
2474 while (mc_global.vfs.preallocate_space &&
2475 vfs_preallocate (dest_desc, file_size, appending ? dst_stat.st_size : 0) != 0)
2477 if (ctx->skip_all)
2479 /* cannot allocate, start the file copying anyway */
2480 return_status = FILE_CONT;
2481 break;
2484 return_status =
2485 file_error (TRUE, _("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
2487 if (return_status == FILE_SKIPALL)
2488 ctx->skip_all = TRUE;
2490 if (ctx->skip_all || return_status == FILE_SKIP)
2492 /* skip the space allocation error, start file copying */
2493 return_status = FILE_CONT;
2494 break;
2497 if (return_status == FILE_ABORT)
2499 mc_close (dest_desc);
2500 dest_desc = -1;
2501 mc_unlink (dst_vpath);
2502 dst_status = DEST_NONE;
2503 goto ret;
2506 /* return_status == FILE_RETRY -- try allocate space again */
2509 ctx->eta_secs = 0.0;
2510 ctx->bps = 0;
2512 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
2513 file_progress_show (ctx, 0, file_size, "", TRUE);
2514 else
2515 file_progress_show (ctx, 1, 1, "", TRUE);
2516 return_status = check_progress_buttons (ctx);
2517 mc_refresh ();
2519 if (return_status == FILE_CONT)
2521 size_t bufsize;
2522 off_t n_read_total = 0;
2523 struct timeval tv_current, tv_last_update, tv_last_input;
2524 int secs, update_secs;
2525 const char *stalled_msg = "";
2526 gboolean is_first_time = TRUE;
2528 tv_last_update = tv_transfer_start;
2530 bufsize = io_blksize (dst_stat);
2531 buf = g_malloc (bufsize);
2533 while (TRUE)
2535 ssize_t n_read = -1, n_written;
2537 /* src_read */
2538 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0) == 0)
2539 while ((n_read = mc_read (src_desc, buf, bufsize)) < 0 && !ctx->skip_all)
2541 return_status =
2542 file_error (TRUE, _("Cannot read source file \"%s\"\n%s"), src_path);
2543 if (return_status == FILE_RETRY)
2544 continue;
2545 if (return_status == FILE_SKIPALL)
2546 ctx->skip_all = TRUE;
2547 goto ret;
2550 if (n_read == 0)
2551 break;
2553 gettimeofday (&tv_current, NULL);
2555 if (n_read > 0)
2557 char *t = buf;
2559 n_read_total += n_read;
2561 /* Windows NT ftp servers report that files have no
2562 * permissions: -------, so if we happen to have actually
2563 * read something, we should fix the permissions.
2565 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
2566 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
2567 gettimeofday (&tv_last_input, NULL);
2569 /* dst_write */
2570 while ((n_written = mc_write (dest_desc, t, (size_t) n_read)) < n_read)
2572 gboolean write_errno_nospace;
2574 if (n_written > 0)
2576 n_read -= n_written;
2577 t += n_written;
2578 continue;
2581 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
2583 if (ctx->skip_all)
2584 return_status = FILE_SKIPALL;
2585 else
2586 return_status =
2587 file_error (TRUE, _("Cannot write target file \"%s\"\n%s"), dst_path);
2589 if (return_status == FILE_SKIP)
2591 if (write_errno_nospace)
2592 goto ret;
2593 break;
2595 if (return_status == FILE_SKIPALL)
2597 ctx->skip_all = TRUE;
2598 if (write_errno_nospace)
2599 goto ret;
2601 if (return_status != FILE_RETRY)
2602 goto ret;
2606 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
2608 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
2609 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
2611 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
2613 copy_file_file_display_progress (tctx, ctx,
2614 tv_current,
2615 tv_transfer_start, file_size, n_read_total);
2616 tv_last_update = tv_current;
2618 is_first_time = FALSE;
2620 if (update_secs > FILEOP_STALLING_INTERVAL)
2622 stalled_msg = _("(stalled)");
2626 gboolean force_update;
2628 force_update =
2629 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
2631 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2633 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2634 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
2637 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
2638 force_update);
2640 mc_refresh ();
2642 return_status = check_progress_buttons (ctx);
2644 if (return_status != FILE_CONT)
2646 mc_refresh ();
2647 goto ret;
2651 dst_status = DEST_FULL; /* copy successful, don't remove target file */
2654 ret:
2655 g_free (buf);
2657 rotate_dash (FALSE);
2658 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
2660 temp_status = file_error (TRUE, _("Cannot close source file \"%s\"\n%s"), src_path);
2661 if (temp_status == FILE_RETRY)
2662 continue;
2663 if (temp_status == FILE_ABORT)
2664 return_status = temp_status;
2665 if (temp_status == FILE_SKIPALL)
2666 ctx->skip_all = TRUE;
2667 break;
2670 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
2672 temp_status = file_error (TRUE, _("Cannot close target file \"%s\"\n%s"), dst_path);
2673 if (temp_status == FILE_RETRY)
2674 continue;
2675 if (temp_status == FILE_SKIPALL)
2676 ctx->skip_all = TRUE;
2677 return_status = temp_status;
2678 break;
2681 if (dst_status == DEST_SHORT)
2683 /* Query to remove short file */
2684 if (query_dialog (Q_ ("DialogTitle|Copy"), _("Incomplete file was retrieved. Keep it?"),
2685 D_ERROR, 2, _("&Delete"), _("&Keep")) == 0)
2686 mc_unlink (dst_vpath);
2688 else if (dst_status == DEST_FULL)
2690 /* Copy has succeeded */
2691 if (!appending && ctx->preserve_uidgid)
2693 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
2695 temp_status = file_error (TRUE, _("Cannot chown target file \"%s\"\n%s"), dst_path);
2696 if (temp_status == FILE_RETRY)
2697 continue;
2698 if (temp_status == FILE_SKIPALL)
2700 ctx->skip_all = TRUE;
2701 return_status = FILE_CONT;
2703 if (temp_status == FILE_SKIP)
2704 return_status = FILE_CONT;
2705 break;
2709 if (!appending)
2711 if (ctx->preserve)
2713 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
2715 temp_status =
2716 file_error (TRUE, _("Cannot chmod target file \"%s\"\n%s"), dst_path);
2717 if (temp_status == FILE_RETRY)
2718 continue;
2719 if (temp_status == FILE_SKIPALL)
2721 ctx->skip_all = TRUE;
2722 return_status = FILE_CONT;
2724 if (temp_status == FILE_SKIP)
2725 return_status = FILE_CONT;
2726 break;
2729 else if (!dst_exists)
2731 src_mode = umask (-1);
2732 umask (src_mode);
2733 src_mode = 0100666 & ~src_mode;
2734 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
2736 mc_utime (dst_vpath, &times);
2740 if (return_status == FILE_CONT)
2741 return_status = progress_update_one (tctx, ctx, file_size);
2743 ret_fast:
2744 vfs_path_free (src_vpath);
2745 vfs_path_free (dst_vpath);
2746 return return_status;
2749 /* --------------------------------------------------------------------------------------------- */
2751 * I think these copy_*_* functions should have a return type.
2752 * anyway, this function *must* have two directories as arguments.
2754 /* FIXME: This function needs to check the return values of the
2755 function calls */
2757 FileProgressStatus
2758 copy_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d,
2759 gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs)
2761 struct dirent *next;
2762 struct stat buf, cbuf;
2763 DIR *reading;
2764 FileProgressStatus return_status = FILE_CONT;
2765 struct link *lp;
2766 vfs_path_t *src_vpath, *dst_vpath;
2767 gboolean do_mkdir = TRUE;
2769 src_vpath = vfs_path_from_str (s);
2770 dst_vpath = vfs_path_from_str (d);
2772 /* First get the mode of the source dir */
2774 retry_src_stat:
2775 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
2777 if (ctx->skip_all)
2778 return_status = FILE_SKIPALL;
2779 else
2781 return_status = file_error (TRUE, _("Cannot stat source directory \"%s\"\n%s"), s);
2782 if (return_status == FILE_RETRY)
2783 goto retry_src_stat;
2784 if (return_status == FILE_SKIPALL)
2785 ctx->skip_all = TRUE;
2787 goto ret_fast;
2790 if (is_in_linklist (dest_dirs, src_vpath, &cbuf) != NULL)
2792 /* Don't copy a directory we created before (we don't want to copy
2793 infinitely if a directory is copied into itself) */
2794 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2795 return_status = FILE_CONT;
2796 goto ret_fast;
2799 /* Hmm, hardlink to directory??? - Norbert */
2800 /* FIXME: In this step we should do something in case the destination already exist */
2801 /* Check the hardlinks */
2802 if (ctx->preserve)
2804 switch (check_hardlinks (src_vpath, &cbuf, dst_vpath, &ctx->skip_all))
2806 case HARDLINK_OK:
2807 /* We have made a hardlink - no more processing is necessary */
2808 goto ret_fast;
2810 case HARDLINK_ABORT:
2811 return_status = FILE_ABORT;
2812 goto ret_fast;
2814 default:
2815 break;
2819 if (!S_ISDIR (cbuf.st_mode))
2821 if (ctx->skip_all)
2822 return_status = FILE_SKIPALL;
2823 else
2825 return_status = file_error (TRUE, _("Source \"%s\" is not a directory\n%s"), s);
2826 if (return_status == FILE_RETRY)
2827 goto retry_src_stat;
2828 if (return_status == FILE_SKIPALL)
2829 ctx->skip_all = TRUE;
2831 goto ret_fast;
2834 if (is_in_linklist (parent_dirs, src_vpath, &cbuf) != NULL)
2836 /* we found a cyclic symbolic link */
2837 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
2838 return_status = FILE_SKIP;
2839 goto ret_fast;
2842 lp = g_new0 (struct link, 1);
2843 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
2844 lp->ino = cbuf.st_ino;
2845 lp->dev = cbuf.st_dev;
2846 parent_dirs = g_slist_prepend (parent_dirs, lp);
2848 retry_dst_stat:
2849 /* Now, check if the dest dir exists, if not, create it. */
2850 if (mc_stat (dst_vpath, &buf) != 0)
2852 /* Here the dir doesn't exist : make it ! */
2853 if (move_over && mc_rename (src_vpath, dst_vpath) == 0)
2855 return_status = FILE_CONT;
2856 goto ret;
2859 else
2862 * If the destination directory exists, we want to copy the whole
2863 * directory, but we only want this to happen once.
2865 * Escape sequences added to the * to compiler warnings.
2866 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2867 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2869 if (!S_ISDIR (buf.st_mode))
2871 if (ctx->skip_all)
2872 return_status = FILE_SKIPALL;
2873 else
2875 return_status =
2876 file_error (TRUE, _("Destination \"%s\" must be a directory\n%s"), d);
2877 if (return_status == FILE_SKIPALL)
2878 ctx->skip_all = TRUE;
2879 if (return_status == FILE_RETRY)
2880 goto retry_dst_stat;
2882 goto ret;
2884 /* Dive into subdir if exists */
2885 if (toplevel && ctx->dive_into_subdirs)
2887 vfs_path_t *tmp;
2889 tmp = dst_vpath;
2890 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
2891 vfs_path_free (tmp);
2894 else
2895 do_mkdir = FALSE;
2898 d = vfs_path_as_str (dst_vpath);
2900 if (do_mkdir)
2902 while (my_mkdir (dst_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2904 if (ctx->skip_all)
2905 return_status = FILE_SKIPALL;
2906 else
2908 return_status =
2909 file_error (TRUE, _("Cannot create target directory \"%s\"\n%s"), d);
2910 if (return_status == FILE_SKIPALL)
2911 ctx->skip_all = TRUE;
2913 if (return_status != FILE_RETRY)
2914 goto ret;
2917 lp = g_new0 (struct link, 1);
2918 mc_stat (dst_vpath, &buf);
2919 lp->vfs = vfs_path_get_by_index (dst_vpath, -1)->class;
2920 lp->ino = buf.st_ino;
2921 lp->dev = buf.st_dev;
2922 dest_dirs = g_slist_prepend (dest_dirs, lp);
2925 if (ctx->preserve_uidgid)
2927 while (mc_chown (dst_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2929 if (ctx->skip_all)
2930 return_status = FILE_SKIPALL;
2931 else
2933 return_status = file_error (TRUE, _("Cannot chown target directory \"%s\"\n%s"), d);
2934 if (return_status == FILE_SKIPALL)
2935 ctx->skip_all = TRUE;
2937 if (return_status != FILE_RETRY)
2938 goto ret;
2942 /* open the source dir for reading */
2943 reading = mc_opendir (src_vpath);
2944 if (reading == NULL)
2945 goto ret;
2947 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2949 char *path;
2950 vfs_path_t *tmp_vpath;
2953 * Now, we don't want '.' and '..' to be created / copied at any time
2955 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
2956 continue;
2958 /* get the filename and add it to the src directory */
2959 path = mc_build_filename (s, next->d_name, (char *) NULL);
2960 tmp_vpath = vfs_path_from_str (path);
2962 (*ctx->stat_func) (tmp_vpath, &buf);
2963 if (S_ISDIR (buf.st_mode))
2965 char *mdpath;
2967 mdpath = mc_build_filename (d, next->d_name, (char *) NULL);
2969 * From here, we just intend to recursively copy subdirs, not
2970 * the double functionality of copying different when the target
2971 * dir already exists. So, we give the recursive call the flag 0
2972 * meaning no toplevel.
2974 return_status =
2975 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2976 g_free (mdpath);
2978 else
2980 char *dest_file;
2982 dest_file = mc_build_filename (d, x_basename (path), (char *) NULL);
2983 return_status = copy_file_file (tctx, ctx, path, dest_file);
2984 g_free (dest_file);
2987 g_free (path);
2989 if (do_delete && return_status == FILE_CONT)
2991 if (ctx->erase_at_end)
2993 lp = g_new0 (struct link, 1);
2994 lp->src_vpath = tmp_vpath;
2995 lp->st_mode = buf.st_mode;
2996 erase_list = g_slist_append (erase_list, lp);
2997 tmp_vpath = NULL;
2999 else if (S_ISDIR (buf.st_mode))
3000 return_status = erase_dir_iff_empty (ctx, tmp_vpath, tctx->progress_count);
3001 else
3002 return_status = erase_file (tctx, ctx, tmp_vpath);
3004 vfs_path_free (tmp_vpath);
3006 mc_closedir (reading);
3008 if (ctx->preserve)
3010 mc_timesbuf_t times;
3012 mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill);
3013 get_times (&cbuf, &times);
3014 mc_utime (dst_vpath, &times);
3016 else
3018 cbuf.st_mode = umask (-1);
3019 umask (cbuf.st_mode);
3020 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
3021 mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill);
3024 ret:
3025 free_link (parent_dirs->data);
3026 g_slist_free_1 (parent_dirs);
3027 ret_fast:
3028 vfs_path_free (src_vpath);
3029 vfs_path_free (dst_vpath);
3030 return return_status;
3033 /* }}} */
3035 /* --------------------------------------------------------------------------------------------- */
3036 /* {{{ Move routines */
3038 FileProgressStatus
3039 move_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d)
3041 return do_move_dir_dir (NULL, tctx, ctx, s, d);
3044 /* }}} */
3046 /* --------------------------------------------------------------------------------------------- */
3047 /* {{{ Erase routines */
3049 FileProgressStatus
3050 erase_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * s_vpath)
3052 file_progress_show_deleting (ctx, vfs_path_as_str (s_vpath), NULL);
3053 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3054 if (check_progress_buttons (ctx) == FILE_ABORT)
3055 return FILE_ABORT;
3057 mc_refresh ();
3059 /* The old way to detect a non empty directory was:
3060 error = my_rmdir (s);
3061 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
3062 For the linux user space nfs server (nfs-server-2.2beta29-2)
3063 we would have to check also for EIO. I hope the new way is
3064 fool proof. (Norbert)
3066 if (check_dir_is_empty (s_vpath) == 0)
3067 { /* not empty */
3068 FileProgressStatus error;
3070 error = query_recursive (ctx, vfs_path_as_str (s_vpath));
3071 if (error == FILE_CONT)
3072 error = recursive_erase (tctx, ctx, s_vpath);
3073 return error;
3076 return try_erase_dir (ctx, vfs_path_as_str (s_vpath));
3079 /* }}} */
3081 /* --------------------------------------------------------------------------------------------- */
3082 /* {{{ Panel operate routines */
3084 void
3085 dirsize_status_init_cb (status_msg_t * sm)
3087 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
3088 Widget *wd = WIDGET (sm->dlg);
3090 const char *b1_name = N_("&Abort");
3091 const char *b2_name = N_("&Skip");
3092 int b_width, ui_width;
3094 #ifdef ENABLE_NLS
3095 b1_name = _(b1_name);
3096 b2_name = _(b2_name);
3097 #endif
3099 b_width = str_term_width1 (b1_name) + 4;
3100 if (dsm->allow_skip)
3101 b_width += str_term_width1 (b2_name) + 4 + 1;
3103 ui_width = MAX (COLS / 2, b_width + 6);
3104 dsm->dirname = label_new (2, 3, "");
3105 add_widget (sm->dlg, dsm->dirname);
3106 dsm->count_size = label_new (3, 3, "");
3107 add_widget (sm->dlg, dsm->count_size);
3108 add_widget (sm->dlg, hline_new (4, -1, -1));
3110 dsm->abort_button = WIDGET (button_new (5, 3, FILE_ABORT, NORMAL_BUTTON, b1_name, NULL));
3111 add_widget (sm->dlg, dsm->abort_button);
3112 if (dsm->allow_skip)
3114 dsm->skip_button = WIDGET (button_new (5, 3, FILE_SKIP, NORMAL_BUTTON, b2_name, NULL));
3115 add_widget (sm->dlg, dsm->skip_button);
3116 widget_select (dsm->skip_button);
3119 widget_set_size (wd, wd->y, wd->x, 8, ui_width);
3120 dirsize_status_locate_buttons (dsm);
3123 /* --------------------------------------------------------------------------------------------- */
3126 dirsize_status_update_cb (status_msg_t * sm)
3128 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
3129 Widget *wd = WIDGET (sm->dlg);
3131 /* update second (longer label) */
3132 label_set_textv (dsm->count_size, _("Directories: %zu, total size: %s"),
3133 dsm->dir_count, size_trunc_sep (dsm->total_size, panels_options.kilobyte_si));
3135 /* enlarge dialog if required */
3136 if (WIDGET (dsm->count_size)->cols + 6 > wd->cols)
3138 dlg_set_size (sm->dlg, wd->lines, WIDGET (dsm->count_size)->cols + 6);
3139 dirsize_status_locate_buttons (dsm);
3140 dlg_redraw (sm->dlg);
3143 /* adjust first label */
3144 label_set_text (dsm->dirname, str_trunc (vfs_path_as_str (dsm->dirname_vpath), wd->cols - 6));
3146 switch (status_msg_common_update (sm))
3148 case B_CANCEL:
3149 case FILE_ABORT:
3150 return FILE_ABORT;
3151 case FILE_SKIP:
3152 return FILE_SKIP;
3153 default:
3154 return FILE_CONT;
3158 /* --------------------------------------------------------------------------------------------- */
3160 void
3161 dirsize_status_deinit_cb (status_msg_t * sm)
3163 (void) sm;
3165 /* schedule to update passive panel */
3166 if (get_other_type () == view_listing)
3167 other_panel->dirty = 1;
3170 /* --------------------------------------------------------------------------------------------- */
3172 * compute_dir_size:
3174 * Computes the number of bytes used by the files in a directory
3177 FileProgressStatus
3178 compute_dir_size (const vfs_path_t * dirname_vpath, dirsize_status_msg_t * sm,
3179 size_t * ret_dir_count, size_t * ret_marked_count, uintmax_t * ret_total,
3180 gboolean compute_symlinks)
3182 return do_compute_dir_size (dirname_vpath, sm, ret_dir_count, ret_marked_count, ret_total,
3183 compute_symlinks);
3186 /* --------------------------------------------------------------------------------------------- */
3188 * panel_operate:
3190 * Performs one of the operations on the selection on the source_panel
3191 * (copy, delete, move).
3193 * Returns TRUE if did change the directory
3194 * structure, Returns FALSE if user aborted
3196 * force_single forces operation on the current entry and affects
3197 * default destination. Current filename is used as default.
3200 gboolean
3201 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
3203 WPanel *panel = PANEL (source_panel);
3204 const gboolean single_entry = force_single || (panel->marked <= 1)
3205 || (get_current_type () == view_tree);
3207 const char *source = NULL;
3208 char *dest = NULL;
3209 vfs_path_t *dest_vpath = NULL;
3210 char *save_cwd = NULL, *save_dest = NULL;
3211 struct stat src_stat;
3212 gboolean ret_val = TRUE;
3213 int i;
3214 FileProgressStatus value;
3215 file_op_context_t *ctx;
3216 file_op_total_context_t *tctx;
3217 vfs_path_t *tmp_vpath;
3218 filegui_dialog_type_t dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3220 gboolean do_bg = FALSE; /* do background operation? */
3222 static gboolean i18n_flag = FALSE;
3223 if (!i18n_flag)
3225 for (i = G_N_ELEMENTS (op_names); i-- != 0;)
3226 op_names[i] = Q_ (op_names[i]);
3227 i18n_flag = TRUE;
3230 linklist = free_linklist (linklist);
3231 dest_dirs = free_linklist (dest_dirs);
3233 if (single_entry)
3235 source = check_single_entry (panel, force_single, &src_stat);
3237 if (source == NULL)
3238 return FALSE;
3241 ctx = file_op_context_new (operation);
3243 /* Show confirmation dialog */
3244 if (operation != OP_DELETE)
3246 dest =
3247 do_confirm_copy_move (panel, operation, force_single, source, &src_stat, ctx, &do_bg);
3249 if (dest == NULL)
3251 ret_val = FALSE;
3252 goto ret_fast;
3255 dest_vpath = vfs_path_from_str (dest);
3257 else if (confirm_delete && !do_confirm_erase (panel, source, &src_stat))
3259 ret_val = FALSE;
3260 goto ret_fast;
3263 tctx = file_op_total_context_new ();
3264 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
3266 #ifdef ENABLE_BACKGROUND
3267 /* Did the user select to do a background operation? */
3268 if (do_bg)
3270 int v;
3272 v = do_background (ctx,
3273 g_strconcat (op_names[operation], ": ",
3274 vfs_path_as_str (panel->cwd_vpath), (char *) NULL));
3275 if (v == -1)
3276 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
3278 /* If we are the parent */
3279 if (v == 1)
3281 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
3283 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
3284 vfs_path_free (dest_vpath);
3285 g_free (dest);
3286 /* file_op_context_destroy (ctx); */
3287 return FALSE;
3290 else
3291 #endif /* ENABLE_BACKGROUND */
3293 if (operation == OP_DELETE)
3294 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
3295 else if (single_entry && S_ISDIR (selection (panel)->st.st_mode))
3296 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3297 else if (single_entry || force_single)
3298 dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3299 else
3300 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3303 /* Initialize things */
3304 /* We do not want to trash cache every time file is
3305 created/touched. However, this will make our cache contain
3306 invalid data. */
3307 if ((dest != NULL)
3308 && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3309 save_dest = g_strdup (dest);
3311 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
3312 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3313 save_cwd = g_strdup (vfs_path_as_str (panel->cwd_vpath));
3315 /* Now, let's do the job */
3317 /* This code is only called by the tree and panel code */
3318 if (single_entry)
3320 /* We now have ETA in all cases */
3322 /* One file: FIXME mc_chdir will take user out of any vfs */
3323 if ((operation != OP_COPY) && (get_current_type () == view_tree))
3325 vfs_path_t *vpath;
3326 int chdir_retcode;
3328 vpath = vfs_path_from_str (PATH_SEP_STR);
3329 chdir_retcode = mc_chdir (vpath);
3330 vfs_path_free (vpath);
3331 if (chdir_retcode < 0)
3333 ret_val = FALSE;
3334 goto clean_up;
3338 value =
3339 operate_single_file (panel, operation, tctx, ctx, source, &src_stat, dest, dialog_type);
3341 if ((value == FILE_CONT) && !force_single)
3342 unmark_files (panel);
3344 else
3346 /* Many files */
3348 /* Check destination for copy or move operation */
3349 while (operation != OP_DELETE)
3351 int dst_result;
3352 struct stat dst_stat;
3354 dst_result = mc_stat (dest_vpath, &dst_stat);
3356 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
3357 break;
3359 if (ctx->skip_all
3360 || file_error (TRUE, _("Destination \"%s\" must be a directory\n%s"),
3361 dest) != FILE_RETRY)
3362 goto clean_up;
3365 /* TODO: the good way is required to skip directories scanning in case of rename/move
3366 * of several directories. Since reqular expression can be used for destination,
3367 * some directory movements can be a cross-filesystem and directory scanning is useful
3368 * for those directories only. */
3370 if (panel_operate_init_totals (panel, NULL, NULL, ctx, file_op_compute_totals, dialog_type)
3371 == FILE_CONT)
3373 /* Loop for every file, perform the actual copy operation */
3374 for (i = 0; i < panel->dir.len; i++)
3376 const char *source2;
3378 if (!panel->dir.list[i].f.marked)
3379 continue; /* Skip the unmarked ones */
3381 source2 = panel->dir.list[i].fname;
3382 src_stat = panel->dir.list[i].st;
3384 value = operate_one_file (panel, operation, tctx, ctx, source2, &src_stat, dest);
3386 if (value == FILE_ABORT)
3387 break;
3389 if (value == FILE_CONT)
3390 do_file_mark (panel, i, 0);
3392 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3394 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3395 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3398 if (operation != OP_DELETE)
3399 file_progress_show (ctx, 0, 0, "", FALSE);
3401 if (check_progress_buttons (ctx) == FILE_ABORT)
3402 break;
3404 mc_refresh ();
3405 } /* Loop for every file */
3407 } /* Many entries */
3409 clean_up:
3410 /* Clean up */
3411 if (save_cwd != NULL)
3413 tmp_vpath = vfs_path_from_str (save_cwd);
3414 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3415 vfs_path_free (tmp_vpath);
3416 g_free (save_cwd);
3419 if (save_dest != NULL)
3421 tmp_vpath = vfs_path_from_str (save_dest);
3422 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3423 vfs_path_free (tmp_vpath);
3424 g_free (save_dest);
3427 linklist = free_linklist (linklist);
3428 dest_dirs = free_linklist (dest_dirs);
3429 g_free (dest);
3430 vfs_path_free (dest_vpath);
3431 MC_PTR_FREE (ctx->dest_mask);
3433 #ifdef ENABLE_BACKGROUND
3434 /* Let our parent know we are saying bye bye */
3435 if (mc_global.we_are_background)
3437 int cur_pid = getpid ();
3438 /* Send pid to parent with child context, it is fork and
3439 don't modify real parent ctx */
3440 ctx->pid = cur_pid;
3441 parent_call ((void *) end_bg_process, ctx, 0);
3443 vfs_shut ();
3444 my_exit (EXIT_SUCCESS);
3446 #endif /* ENABLE_BACKGROUND */
3448 file_op_total_context_destroy (tctx);
3449 ret_fast:
3450 file_op_context_destroy (ctx);
3452 return ret_val;
3455 /* }}} */
3457 /* --------------------------------------------------------------------------------------------- */
3458 /* {{{ Query/status report routines */
3459 /** Report error with one file */
3460 FileProgressStatus
3461 file_error (gboolean allow_retry, const char *format, const char *file)
3463 char buf[BUF_MEDIUM];
3465 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3467 return do_file_error (allow_retry, buf);
3470 /* --------------------------------------------------------------------------------------------- */
3473 Cause emacs to enter folding mode for this file:
3474 Local variables:
3475 end: