Move widget add/del API from WDialog to WGroup.
[midnight-commander.git] / src / filemanager / file.c
blob298e589e2b1d888d6c531f1314a1d3f52591437a
1 /*
2 File management.
4 Copyright (C) 1994-2020
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 dst_stat;
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, &dst_stat) == 0) && S_ISLNK (dst_stat.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, 3, _("&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 *src, struct stat *src_stat, const char *dst,
1039 struct stat *dst_stat)
1041 /* *INDENT-OFF* */
1042 union
1044 void *p;
1045 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *,
1046 struct stat *, const char *, 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, 4, strlen (src), src, sizeof (struct stat), src_stat,
1054 strlen (dst), dst, sizeof (struct stat), dst_stat);
1055 else
1056 return file_progress_real_query_replace (ctx, Foreground, src, src_stat, dst, dst_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 *src, struct stat *src_stat, const char *dst,
1080 struct stat *dst_stat)
1082 return file_progress_real_query_replace (ctx, Foreground, src, src_stat, dst, dst_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_stat, dst_stat;
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_stat) != 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_stat) == 0)
1237 if (check_same_file (s, &src_stat, d, &dst_stat, &return_status))
1238 goto ret;
1240 if (S_ISDIR (dst_stat.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, s, &src_stat, d, &dst_stat);
1251 if (return_status != FILE_CONT)
1252 goto ret;
1254 /* Ok to overwrite */
1257 if (!ctx->do_append)
1259 if (S_ISLNK (src_stat.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_stat.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_stat, 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_stat.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 /* check buttons if deleting info was changed */
1361 if (file_progress_show_deleting (ctx, vfs_path_as_str (vpath), &tctx->progress_count))
1363 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1364 if (check_progress_buttons (ctx) == FILE_ABORT)
1365 return FILE_ABORT;
1367 mc_refresh ();
1370 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
1372 /* ignore, most likely the mc_unlink fails, too */
1373 buf.st_size = 0;
1376 if (!try_remove_file (ctx, vpath, &return_status) && return_status == FILE_ABORT)
1377 return FILE_ABORT;
1379 if (tctx->progress_count == 0)
1380 return FILE_CONT;
1382 return check_progress_buttons (ctx);
1385 /* --------------------------------------------------------------------------------------------- */
1387 static FileProgressStatus
1388 try_erase_dir (file_op_context_t * ctx, const char *dir)
1390 FileProgressStatus return_status = FILE_CONT;
1392 while (my_rmdir (dir) != 0 && !ctx->skip_all)
1394 return_status = file_error (TRUE, _("Cannot remove directory \"%s\"\n%s"), dir);
1395 if (return_status == FILE_SKIPALL)
1396 ctx->skip_all = TRUE;
1397 if (return_status != FILE_RETRY)
1398 break;
1401 return return_status;
1404 /* --------------------------------------------------------------------------------------------- */
1407 Recursive remove of files
1408 abort->cancel stack
1409 skip ->warn every level, gets default
1410 skipall->remove as much as possible
1412 static FileProgressStatus
1413 recursive_erase (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * vpath)
1415 struct dirent *next;
1416 DIR *reading;
1417 const char *s;
1418 FileProgressStatus return_status = FILE_CONT;
1420 reading = mc_opendir (vpath);
1421 if (reading == NULL)
1422 return FILE_RETRY;
1424 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1426 vfs_path_t *tmp_vpath;
1427 struct stat buf;
1429 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
1430 continue;
1432 tmp_vpath = vfs_path_append_new (vpath, next->d_name, (char *) NULL);
1433 if (mc_lstat (tmp_vpath, &buf) != 0)
1435 mc_closedir (reading);
1436 vfs_path_free (tmp_vpath);
1437 return FILE_RETRY;
1439 if (S_ISDIR (buf.st_mode))
1440 return_status = recursive_erase (tctx, ctx, tmp_vpath);
1441 else
1442 return_status = erase_file (tctx, ctx, tmp_vpath);
1443 vfs_path_free (tmp_vpath);
1445 mc_closedir (reading);
1447 if (return_status == FILE_ABORT)
1448 return FILE_ABORT;
1450 s = vfs_path_as_str (vpath);
1452 file_progress_show_deleting (ctx, s, NULL);
1453 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1454 if (check_progress_buttons (ctx) == FILE_ABORT)
1455 return FILE_ABORT;
1457 mc_refresh ();
1459 return try_erase_dir (ctx, s);
1462 /* --------------------------------------------------------------------------------------------- */
1463 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1464 in the directory path points to, 0 else. */
1466 static int
1467 check_dir_is_empty (const vfs_path_t * vpath)
1469 DIR *dir;
1470 struct dirent *d;
1471 int i = 1;
1473 dir = mc_opendir (vpath);
1474 if (dir == NULL)
1475 return -1;
1477 for (d = mc_readdir (dir); d != NULL; d = mc_readdir (dir))
1478 if (!DIR_IS_DOT (d->d_name) && !DIR_IS_DOTDOT (d->d_name))
1480 i = 0;
1481 break;
1484 mc_closedir (dir);
1485 return i;
1488 /* --------------------------------------------------------------------------------------------- */
1490 static FileProgressStatus
1491 erase_dir_iff_empty (file_op_context_t * ctx, const vfs_path_t * vpath, size_t count)
1493 const char *s;
1495 s = vfs_path_as_str (vpath);
1497 file_progress_show_deleting (ctx, s, NULL);
1498 file_progress_show_count (ctx, count, ctx->progress_count);
1499 if (check_progress_buttons (ctx) == FILE_ABORT)
1500 return FILE_ABORT;
1502 mc_refresh ();
1504 if (check_dir_is_empty (vpath) != 1)
1505 return FILE_CONT;
1507 /* not empty or error */
1508 return try_erase_dir (ctx, s);
1512 /* --------------------------------------------------------------------------------------------- */
1514 static void
1515 erase_dir_after_copy (file_op_total_context_t * tctx, file_op_context_t * ctx,
1516 const vfs_path_t * vpath, FileProgressStatus * status)
1518 if (ctx->erase_at_end)
1520 /* Reset progress count before delete to avoid counting files twice */
1521 tctx->progress_count = tctx->prev_progress_count;
1523 while (erase_list != NULL && *status != FILE_ABORT)
1525 struct link *lp = (struct link *) erase_list->data;
1527 if (S_ISDIR (lp->st_mode))
1528 *status = erase_dir_iff_empty (ctx, lp->src_vpath, tctx->progress_count);
1529 else
1530 *status = erase_file (tctx, ctx, lp->src_vpath);
1532 erase_list = g_slist_remove (erase_list, lp);
1533 free_link (lp);
1536 /* Save progress counter before move next directory */
1537 tctx->prev_progress_count = tctx->progress_count;
1540 erase_dir_iff_empty (ctx, vpath, tctx->progress_count);
1543 /* }}} */
1545 /* --------------------------------------------------------------------------------------------- */
1548 * Move single directory or one of many directories from one location to another.
1550 * @panel pointer to panel in case of single directory, NULL otherwise
1551 * @tctx file operation total context object
1552 * @ctx file operation context object
1553 * @s source directory name
1554 * @d destination directory name
1556 * @return operation result
1558 static FileProgressStatus
1559 do_move_dir_dir (const WPanel * panel, file_op_total_context_t * tctx, file_op_context_t * ctx,
1560 const char *s, const char *d)
1562 struct stat src_stat, dst_stat;
1563 FileProgressStatus return_status = FILE_CONT;
1564 gboolean move_over = FALSE;
1565 gboolean dstat_ok;
1566 vfs_path_t *src_vpath, *dst_vpath;
1567 gboolean calc_total = FALSE;
1569 src_vpath = vfs_path_from_str (s);
1570 dst_vpath = vfs_path_from_str (d);
1572 file_progress_show_source (ctx, src_vpath);
1573 file_progress_show_target (ctx, dst_vpath);
1575 /* FIXME: do we really need to check buttons in case of single directory? */
1576 if (panel != NULL && check_progress_buttons (ctx) == FILE_ABORT)
1578 return_status = FILE_ABORT;
1579 goto ret_fast;
1582 mc_refresh ();
1584 mc_stat (src_vpath, &src_stat);
1586 dstat_ok = (mc_stat (dst_vpath, &dst_stat) == 0);
1588 if (dstat_ok && check_same_file (s, &src_stat, d, &dst_stat, &return_status))
1589 goto ret_fast;
1591 if (!dstat_ok)
1592 ; /* destination doesn't exist */
1593 else if (!ctx->dive_into_subdirs)
1594 move_over = TRUE;
1595 else
1597 vfs_path_t *tmp;
1599 tmp = dst_vpath;
1600 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
1601 vfs_path_free (tmp);
1604 d = vfs_path_as_str (dst_vpath);
1606 /* Check if the user inputted an existing dir */
1607 retry_dst_stat:
1608 if (mc_stat (dst_vpath, &dst_stat) == 0)
1610 if (move_over)
1612 if (panel != NULL)
1614 /* In case of single directory, calculate totals. In case of many directories,
1615 totals are calcuated already. */
1616 return_status =
1617 panel_operate_init_totals (panel, src_vpath, &src_stat, ctx, TRUE,
1618 FILEGUI_DIALOG_MULTI_ITEM);
1619 if (return_status != FILE_CONT)
1620 goto ret;
1622 calc_total = TRUE;
1625 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, TRUE, TRUE, NULL);
1627 if (return_status != FILE_CONT)
1628 goto ret;
1629 goto oktoret;
1631 else if (ctx->skip_all)
1632 return_status = FILE_SKIPALL;
1633 else
1635 if (S_ISDIR (dst_stat.st_mode))
1636 return_status = file_error (TRUE, _("Cannot overwrite directory \"%s\"\n%s"), d);
1637 else
1638 return_status = file_error (TRUE, _("Cannot overwrite file \"%s\"\n%s"), d);
1639 if (return_status == FILE_SKIPALL)
1640 ctx->skip_all = TRUE;
1641 if (return_status == FILE_RETRY)
1642 goto retry_dst_stat;
1645 goto ret_fast;
1648 retry_rename:
1649 if (mc_rename (src_vpath, dst_vpath) == 0)
1651 return_status = FILE_CONT;
1652 goto ret;
1655 if (errno != EXDEV)
1657 if (!ctx->skip_all)
1659 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
1660 if (return_status == FILE_SKIPALL)
1661 ctx->skip_all = TRUE;
1662 if (return_status == FILE_RETRY)
1663 goto retry_rename;
1665 goto ret;
1668 /* Failed because of filesystem boundary -> copy dir instead */
1669 if (panel != NULL && !calc_total)
1671 /* In case of single directory, calculate totals. In case of many directories,
1672 totals are calcuated already. */
1673 return_status =
1674 panel_operate_init_totals (panel, src_vpath, &src_stat, ctx, TRUE,
1675 FILEGUI_DIALOG_MULTI_ITEM);
1676 if (return_status != FILE_CONT)
1677 goto ret;
1680 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, FALSE, TRUE, NULL);
1682 if (return_status != FILE_CONT)
1683 goto ret;
1685 oktoret:
1686 /* FIXME: there is no need to update progress and check buttons
1687 at the finish of single directory operation. */
1688 if (panel == NULL)
1690 file_progress_show_source (ctx, NULL);
1691 file_progress_show_target (ctx, NULL);
1692 file_progress_show (ctx, 0, 0, "", FALSE);
1694 return_status = check_progress_buttons (ctx);
1695 if (return_status != FILE_CONT)
1696 goto ret;
1699 mc_refresh ();
1701 erase_dir_after_copy (tctx, ctx, src_vpath, &return_status);
1703 ret:
1704 erase_list = free_linklist (erase_list);
1705 ret_fast:
1706 vfs_path_free (src_vpath);
1707 vfs_path_free (dst_vpath);
1708 return return_status;
1711 /* --------------------------------------------------------------------------------------------- */
1713 /* {{{ Panel operate routines */
1716 * Return currently selected entry name or the name of the first marked
1717 * entry if there is one.
1720 static const char *
1721 panel_get_file (const WPanel * panel)
1723 if (get_current_type () == view_tree)
1725 WTree *tree;
1726 const vfs_path_t *selected_name;
1728 tree = (WTree *) get_panel_widget (get_current_index ());
1729 selected_name = tree_selected_name (tree);
1730 return vfs_path_as_str (selected_name);
1733 if (panel->marked != 0)
1735 int i;
1737 for (i = 0; i < panel->dir.len; i++)
1738 if (panel->dir.list[i].f.marked)
1739 return panel->dir.list[i].fname;
1742 return panel->dir.list[panel->selected].fname;
1745 /* --------------------------------------------------------------------------------------------- */
1747 static const char *
1748 check_single_entry (const WPanel * panel, gboolean force_single, struct stat *src_stat)
1750 const char *source;
1751 gboolean ok;
1753 if (force_single)
1754 source = selection (panel)->fname;
1755 else
1756 source = panel_get_file (panel);
1758 ok = !DIR_IS_DOTDOT (source);
1760 if (!ok)
1761 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
1762 else
1764 vfs_path_t *source_vpath;
1766 source_vpath = vfs_path_from_str (source);
1768 /* Update stat to get actual info */
1769 ok = mc_lstat (source_vpath, src_stat) == 0;
1770 if (!ok)
1772 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
1773 path_trunc (source, 30), unix_error_string (errno));
1775 /* Directory was changed outside MC. Reload it forced */
1776 if (!panel->is_panelized)
1778 panel_update_flags_t flags = UP_RELOAD;
1780 /* don't update panelized panel */
1781 if (get_other_type () == view_listing && other_panel->is_panelized)
1782 flags |= UP_ONLY_CURRENT;
1784 update_panels (flags, UP_KEEPSEL);
1788 vfs_path_free (source_vpath);
1791 return ok ? source : NULL;
1794 /* --------------------------------------------------------------------------------------------- */
1796 * Generate user prompt for panel operation.
1797 * src_stat must be not NULL for single source, and NULL for multiple sources
1800 static char *
1801 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1802 const struct stat *src_stat)
1804 char *sp;
1805 char *format_string;
1806 const char *cp;
1808 static gboolean i18n_flag = FALSE;
1809 if (!i18n_flag)
1811 size_t i;
1813 for (i = G_N_ELEMENTS (op_names1); i-- != 0;)
1814 op_names1[i] = Q_ (op_names1[i]);
1816 #ifdef ENABLE_NLS
1817 for (i = G_N_ELEMENTS (prompt_parts); i-- != 0;)
1818 prompt_parts[i] = _(prompt_parts[i]);
1820 one_format = _(one_format);
1821 many_format = _(many_format);
1822 #endif /* ENABLE_NLS */
1823 i18n_flag = TRUE;
1826 /* Possible prompts:
1827 * OP_COPY:
1828 * "Copy file \"%s\" with source mask:"
1829 * "Copy %d files with source mask:"
1830 * "Copy directory \"%s\" with source mask:"
1831 * "Copy %d directories with source mask:"
1832 * "Copy %d files/directories with source mask:"
1833 * OP_MOVE:
1834 * "Move file \"%s\" with source mask:"
1835 * "Move %d files with source mask:"
1836 * "Move directory \"%s\" with source mask:"
1837 * "Move %d directories with source mask:"
1838 * "Move %d files/directories with source mask:"
1839 * OP_DELETE:
1840 * "Delete file \"%s\"?"
1841 * "Delete %d files?"
1842 * "Delete directory \"%s\"?"
1843 * "Delete %d directories?"
1844 * "Delete %d files/directories?"
1847 cp = (src_stat != NULL ? one_format : many_format);
1849 /* 1. Substitute %o */
1850 format_string = str_replace_all (cp, "%o", op_names1[(int) operation]);
1852 /* 2. Substitute %n */
1853 cp = operation == OP_DELETE ? "\n" : " ";
1854 sp = format_string;
1855 format_string = str_replace_all (sp, "%n", cp);
1856 g_free (sp);
1858 /* 3. Substitute %f */
1859 if (src_stat != NULL)
1860 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1861 else if (panel->marked == panel->dirs_marked)
1862 cp = prompt_parts[3];
1863 else
1864 cp = panel->dirs_marked != 0 ? prompt_parts[4] : prompt_parts[1];
1866 sp = format_string;
1867 format_string = str_replace_all (sp, "%f", cp);
1868 g_free (sp);
1870 /* 4. Substitute %m */
1871 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1872 sp = format_string;
1873 format_string = str_replace_all (sp, "%m", cp);
1874 g_free (sp);
1876 return format_string;
1879 /* --------------------------------------------------------------------------------------------- */
1881 static char *
1882 do_confirm_copy_move (const WPanel * panel, FileOperation operation, gboolean force_single,
1883 const char *source, struct stat *src_stat, file_op_context_t * ctx,
1884 gboolean * do_bg)
1886 const char *tmp_dest_dir;
1887 char *dest_dir;
1888 char *format;
1889 char *ret;
1891 /* Forced single operations default to the original name */
1892 if (force_single)
1893 tmp_dest_dir = source;
1894 else if (get_other_type () == view_listing)
1895 tmp_dest_dir = vfs_path_as_str (other_panel->cwd_vpath);
1896 else
1897 tmp_dest_dir = vfs_path_as_str (panel->cwd_vpath);
1900 * Add trailing backslash only when do non-local ops.
1901 * It saves user from occasional file renames (when destination
1902 * dir is deleted)
1904 if (!force_single && tmp_dest_dir != NULL && tmp_dest_dir[0] != '\0'
1905 && !IS_PATH_SEP (tmp_dest_dir[strlen (tmp_dest_dir) - 1]))
1907 /* add trailing separator */
1908 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
1910 else
1912 /* just copy */
1913 dest_dir = g_strdup (tmp_dest_dir);
1916 if (dest_dir == NULL)
1917 return NULL;
1919 if (source == NULL)
1920 src_stat = NULL;
1922 /* Generate confirmation prompt */
1923 format = panel_operate_generate_prompt (panel, operation, src_stat);
1925 ret = file_mask_dialog (ctx, operation, source != NULL, format,
1926 source != NULL ? source : (const void *) &panel->marked, dest_dir,
1927 do_bg);
1929 g_free (format);
1930 g_free (dest_dir);
1932 return ret;
1935 /* --------------------------------------------------------------------------------------------- */
1937 static gboolean
1938 do_confirm_erase (const WPanel * panel, const char *source, struct stat *src_stat)
1940 int i;
1941 char *format;
1942 char fmd_buf[BUF_MEDIUM];
1944 if (source == NULL)
1945 src_stat = NULL;
1947 /* Generate confirmation prompt */
1948 format = panel_operate_generate_prompt (panel, OP_DELETE, src_stat);
1950 if (source == NULL)
1951 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
1952 else
1954 const int fmd_xlen = 64;
1956 i = fmd_xlen - str_term_width1 (format) - 4;
1957 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
1960 g_free (format);
1962 if (safe_delete)
1963 query_set_sel (1);
1965 i = query_dialog (op_names[OP_DELETE], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
1967 return (i == 0);
1970 /* --------------------------------------------------------------------------------------------- */
1972 static FileProgressStatus
1973 operate_single_file (const WPanel * panel, FileOperation operation, file_op_total_context_t * tctx,
1974 file_op_context_t * ctx, const char *src, struct stat *src_stat,
1975 const char *dest, filegui_dialog_type_t dialog_type)
1977 FileProgressStatus value;
1978 vfs_path_t *src_vpath;
1979 gboolean is_file;
1981 if (g_path_is_absolute (src))
1982 src_vpath = vfs_path_from_str (src);
1983 else
1984 src_vpath = vfs_path_append_new (panel->cwd_vpath, src, (char *) NULL);
1986 is_file = !S_ISDIR (src_stat->st_mode);
1987 /* Is link to directory? */
1988 if (is_file)
1990 gboolean is_link;
1992 is_link = file_is_symlink_to_dir (src_vpath, src_stat, NULL);
1993 is_file = !(is_link && ctx->follow_links);
1997 if (operation == OP_DELETE)
1999 value = panel_operate_init_totals (panel, src_vpath, src_stat, ctx, !is_file, dialog_type);
2000 if (value == FILE_CONT)
2002 if (is_file)
2003 value = erase_file (tctx, ctx, src_vpath);
2004 else
2005 value = erase_dir (tctx, ctx, src_vpath);
2008 else
2010 char *temp;
2012 src = vfs_path_as_str (src_vpath);
2014 temp = build_dest (ctx, src, dest, &value);
2015 if (temp != NULL)
2017 dest = temp;
2019 switch (operation)
2021 case OP_COPY:
2022 /* we use file_mask_op_follow_links only with OP_COPY */
2023 ctx->stat_func (src_vpath, src_stat);
2025 value =
2026 panel_operate_init_totals (panel, src_vpath, src_stat, ctx, !is_file,
2027 dialog_type);
2028 if (value == FILE_CONT)
2030 is_file = !S_ISDIR (src_stat->st_mode);
2031 /* Is link to directory? */
2032 if (is_file)
2034 gboolean is_link;
2036 is_link = file_is_symlink_to_dir (src_vpath, src_stat, NULL);
2037 is_file = !(is_link && ctx->follow_links);
2040 if (is_file)
2041 value = copy_file_file (tctx, ctx, src, dest);
2042 else
2043 value = copy_dir_dir (tctx, ctx, src, dest, TRUE, FALSE, FALSE, NULL);
2045 break;
2047 case OP_MOVE:
2048 #ifdef ENABLE_BACKGROUND
2049 /* create UI to show confirmation dialog */
2050 if (!mc_global.we_are_background)
2051 file_op_context_create_ui (ctx, TRUE, FILEGUI_DIALOG_ONE_ITEM);
2052 #endif
2053 if (is_file)
2054 value = move_file_file (panel, tctx, ctx, src, dest);
2055 else
2056 value = do_move_dir_dir (panel, tctx, ctx, src, dest);
2057 break;
2059 default:
2060 /* Unknown file operation */
2061 abort ();
2064 g_free (temp);
2068 vfs_path_free (src_vpath);
2070 return value;
2073 /* --------------------------------------------------------------------------------------------- */
2075 static FileProgressStatus
2076 operate_one_file (const WPanel * panel, FileOperation operation, file_op_total_context_t * tctx,
2077 file_op_context_t * ctx, const char *src, struct stat *src_stat, const char *dest)
2079 FileProgressStatus value = FILE_CONT;
2080 vfs_path_t *src_vpath;
2081 gboolean is_file;
2083 if (g_path_is_absolute (src))
2084 src_vpath = vfs_path_from_str (src);
2085 else
2086 src_vpath = vfs_path_append_new (panel->cwd_vpath, src, (char *) NULL);
2088 is_file = !S_ISDIR (src_stat->st_mode);
2090 if (operation == OP_DELETE)
2092 if (is_file)
2093 value = erase_file (tctx, ctx, src_vpath);
2094 else
2095 value = erase_dir (tctx, ctx, src_vpath);
2097 else
2099 char *temp;
2101 src = vfs_path_as_str (src_vpath);
2103 temp = build_dest (ctx, src, dest, &value);
2104 if (temp != NULL)
2106 dest = temp;
2108 switch (operation)
2110 case OP_COPY:
2111 /* we use file_mask_op_follow_links only with OP_COPY */
2112 ctx->stat_func (src_vpath, src_stat);
2113 is_file = !S_ISDIR (src_stat->st_mode);
2115 if (is_file)
2116 value = copy_file_file (tctx, ctx, src, dest);
2117 else
2118 value = copy_dir_dir (tctx, ctx, src, dest, TRUE, FALSE, FALSE, NULL);
2119 dest_dirs = free_linklist (dest_dirs);
2120 break;
2122 case OP_MOVE:
2123 if (is_file)
2124 value = move_file_file (NULL, tctx, ctx, src, dest);
2125 else
2126 value = do_move_dir_dir (NULL, tctx, ctx, src, dest);
2127 break;
2129 default:
2130 /* Unknown file operation */
2131 abort ();
2134 g_free (temp);
2138 vfs_path_free (src_vpath);
2140 return value;
2143 /* --------------------------------------------------------------------------------------------- */
2145 #ifdef ENABLE_BACKGROUND
2146 static int
2147 end_bg_process (file_op_context_t * ctx, enum OperationMode mode)
2149 int pid = ctx->pid;
2151 (void) mode;
2152 ctx->pid = 0;
2154 unregister_task_with_pid (pid);
2155 /* file_op_context_destroy(ctx); */
2156 return 1;
2158 #endif
2159 /* }}} */
2161 /* --------------------------------------------------------------------------------------------- */
2162 /*** public functions ****************************************************************************/
2163 /* --------------------------------------------------------------------------------------------- */
2165 /* Is file symlink to directory or not.
2167 * @param path file or directory
2168 * @param st result of mc_lstat(vpath). If NULL, mc_lstat(vpath) is performed here
2169 * @param stale_link TRUE if file is stale link to directory
2171 * @return TRUE if file symlink to directory, ELSE otherwise.
2173 gboolean
2174 file_is_symlink_to_dir (const vfs_path_t * vpath, struct stat * st, gboolean * stale_link)
2176 struct stat st2;
2177 gboolean stale = FALSE;
2178 gboolean res = FALSE;
2180 if (st == NULL)
2182 st = &st2;
2184 if (mc_lstat (vpath, st) != 0)
2185 goto ret;
2188 if (S_ISLNK (st->st_mode))
2190 struct stat st3;
2192 stale = (mc_stat (vpath, &st3) != 0);
2194 if (!stale)
2195 res = (S_ISDIR (st3.st_mode) != 0);
2198 ret:
2199 if (stale_link != NULL)
2200 *stale_link = stale;
2202 return res;
2205 /* --------------------------------------------------------------------------------------------- */
2207 FileProgressStatus
2208 copy_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx,
2209 const char *src_path, const char *dst_path)
2211 uid_t src_uid = (uid_t) (-1);
2212 gid_t src_gid = (gid_t) (-1);
2214 int src_desc, dest_desc = -1;
2215 mode_t src_mode = 0; /* The mode of the source file */
2216 struct stat src_stat, dst_stat;
2217 mc_timesbuf_t times;
2218 gboolean dst_exists = FALSE, appending = FALSE;
2219 off_t file_size = -1;
2220 FileProgressStatus return_status, temp_status;
2221 struct timeval tv_transfer_start;
2222 dest_status_t dst_status = DEST_NONE;
2223 int open_flags;
2224 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
2225 char *buf = NULL;
2227 /* FIXME: We should not be using global variables! */
2228 ctx->do_reget = 0;
2229 return_status = FILE_RETRY;
2231 dst_vpath = vfs_path_from_str (dst_path);
2232 src_vpath = vfs_path_from_str (src_path);
2234 file_progress_show_source (ctx, src_vpath);
2235 file_progress_show_target (ctx, dst_vpath);
2237 if (check_progress_buttons (ctx) == FILE_ABORT)
2239 return_status = FILE_ABORT;
2240 goto ret_fast;
2243 mc_refresh ();
2245 while (mc_stat (dst_vpath, &dst_stat) == 0)
2247 if (S_ISDIR (dst_stat.st_mode))
2249 if (ctx->skip_all)
2250 return_status = FILE_SKIPALL;
2251 else
2253 return_status =
2254 file_error (TRUE, _("Cannot overwrite directory \"%s\"\n%s"), dst_path);
2255 if (return_status == FILE_SKIPALL)
2256 ctx->skip_all = TRUE;
2257 if (return_status == FILE_RETRY)
2258 continue;
2260 goto ret_fast;
2263 dst_exists = TRUE;
2264 break;
2267 while ((*ctx->stat_func) (src_vpath, &src_stat) != 0)
2269 if (ctx->skip_all)
2270 return_status = FILE_SKIPALL;
2271 else
2273 return_status = file_error (TRUE, _("Cannot stat source file \"%s\"\n%s"), src_path);
2274 if (return_status == FILE_SKIPALL)
2275 ctx->skip_all = TRUE;
2278 if (return_status != FILE_RETRY)
2279 goto ret_fast;
2282 if (dst_exists)
2284 /* Destination already exists */
2285 if (check_same_file (src_path, &src_stat, dst_path, &dst_stat, &return_status))
2286 goto ret_fast;
2288 /* Should we replace destination? */
2289 if (tctx->ask_overwrite)
2291 ctx->do_reget = 0;
2292 return_status = query_replace (ctx, src_path, &src_stat, dst_path, &dst_stat);
2293 if (return_status != FILE_CONT)
2294 goto ret_fast;
2298 if (!ctx->do_append)
2300 /* Check the hardlinks */
2301 if (!ctx->follow_links)
2303 switch (check_hardlinks (src_vpath, &src_stat, dst_vpath, &ctx->skip_all))
2305 case HARDLINK_OK:
2306 /* We have made a hardlink - no more processing is necessary */
2307 return_status = FILE_CONT;
2308 goto ret_fast;
2310 case HARDLINK_ABORT:
2311 return_status = FILE_ABORT;
2312 goto ret_fast;
2314 default:
2315 break;
2319 if (S_ISLNK (src_stat.st_mode))
2321 return_status = make_symlink (ctx, src_path, dst_path);
2322 goto ret_fast;
2325 if (S_ISCHR (src_stat.st_mode) || S_ISBLK (src_stat.st_mode) || S_ISFIFO (src_stat.st_mode)
2326 || S_ISNAM (src_stat.st_mode) || S_ISSOCK (src_stat.st_mode))
2328 dev_t rdev = 0;
2330 #ifdef HAVE_STRUCT_STAT_ST_RDEV
2331 rdev = src_stat.st_rdev;
2332 #endif
2334 while (mc_mknod (dst_vpath, src_stat.st_mode & ctx->umask_kill, rdev) < 0
2335 && !ctx->skip_all)
2337 return_status =
2338 file_error (TRUE, _("Cannot create special file \"%s\"\n%s"), dst_path);
2339 if (return_status == FILE_RETRY)
2340 continue;
2341 if (return_status == FILE_SKIPALL)
2342 ctx->skip_all = TRUE;
2343 goto ret_fast;
2345 /* Success */
2347 while (ctx->preserve_uidgid
2348 && mc_chown (dst_vpath, src_stat.st_uid, src_stat.st_gid) != 0 && !ctx->skip_all)
2350 temp_status = file_error (TRUE, _("Cannot chown target file \"%s\"\n%s"), dst_path);
2351 if (temp_status == FILE_SKIP)
2352 break;
2353 if (temp_status == FILE_SKIPALL)
2354 ctx->skip_all = TRUE;
2355 if (temp_status != FILE_RETRY)
2357 return_status = temp_status;
2358 goto ret_fast;
2362 while (ctx->preserve && mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill) != 0
2363 && !ctx->skip_all)
2365 temp_status = file_error (TRUE, _("Cannot chmod target file \"%s\"\n%s"), dst_path);
2366 if (temp_status == FILE_SKIP)
2367 break;
2368 if (temp_status == FILE_SKIPALL)
2369 ctx->skip_all = TRUE;
2370 if (temp_status != FILE_RETRY)
2372 return_status = temp_status;
2373 goto ret_fast;
2377 return_status = FILE_CONT;
2378 goto ret_fast;
2382 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
2384 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
2386 return_status = file_error (TRUE, _("Cannot open source file \"%s\"\n%s"), src_path);
2387 if (return_status == FILE_RETRY)
2388 continue;
2389 if (return_status == FILE_SKIPALL)
2390 ctx->skip_all = TRUE;
2391 if (return_status == FILE_SKIP)
2392 break;
2393 ctx->do_append = FALSE;
2394 goto ret_fast;
2397 if (ctx->do_reget != 0)
2399 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
2401 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
2402 ctx->do_reget = 0;
2403 ctx->do_append = FALSE;
2407 while (mc_fstat (src_desc, &src_stat) != 0)
2409 if (ctx->skip_all)
2410 return_status = FILE_SKIPALL;
2411 else
2413 return_status = file_error (TRUE, _("Cannot fstat source file \"%s\"\n%s"), src_path);
2414 if (return_status == FILE_RETRY)
2415 continue;
2416 if (return_status == FILE_SKIPALL)
2417 ctx->skip_all = TRUE;
2418 ctx->do_append = FALSE;
2420 goto ret;
2423 src_mode = src_stat.st_mode;
2424 src_uid = src_stat.st_uid;
2425 src_gid = src_stat.st_gid;
2426 get_times (&src_stat, &times);
2427 file_size = src_stat.st_size;
2429 open_flags = O_WRONLY;
2430 if (dst_exists)
2432 if (ctx->do_append)
2433 open_flags |= O_APPEND;
2434 else
2435 open_flags |= O_CREAT | O_TRUNC;
2437 else
2439 open_flags |= O_CREAT | O_EXCL;
2442 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
2444 if (errno != EEXIST)
2446 if (ctx->skip_all)
2447 return_status = FILE_SKIPALL;
2448 else
2450 return_status =
2451 file_error (TRUE, _("Cannot create target file \"%s\"\n%s"), dst_path);
2452 if (return_status == FILE_RETRY)
2453 continue;
2454 if (return_status == FILE_SKIPALL)
2455 ctx->skip_all = TRUE;
2456 ctx->do_append = FALSE;
2459 goto ret;
2461 dst_status = DEST_SHORT; /* file opened, but not fully copied */
2463 appending = ctx->do_append;
2464 ctx->do_append = FALSE;
2466 /* Try clone the file first. */
2467 if (vfs_clone_file (dest_desc, src_desc) == 0)
2469 dst_status = DEST_FULL;
2470 return_status = FILE_CONT;
2471 goto ret;
2474 /* Find out the optimal buffer size. */
2475 while (mc_fstat (dest_desc, &dst_stat) != 0)
2477 if (ctx->skip_all)
2478 return_status = FILE_SKIPALL;
2479 else
2481 return_status = file_error (TRUE, _("Cannot fstat target file \"%s\"\n%s"), dst_path);
2482 if (return_status == FILE_RETRY)
2483 continue;
2484 if (return_status == FILE_SKIPALL)
2485 ctx->skip_all = TRUE;
2487 goto ret;
2490 /* try preallocate space; if fail, try copy anyway */
2491 while (mc_global.vfs.preallocate_space &&
2492 vfs_preallocate (dest_desc, file_size, appending ? dst_stat.st_size : 0) != 0)
2494 if (ctx->skip_all)
2496 /* cannot allocate, start the file copying anyway */
2497 return_status = FILE_CONT;
2498 break;
2501 return_status =
2502 file_error (TRUE, _("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
2504 if (return_status == FILE_SKIPALL)
2505 ctx->skip_all = TRUE;
2507 if (ctx->skip_all || return_status == FILE_SKIP)
2509 /* skip the space allocation error, start file copying */
2510 return_status = FILE_CONT;
2511 break;
2514 if (return_status == FILE_ABORT)
2516 mc_close (dest_desc);
2517 dest_desc = -1;
2518 mc_unlink (dst_vpath);
2519 dst_status = DEST_NONE;
2520 goto ret;
2523 /* return_status == FILE_RETRY -- try allocate space again */
2526 ctx->eta_secs = 0.0;
2527 ctx->bps = 0;
2529 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
2530 file_progress_show (ctx, 0, file_size, "", TRUE);
2531 else
2532 file_progress_show (ctx, 1, 1, "", TRUE);
2533 return_status = check_progress_buttons (ctx);
2534 mc_refresh ();
2536 if (return_status == FILE_CONT)
2538 size_t bufsize;
2539 off_t n_read_total = 0;
2540 struct timeval tv_current, tv_last_update, tv_last_input;
2541 int secs, update_secs;
2542 const char *stalled_msg = "";
2543 gboolean is_first_time = TRUE;
2545 tv_last_update = tv_transfer_start;
2547 bufsize = io_blksize (dst_stat);
2548 buf = g_malloc (bufsize);
2550 while (TRUE)
2552 ssize_t n_read = -1, n_written;
2554 /* src_read */
2555 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0) == 0)
2556 while ((n_read = mc_read (src_desc, buf, bufsize)) < 0 && !ctx->skip_all)
2558 return_status =
2559 file_error (TRUE, _("Cannot read source file \"%s\"\n%s"), src_path);
2560 if (return_status == FILE_RETRY)
2561 continue;
2562 if (return_status == FILE_SKIPALL)
2563 ctx->skip_all = TRUE;
2564 goto ret;
2567 if (n_read == 0)
2568 break;
2570 gettimeofday (&tv_current, NULL);
2572 if (n_read > 0)
2574 char *t = buf;
2576 n_read_total += n_read;
2578 /* Windows NT ftp servers report that files have no
2579 * permissions: -------, so if we happen to have actually
2580 * read something, we should fix the permissions.
2582 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
2583 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
2584 gettimeofday (&tv_last_input, NULL);
2586 /* dst_write */
2587 while ((n_written = mc_write (dest_desc, t, (size_t) n_read)) < n_read)
2589 gboolean write_errno_nospace;
2591 if (n_written > 0)
2593 n_read -= n_written;
2594 t += n_written;
2595 continue;
2598 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
2600 if (ctx->skip_all)
2601 return_status = FILE_SKIPALL;
2602 else
2603 return_status =
2604 file_error (TRUE, _("Cannot write target file \"%s\"\n%s"), dst_path);
2606 if (return_status == FILE_SKIP)
2608 if (write_errno_nospace)
2609 goto ret;
2610 break;
2612 if (return_status == FILE_SKIPALL)
2614 ctx->skip_all = TRUE;
2615 if (write_errno_nospace)
2616 goto ret;
2618 if (return_status != FILE_RETRY)
2619 goto ret;
2623 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
2625 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
2626 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
2628 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
2630 copy_file_file_display_progress (tctx, ctx,
2631 tv_current,
2632 tv_transfer_start, file_size, n_read_total);
2633 tv_last_update = tv_current;
2635 is_first_time = FALSE;
2637 if (update_secs > FILEOP_STALLING_INTERVAL)
2639 stalled_msg = _("(stalled)");
2643 gboolean force_update;
2645 force_update =
2646 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
2648 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2650 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2651 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
2654 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
2655 force_update);
2657 mc_refresh ();
2659 return_status = check_progress_buttons (ctx);
2661 if (return_status != FILE_CONT)
2663 mc_refresh ();
2664 goto ret;
2668 dst_status = DEST_FULL; /* copy successful, don't remove target file */
2671 ret:
2672 g_free (buf);
2674 rotate_dash (FALSE);
2675 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
2677 temp_status = file_error (TRUE, _("Cannot close source file \"%s\"\n%s"), src_path);
2678 if (temp_status == FILE_RETRY)
2679 continue;
2680 if (temp_status == FILE_ABORT)
2681 return_status = temp_status;
2682 if (temp_status == FILE_SKIPALL)
2683 ctx->skip_all = TRUE;
2684 break;
2687 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
2689 temp_status = file_error (TRUE, _("Cannot close target file \"%s\"\n%s"), dst_path);
2690 if (temp_status == FILE_RETRY)
2691 continue;
2692 if (temp_status == FILE_SKIPALL)
2693 ctx->skip_all = TRUE;
2694 return_status = temp_status;
2695 break;
2698 if (dst_status == DEST_SHORT)
2700 /* Query to remove short file */
2701 if (query_dialog (Q_ ("DialogTitle|Copy"), _("Incomplete file was retrieved. Keep it?"),
2702 D_ERROR, 2, _("&Delete"), _("&Keep")) == 0)
2703 mc_unlink (dst_vpath);
2705 else if (dst_status == DEST_FULL)
2707 /* Copy has succeeded */
2708 if (!appending && ctx->preserve_uidgid)
2710 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
2712 temp_status = file_error (TRUE, _("Cannot chown target file \"%s\"\n%s"), dst_path);
2713 if (temp_status == FILE_RETRY)
2714 continue;
2715 if (temp_status == FILE_SKIPALL)
2717 ctx->skip_all = TRUE;
2718 return_status = FILE_CONT;
2720 if (temp_status == FILE_SKIP)
2721 return_status = FILE_CONT;
2722 break;
2726 if (!appending)
2728 if (ctx->preserve)
2730 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
2732 temp_status =
2733 file_error (TRUE, _("Cannot chmod target file \"%s\"\n%s"), dst_path);
2734 if (temp_status == FILE_RETRY)
2735 continue;
2736 if (temp_status == FILE_SKIPALL)
2738 ctx->skip_all = TRUE;
2739 return_status = FILE_CONT;
2741 if (temp_status == FILE_SKIP)
2742 return_status = FILE_CONT;
2743 break;
2746 else if (!dst_exists)
2748 src_mode = umask (-1);
2749 umask (src_mode);
2750 src_mode = 0100666 & ~src_mode;
2751 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
2753 mc_utime (dst_vpath, &times);
2757 if (return_status == FILE_CONT)
2758 return_status = progress_update_one (tctx, ctx, file_size);
2760 ret_fast:
2761 vfs_path_free (src_vpath);
2762 vfs_path_free (dst_vpath);
2763 return return_status;
2766 /* --------------------------------------------------------------------------------------------- */
2768 * I think these copy_*_* functions should have a return type.
2769 * anyway, this function *must* have two directories as arguments.
2771 /* FIXME: This function needs to check the return values of the
2772 function calls */
2774 FileProgressStatus
2775 copy_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d,
2776 gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs)
2778 struct dirent *next;
2779 struct stat dst_stat, src_stat;
2780 DIR *reading;
2781 FileProgressStatus return_status = FILE_CONT;
2782 struct link *lp;
2783 vfs_path_t *src_vpath, *dst_vpath;
2784 gboolean do_mkdir = TRUE;
2786 src_vpath = vfs_path_from_str (s);
2787 dst_vpath = vfs_path_from_str (d);
2789 /* First get the mode of the source dir */
2791 retry_src_stat:
2792 if ((*ctx->stat_func) (src_vpath, &src_stat) != 0)
2794 if (ctx->skip_all)
2795 return_status = FILE_SKIPALL;
2796 else
2798 return_status = file_error (TRUE, _("Cannot stat source directory \"%s\"\n%s"), s);
2799 if (return_status == FILE_RETRY)
2800 goto retry_src_stat;
2801 if (return_status == FILE_SKIPALL)
2802 ctx->skip_all = TRUE;
2804 goto ret_fast;
2807 if (is_in_linklist (dest_dirs, src_vpath, &src_stat) != NULL)
2809 /* Don't copy a directory we created before (we don't want to copy
2810 infinitely if a directory is copied into itself) */
2811 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2812 return_status = FILE_CONT;
2813 goto ret_fast;
2816 /* Hmm, hardlink to directory??? - Norbert */
2817 /* FIXME: In this step we should do something in case the destination already exist */
2818 /* Check the hardlinks */
2819 if (ctx->preserve)
2821 switch (check_hardlinks (src_vpath, &src_stat, dst_vpath, &ctx->skip_all))
2823 case HARDLINK_OK:
2824 /* We have made a hardlink - no more processing is necessary */
2825 goto ret_fast;
2827 case HARDLINK_ABORT:
2828 return_status = FILE_ABORT;
2829 goto ret_fast;
2831 default:
2832 break;
2836 if (!S_ISDIR (src_stat.st_mode))
2838 if (ctx->skip_all)
2839 return_status = FILE_SKIPALL;
2840 else
2842 return_status = file_error (TRUE, _("Source \"%s\" is not a directory\n%s"), s);
2843 if (return_status == FILE_RETRY)
2844 goto retry_src_stat;
2845 if (return_status == FILE_SKIPALL)
2846 ctx->skip_all = TRUE;
2848 goto ret_fast;
2851 if (is_in_linklist (parent_dirs, src_vpath, &src_stat) != NULL)
2853 /* we found a cyclic symbolic link */
2854 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
2855 return_status = FILE_SKIP;
2856 goto ret_fast;
2859 lp = g_new0 (struct link, 1);
2860 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
2861 lp->ino = src_stat.st_ino;
2862 lp->dev = src_stat.st_dev;
2863 parent_dirs = g_slist_prepend (parent_dirs, lp);
2865 retry_dst_stat:
2866 /* Now, check if the dest dir exists, if not, create it. */
2867 if (mc_stat (dst_vpath, &dst_stat) != 0)
2869 /* Here the dir doesn't exist : make it ! */
2870 if (move_over && mc_rename (src_vpath, dst_vpath) == 0)
2872 return_status = FILE_CONT;
2873 goto ret;
2876 else
2879 * If the destination directory exists, we want to copy the whole
2880 * directory, but we only want this to happen once.
2882 * Escape sequences added to the * to compiler warnings.
2883 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2884 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2886 if (!S_ISDIR (dst_stat.st_mode))
2888 if (ctx->skip_all)
2889 return_status = FILE_SKIPALL;
2890 else
2892 return_status =
2893 file_error (TRUE, _("Destination \"%s\" must be a directory\n%s"), d);
2894 if (return_status == FILE_SKIPALL)
2895 ctx->skip_all = TRUE;
2896 if (return_status == FILE_RETRY)
2897 goto retry_dst_stat;
2899 goto ret;
2901 /* Dive into subdir if exists */
2902 if (toplevel && ctx->dive_into_subdirs)
2904 vfs_path_t *tmp;
2906 tmp = dst_vpath;
2907 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
2908 vfs_path_free (tmp);
2911 else
2912 do_mkdir = FALSE;
2915 d = vfs_path_as_str (dst_vpath);
2917 if (do_mkdir)
2919 while (my_mkdir (dst_vpath, (src_stat.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2921 if (ctx->skip_all)
2922 return_status = FILE_SKIPALL;
2923 else
2925 return_status =
2926 file_error (TRUE, _("Cannot create target directory \"%s\"\n%s"), d);
2927 if (return_status == FILE_SKIPALL)
2928 ctx->skip_all = TRUE;
2930 if (return_status != FILE_RETRY)
2931 goto ret;
2934 lp = g_new0 (struct link, 1);
2935 mc_stat (dst_vpath, &dst_stat);
2936 lp->vfs = vfs_path_get_by_index (dst_vpath, -1)->class;
2937 lp->ino = dst_stat.st_ino;
2938 lp->dev = dst_stat.st_dev;
2939 dest_dirs = g_slist_prepend (dest_dirs, lp);
2942 if (ctx->preserve_uidgid)
2944 while (mc_chown (dst_vpath, src_stat.st_uid, src_stat.st_gid) != 0)
2946 if (ctx->skip_all)
2947 return_status = FILE_SKIPALL;
2948 else
2950 return_status = file_error (TRUE, _("Cannot chown target directory \"%s\"\n%s"), d);
2951 if (return_status == FILE_SKIPALL)
2952 ctx->skip_all = TRUE;
2954 if (return_status != FILE_RETRY)
2955 goto ret;
2959 /* open the source dir for reading */
2960 reading = mc_opendir (src_vpath);
2961 if (reading == NULL)
2962 goto ret;
2964 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2966 char *path;
2967 vfs_path_t *tmp_vpath;
2970 * Now, we don't want '.' and '..' to be created / copied at any time
2972 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
2973 continue;
2975 /* get the filename and add it to the src directory */
2976 path = mc_build_filename (s, next->d_name, (char *) NULL);
2977 tmp_vpath = vfs_path_from_str (path);
2979 (*ctx->stat_func) (tmp_vpath, &dst_stat);
2980 if (S_ISDIR (dst_stat.st_mode))
2982 char *mdpath;
2984 mdpath = mc_build_filename (d, next->d_name, (char *) NULL);
2986 * From here, we just intend to recursively copy subdirs, not
2987 * the double functionality of copying different when the target
2988 * dir already exists. So, we give the recursive call the flag 0
2989 * meaning no toplevel.
2991 return_status =
2992 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2993 g_free (mdpath);
2995 else
2997 char *dest_file;
2999 dest_file = mc_build_filename (d, x_basename (path), (char *) NULL);
3000 return_status = copy_file_file (tctx, ctx, path, dest_file);
3001 g_free (dest_file);
3004 g_free (path);
3006 if (do_delete && return_status == FILE_CONT)
3008 if (ctx->erase_at_end)
3010 lp = g_new0 (struct link, 1);
3011 lp->src_vpath = tmp_vpath;
3012 lp->st_mode = dst_stat.st_mode;
3013 erase_list = g_slist_append (erase_list, lp);
3014 tmp_vpath = NULL;
3016 else if (S_ISDIR (dst_stat.st_mode))
3017 return_status = erase_dir_iff_empty (ctx, tmp_vpath, tctx->progress_count);
3018 else
3019 return_status = erase_file (tctx, ctx, tmp_vpath);
3021 vfs_path_free (tmp_vpath);
3023 mc_closedir (reading);
3025 if (ctx->preserve)
3027 mc_timesbuf_t times;
3029 mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill);
3030 get_times (&src_stat, &times);
3031 mc_utime (dst_vpath, &times);
3033 else
3035 src_stat.st_mode = umask (-1);
3036 umask (src_stat.st_mode);
3037 src_stat.st_mode = 0100777 & ~src_stat.st_mode;
3038 mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill);
3041 ret:
3042 free_link (parent_dirs->data);
3043 g_slist_free_1 (parent_dirs);
3044 ret_fast:
3045 vfs_path_free (src_vpath);
3046 vfs_path_free (dst_vpath);
3047 return return_status;
3050 /* }}} */
3052 /* --------------------------------------------------------------------------------------------- */
3053 /* {{{ Move routines */
3055 FileProgressStatus
3056 move_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d)
3058 return do_move_dir_dir (NULL, tctx, ctx, s, d);
3061 /* }}} */
3063 /* --------------------------------------------------------------------------------------------- */
3064 /* {{{ Erase routines */
3066 FileProgressStatus
3067 erase_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * vpath)
3069 file_progress_show_deleting (ctx, vfs_path_as_str (vpath), NULL);
3070 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3071 if (check_progress_buttons (ctx) == FILE_ABORT)
3072 return FILE_ABORT;
3074 mc_refresh ();
3076 /* The old way to detect a non empty directory was:
3077 error = my_rmdir (s);
3078 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
3079 For the linux user space nfs server (nfs-server-2.2beta29-2)
3080 we would have to check also for EIO. I hope the new way is
3081 fool proof. (Norbert)
3083 if (check_dir_is_empty (vpath) == 0)
3084 { /* not empty */
3085 FileProgressStatus error;
3087 error = query_recursive (ctx, vfs_path_as_str (vpath));
3088 if (error == FILE_CONT)
3089 error = recursive_erase (tctx, ctx, vpath);
3090 return error;
3093 return try_erase_dir (ctx, vfs_path_as_str (vpath));
3096 /* }}} */
3098 /* --------------------------------------------------------------------------------------------- */
3099 /* {{{ Panel operate routines */
3101 void
3102 dirsize_status_init_cb (status_msg_t * sm)
3104 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
3105 WGroup *gd = GROUP (sm->dlg);
3106 Widget *wd = WIDGET (sm->dlg);
3108 const char *b1_name = N_("&Abort");
3109 const char *b2_name = N_("&Skip");
3110 int b_width, ui_width;
3112 #ifdef ENABLE_NLS
3113 b1_name = _(b1_name);
3114 b2_name = _(b2_name);
3115 #endif
3117 b_width = str_term_width1 (b1_name) + 4;
3118 if (dsm->allow_skip)
3119 b_width += str_term_width1 (b2_name) + 4 + 1;
3121 ui_width = MAX (COLS / 2, b_width + 6);
3122 dsm->dirname = label_new (2, 3, "");
3123 group_add_widget (gd, dsm->dirname);
3124 dsm->count_size = label_new (3, 3, "");
3125 group_add_widget (gd, dsm->count_size);
3126 group_add_widget (gd, hline_new (4, -1, -1));
3128 dsm->abort_button = WIDGET (button_new (5, 3, FILE_ABORT, NORMAL_BUTTON, b1_name, NULL));
3129 group_add_widget (gd, dsm->abort_button);
3130 if (dsm->allow_skip)
3132 dsm->skip_button = WIDGET (button_new (5, 3, FILE_SKIP, NORMAL_BUTTON, b2_name, NULL));
3133 group_add_widget (gd, dsm->skip_button);
3134 widget_select (dsm->skip_button);
3137 widget_set_size (wd, wd->y, wd->x, 8, ui_width);
3138 dirsize_status_locate_buttons (dsm);
3141 /* --------------------------------------------------------------------------------------------- */
3144 dirsize_status_update_cb (status_msg_t * sm)
3146 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
3147 Widget *wd = WIDGET (sm->dlg);
3149 /* update second (longer label) */
3150 label_set_textv (dsm->count_size, _("Directories: %zu, total size: %s"),
3151 dsm->dir_count, size_trunc_sep (dsm->total_size, panels_options.kilobyte_si));
3153 /* enlarge dialog if required */
3154 if (WIDGET (dsm->count_size)->cols + 6 > wd->cols)
3156 dlg_set_size (sm->dlg, wd->lines, WIDGET (dsm->count_size)->cols + 6);
3157 dirsize_status_locate_buttons (dsm);
3158 dlg_draw (sm->dlg);
3161 /* adjust first label */
3162 label_set_text (dsm->dirname, str_trunc (vfs_path_as_str (dsm->dirname_vpath), wd->cols - 6));
3164 switch (status_msg_common_update (sm))
3166 case B_CANCEL:
3167 case FILE_ABORT:
3168 return FILE_ABORT;
3169 case FILE_SKIP:
3170 return FILE_SKIP;
3171 default:
3172 return FILE_CONT;
3176 /* --------------------------------------------------------------------------------------------- */
3178 void
3179 dirsize_status_deinit_cb (status_msg_t * sm)
3181 (void) sm;
3183 /* schedule to update passive panel */
3184 if (get_other_type () == view_listing)
3185 other_panel->dirty = 1;
3188 /* --------------------------------------------------------------------------------------------- */
3190 * compute_dir_size:
3192 * Computes the number of bytes used by the files in a directory
3195 FileProgressStatus
3196 compute_dir_size (const vfs_path_t * dirname_vpath, dirsize_status_msg_t * sm,
3197 size_t * ret_dir_count, size_t * ret_marked_count, uintmax_t * ret_total,
3198 gboolean compute_symlinks)
3200 return do_compute_dir_size (dirname_vpath, sm, ret_dir_count, ret_marked_count, ret_total,
3201 compute_symlinks);
3204 /* --------------------------------------------------------------------------------------------- */
3206 * panel_operate:
3208 * Performs one of the operations on the selection on the source_panel
3209 * (copy, delete, move).
3211 * Returns TRUE if did change the directory
3212 * structure, Returns FALSE if user aborted
3214 * force_single forces operation on the current entry and affects
3215 * default destination. Current filename is used as default.
3218 gboolean
3219 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
3221 WPanel *panel = PANEL (source_panel);
3222 const gboolean single_entry = force_single || (panel->marked <= 1)
3223 || (get_current_type () == view_tree);
3225 const char *source = NULL;
3226 char *dest = NULL;
3227 vfs_path_t *dest_vpath = NULL;
3228 char *save_cwd = NULL, *save_dest = NULL;
3229 struct stat src_stat;
3230 gboolean ret_val = TRUE;
3231 int i;
3232 FileProgressStatus value;
3233 file_op_context_t *ctx;
3234 file_op_total_context_t *tctx;
3235 vfs_path_t *tmp_vpath;
3236 filegui_dialog_type_t dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3238 gboolean do_bg = FALSE; /* do background operation? */
3240 static gboolean i18n_flag = FALSE;
3241 if (!i18n_flag)
3243 for (i = G_N_ELEMENTS (op_names); i-- != 0;)
3244 op_names[i] = Q_ (op_names[i]);
3245 i18n_flag = TRUE;
3248 linklist = free_linklist (linklist);
3249 dest_dirs = free_linklist (dest_dirs);
3251 if (single_entry)
3253 source = check_single_entry (panel, force_single, &src_stat);
3255 if (source == NULL)
3256 return FALSE;
3259 ctx = file_op_context_new (operation);
3261 /* Show confirmation dialog */
3262 if (operation != OP_DELETE)
3264 dest =
3265 do_confirm_copy_move (panel, operation, force_single, source, &src_stat, ctx, &do_bg);
3267 if (dest == NULL)
3269 ret_val = FALSE;
3270 goto ret_fast;
3273 dest_vpath = vfs_path_from_str (dest);
3275 else if (confirm_delete && !do_confirm_erase (panel, source, &src_stat))
3277 ret_val = FALSE;
3278 goto ret_fast;
3281 tctx = file_op_total_context_new ();
3282 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
3284 #ifdef ENABLE_BACKGROUND
3285 /* Did the user select to do a background operation? */
3286 if (do_bg)
3288 int v;
3290 v = do_background (ctx,
3291 g_strconcat (op_names[operation], ": ",
3292 vfs_path_as_str (panel->cwd_vpath), (char *) NULL));
3293 if (v == -1)
3294 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
3296 /* If we are the parent */
3297 if (v == 1)
3299 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
3301 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
3302 vfs_path_free (dest_vpath);
3303 g_free (dest);
3304 /* file_op_context_destroy (ctx); */
3305 return FALSE;
3308 else
3309 #endif /* ENABLE_BACKGROUND */
3311 if (operation == OP_DELETE)
3312 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
3313 else if (single_entry && S_ISDIR (selection (panel)->st.st_mode))
3314 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3315 else if (single_entry || force_single)
3316 dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3317 else
3318 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3321 /* Initialize things */
3322 /* We do not want to trash cache every time file is
3323 created/touched. However, this will make our cache contain
3324 invalid data. */
3325 if ((dest != NULL)
3326 && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3327 save_dest = g_strdup (dest);
3329 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
3330 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3331 save_cwd = g_strdup (vfs_path_as_str (panel->cwd_vpath));
3333 /* Now, let's do the job */
3335 /* This code is only called by the tree and panel code */
3336 if (single_entry)
3338 /* We now have ETA in all cases */
3340 /* One file: FIXME mc_chdir will take user out of any vfs */
3341 if ((operation != OP_COPY) && (get_current_type () == view_tree))
3343 vfs_path_t *vpath;
3344 int chdir_retcode;
3346 vpath = vfs_path_from_str (PATH_SEP_STR);
3347 chdir_retcode = mc_chdir (vpath);
3348 vfs_path_free (vpath);
3349 if (chdir_retcode < 0)
3351 ret_val = FALSE;
3352 goto clean_up;
3356 value =
3357 operate_single_file (panel, operation, tctx, ctx, source, &src_stat, dest, dialog_type);
3359 if ((value == FILE_CONT) && !force_single)
3360 unmark_files (panel);
3362 else
3364 /* Many files */
3366 /* Check destination for copy or move operation */
3367 while (operation != OP_DELETE)
3369 int dst_result;
3370 struct stat dst_stat;
3372 dst_result = mc_stat (dest_vpath, &dst_stat);
3374 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
3375 break;
3377 if (ctx->skip_all
3378 || file_error (TRUE, _("Destination \"%s\" must be a directory\n%s"),
3379 dest) != FILE_RETRY)
3380 goto clean_up;
3383 /* TODO: the good way is required to skip directories scanning in case of rename/move
3384 * of several directories. Since reqular expression can be used for destination,
3385 * some directory movements can be a cross-filesystem and directory scanning is useful
3386 * for those directories only. */
3388 if (panel_operate_init_totals (panel, NULL, NULL, ctx, file_op_compute_totals, dialog_type)
3389 == FILE_CONT)
3391 /* Loop for every file, perform the actual copy operation */
3392 for (i = 0; i < panel->dir.len; i++)
3394 const char *source2;
3396 if (!panel->dir.list[i].f.marked)
3397 continue; /* Skip the unmarked ones */
3399 source2 = panel->dir.list[i].fname;
3400 src_stat = panel->dir.list[i].st;
3402 value = operate_one_file (panel, operation, tctx, ctx, source2, &src_stat, dest);
3404 if (value == FILE_ABORT)
3405 break;
3407 if (value == FILE_CONT)
3408 do_file_mark (panel, i, 0);
3410 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3412 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3413 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3416 if (operation != OP_DELETE)
3417 file_progress_show (ctx, 0, 0, "", FALSE);
3419 if (check_progress_buttons (ctx) == FILE_ABORT)
3420 break;
3422 mc_refresh ();
3423 } /* Loop for every file */
3425 } /* Many entries */
3427 clean_up:
3428 /* Clean up */
3429 if (save_cwd != NULL)
3431 tmp_vpath = vfs_path_from_str (save_cwd);
3432 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3433 vfs_path_free (tmp_vpath);
3434 g_free (save_cwd);
3437 if (save_dest != NULL)
3439 tmp_vpath = vfs_path_from_str (save_dest);
3440 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3441 vfs_path_free (tmp_vpath);
3442 g_free (save_dest);
3445 linklist = free_linklist (linklist);
3446 dest_dirs = free_linklist (dest_dirs);
3447 g_free (dest);
3448 vfs_path_free (dest_vpath);
3449 MC_PTR_FREE (ctx->dest_mask);
3451 #ifdef ENABLE_BACKGROUND
3452 /* Let our parent know we are saying bye bye */
3453 if (mc_global.we_are_background)
3455 int cur_pid = getpid ();
3456 /* Send pid to parent with child context, it is fork and
3457 don't modify real parent ctx */
3458 ctx->pid = cur_pid;
3459 parent_call ((void *) end_bg_process, ctx, 0);
3461 vfs_shut ();
3462 my_exit (EXIT_SUCCESS);
3464 #endif /* ENABLE_BACKGROUND */
3466 file_op_total_context_destroy (tctx);
3467 ret_fast:
3468 file_op_context_destroy (ctx);
3470 return ret_val;
3473 /* }}} */
3475 /* --------------------------------------------------------------------------------------------- */
3476 /* {{{ Query/status report routines */
3477 /** Report error with one file */
3478 FileProgressStatus
3479 file_error (gboolean allow_retry, const char *format, const char *file)
3481 char buf[BUF_MEDIUM];
3483 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3485 return do_file_error (allow_retry, buf);
3488 /* --------------------------------------------------------------------------------------------- */
3491 Cause emacs to enter folding mode for this file:
3492 Local variables:
3493 end: