(check_hardlinks): make error message more verbose.
[midnight-commander.git] / src / filemanager / file.c
blobd97731e6b4d9e37c144d50de009e5c246c44aaee
1 /*
2 File management.
4 Copyright (C) 1994-2018
5 Free Software Foundation, Inc.
7 Written by:
8 Janne Kukonlehto, 1994, 1995
9 Fred Leeflang, 1994, 1995
10 Miguel de Icaza, 1994, 1995, 1996
11 Jakub Jelinek, 1995, 1996
12 Norbert Warmuth, 1997
13 Pavel Machek, 1998
14 Andrew Borodin <aborodin@vmail.ru>, 2011-2014
16 The copy code was based in GNU's cp, and was written by:
17 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
19 The move code was based in GNU's mv, and was written by:
20 Mike Parker and David MacKenzie.
22 Janne Kukonlehto added much error recovery to them for being used
23 in an interactive program.
25 This file is part of the Midnight Commander.
27 The Midnight Commander is free software: you can redistribute it
28 and/or modify it under the terms of the GNU General Public License as
29 published by the Free Software Foundation, either version 3 of the License,
30 or (at your option) any later version.
32 The Midnight Commander is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program. If not, see <http://www.gnu.org/licenses/>.
42 * Please note that all dialogs used here must be safe for background
43 * operations.
46 /** \file src/filemanager/file.c
47 * \brief Source: file management
50 /* {{{ Include files */
52 #include <config.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
63 #include "lib/global.h"
64 #include "lib/tty/tty.h"
65 #include "lib/tty/key.h"
66 #include "lib/search.h"
67 #include "lib/strescape.h"
68 #include "lib/strutil.h"
69 #include "lib/util.h"
70 #include "lib/vfs/vfs.h"
71 #include "lib/widget.h"
73 #include "src/setup.h"
74 #ifdef ENABLE_BACKGROUND
75 #include "src/background.h" /* do_background() */
76 #endif
78 /* Needed for current_panel, other_panel and WTree */
79 #include "dir.h"
80 #include "filegui.h"
81 #include "filenot.h"
82 #include "tree.h"
83 #include "midnight.h" /* current_panel */
84 #include "layout.h" /* rotate_dash() */
85 #include "ioblksize.h" /* io_blksize() */
87 #include "file.h"
89 /* }}} */
91 /*** global variables ****************************************************************************/
93 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
94 const char *op_names[3] = {
95 N_("DialogTitle|Copy"),
96 N_("DialogTitle|Move"),
97 N_("DialogTitle|Delete")
100 /*** file scope macro definitions ****************************************************************/
102 #define FILEOP_UPDATE_INTERVAL 2
103 #define FILEOP_STALLING_INTERVAL 4
105 /*** file scope type declarations ****************************************************************/
107 /* This is a hard link cache */
108 struct link
110 const struct vfs_class *vfs;
111 dev_t dev;
112 ino_t ino;
113 short linkcount;
114 mode_t st_mode;
115 vfs_path_t *src_vpath;
116 vfs_path_t *dst_vpath;
119 /* Status of the destination file */
120 typedef enum
122 DEST_NONE = 0, /* Not created */
123 DEST_SHORT = 1, /* Created, not fully copied */
124 DEST_FULL = 2 /* Created, fully copied */
125 } dest_status_t;
128 * This array introduced to avoid translation problems. The former (op_names)
129 * is assumed to be nouns, suitable in dialog box titles; this one should
130 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
131 * (I don't use spaces around the words, because someday they could be
132 * dropped, when widgets get smarter)
135 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
136 static const char *op_names1[] = {
137 N_("FileOperation|Copy"),
138 N_("FileOperation|Move"),
139 N_("FileOperation|Delete")
143 * These are formats for building a prompt. Parts encoded as follows:
144 * %o - operation from op_names1
145 * %f - file/files or files/directories, as appropriate
146 * %m - "with source mask" or question mark for delete
147 * %s - source name (truncated)
148 * %d - number of marked files
149 * %n - the '\n' symbol to form two-line prompt for delete or space for other operations
151 /* xgettext:no-c-format */
152 static const char *one_format = N_("%o %f%n\"%s\"%m");
153 /* xgettext:no-c-format */
154 static const char *many_format = N_("%o %d %f%m");
156 static const char *prompt_parts[] = {
157 N_("file"),
158 N_("files"),
159 N_("directory"),
160 N_("directories"),
161 N_("files/directories"),
162 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
163 N_(" with source mask:")
166 /*** file scope variables ************************************************************************/
168 /* the hard link cache */
169 static GSList *linklist = NULL;
171 /* the files-to-be-erased list */
172 static GSList *erase_list = NULL;
175 * In copy_dir_dir we use two additional single linked lists: The first -
176 * variable name 'parent_dirs' - holds information about already copied
177 * directories and is used to detect cyclic symbolic links.
178 * The second ('dest_dirs' below) holds information about just created
179 * target directories and is used to detect when an directory is copied
180 * into itself (we don't want to copy infinitly).
181 * Both lists don't use the linkcount and name structure members of struct
182 * link.
184 static GSList *dest_dirs = NULL;
186 /* --------------------------------------------------------------------------------------------- */
187 /*** file scope functions ************************************************************************/
188 /* --------------------------------------------------------------------------------------------- */
190 static void
191 dirsize_status_locate_buttons (dirsize_status_msg_t * dsm)
193 status_msg_t *sm = STATUS_MSG (dsm);
194 Widget *wd = WIDGET (sm->dlg);
195 int y, x;
197 y = wd->y + 5;
198 x = wd->x;
200 if (!dsm->allow_skip)
202 /* single button: "Abort" */
203 x += (wd->cols - dsm->abort_button->cols) / 2;
204 widget_set_size (dsm->abort_button, y, x,
205 dsm->abort_button->lines, dsm->abort_button->cols);
207 else
209 /* two buttons: "Abort" and "Skip" */
210 int cols;
212 cols = dsm->abort_button->cols + dsm->skip_button->cols + 1;
213 x += (wd->cols - cols) / 2;
214 widget_set_size (dsm->abort_button, y, x, dsm->abort_button->lines,
215 dsm->abort_button->cols);
216 x += dsm->abort_button->cols + 1;
217 widget_set_size (dsm->skip_button, y, x, dsm->skip_button->lines, dsm->skip_button->cols);
221 /* --------------------------------------------------------------------------------------------- */
223 static char *
224 build_dest (file_op_context_t * ctx, const char *src, const char *dest, FileProgressStatus * status)
226 char *s, *q;
227 const char *fnsource;
229 *status = FILE_CONT;
231 s = g_strdup (src);
233 /* We remove \n from the filename since regex routines would use \n as an anchor */
234 /* this is just to be allowed to maniupulate file names with \n on it */
235 for (q = s; *q != '\0'; q++)
236 if (*q == '\n')
237 *q = ' ';
239 fnsource = x_basename (s);
241 if (!mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
243 q = NULL;
244 *status = FILE_SKIP;
246 else
248 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
249 if (ctx->search_handle->error != MC_SEARCH_E_OK)
251 if (ctx->search_handle->error_str != NULL)
252 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
254 *status = FILE_ABORT;
258 MC_PTR_FREE (s);
260 if (*status == FILE_CONT)
262 char *repl_dest;
264 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
265 if (ctx->search_handle->error == MC_SEARCH_E_OK)
266 s = mc_build_filename (repl_dest, q, (char *) NULL);
267 else
269 if (ctx->search_handle->error_str != NULL)
270 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
272 *status = FILE_ABORT;
275 g_free (repl_dest);
278 g_free (q);
280 return s;
283 /* --------------------------------------------------------------------------------------------- */
285 static void
286 free_link (void *data)
288 struct link *lp = (struct link *) data;
290 vfs_path_free (lp->src_vpath);
291 vfs_path_free (lp->dst_vpath);
292 g_free (lp);
295 /* --------------------------------------------------------------------------------------------- */
297 static inline void *
298 free_linklist (GSList * lp)
300 g_slist_free_full (lp, free_link);
302 return NULL;
305 /* --------------------------------------------------------------------------------------------- */
307 static gboolean
308 is_in_linklist (const GSList * lp, const vfs_path_t * vpath, const struct stat *sb)
310 const struct vfs_class *class;
311 ino_t ino = sb->st_ino;
312 dev_t dev = sb->st_dev;
314 class = vfs_path_get_last_path_vfs (vpath);
316 for (; lp != NULL; lp = (const GSList *) g_slist_next (lp))
318 const struct link *lnk = (const struct link *) lp->data;
320 if (lnk->vfs == class && lnk->ino == ino && lnk->dev == dev)
321 return TRUE;
323 return FALSE;
326 /* --------------------------------------------------------------------------------------------- */
328 * Check and made hardlink
330 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
331 * and a hardlink was successfully made
334 static gboolean
335 check_hardlinks (const vfs_path_t * src_vpath, const struct stat *src_stat,
336 const vfs_path_t * dst_vpath)
338 GSList *lp;
339 struct link *lnk;
341 const struct vfs_class *my_vfs;
342 ino_t ino = src_stat->st_ino;
343 dev_t dev = src_stat->st_dev;
344 struct stat link_stat;
346 if (src_stat->st_nlink < 2 || (vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
347 return FALSE;
349 my_vfs = vfs_path_get_by_index (src_vpath, -1)->class;
351 for (lp = linklist; lp != NULL; lp = g_slist_next (lp))
353 lnk = (struct link *) lp->data;
355 if (lnk->vfs == my_vfs && lnk->ino == ino && lnk->dev == dev)
357 const struct vfs_class *lp_name_class;
358 int stat_result;
360 lp_name_class = vfs_path_get_last_path_vfs (lnk->src_vpath);
361 stat_result = mc_stat (lnk->src_vpath, &link_stat);
363 if (stat_result == 0 && link_stat.st_ino == ino
364 && link_stat.st_dev == dev && lp_name_class == my_vfs)
366 const struct vfs_class *p_class, *dst_name_class;
368 dst_name_class = vfs_path_get_last_path_vfs (dst_vpath);
369 p_class = vfs_path_get_last_path_vfs (lnk->dst_vpath);
371 if (dst_name_class == p_class &&
372 mc_stat (lnk->dst_vpath, &link_stat) == 0 &&
373 mc_link (lnk->dst_vpath, dst_vpath) == 0)
374 return TRUE;
377 message (D_ERROR, MSG_ERROR,
378 _("Cannot make the hardlink\n%s\nto\n%s"), vfs_path_as_str (dst_vpath),
379 vfs_path_as_str (lnk->dst_vpath));
380 return FALSE;
384 lnk = g_try_new (struct link, 1);
385 if (lnk != NULL)
387 lnk->vfs = my_vfs;
388 lnk->ino = ino;
389 lnk->dev = dev;
390 lnk->linkcount = 0;
391 lnk->st_mode = 0;
392 lnk->src_vpath = vfs_path_clone (src_vpath);
393 lnk->dst_vpath = vfs_path_clone (dst_vpath);
395 linklist = g_slist_prepend (linklist, lnk);
398 return FALSE;
401 /* --------------------------------------------------------------------------------------------- */
403 * Duplicate the contents of the symbolic link src_path in dst_path.
404 * Try to make a stable symlink if the option "stable symlink" was
405 * set in the file mask dialog.
406 * If dst_path is an existing symlink it will be deleted silently
407 * (upper levels take already care of existing files at dst_path).
410 static FileProgressStatus
411 make_symlink (file_op_context_t * ctx, const char *src_path, const char *dst_path)
413 char link_target[MC_MAXPATHLEN];
414 int len;
415 FileProgressStatus return_status;
416 struct stat sb;
417 vfs_path_t *src_vpath;
418 vfs_path_t *dst_vpath;
419 gboolean dst_is_symlink;
420 vfs_path_t *link_target_vpath = NULL;
422 src_vpath = vfs_path_from_str (src_path);
423 dst_vpath = vfs_path_from_str (dst_path);
424 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
426 retry_src_readlink:
427 len = mc_readlink (src_vpath, link_target, sizeof (link_target) - 1);
428 if (len < 0)
430 if (ctx->skip_all)
431 return_status = FILE_SKIPALL;
432 else
434 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
435 if (return_status == FILE_SKIPALL)
436 ctx->skip_all = TRUE;
437 if (return_status == FILE_RETRY)
438 goto retry_src_readlink;
440 goto ret;
443 link_target[len] = '\0';
445 if (ctx->stable_symlinks && !(vfs_file_is_local (src_vpath) && vfs_file_is_local (dst_vpath)))
447 message (D_ERROR, MSG_ERROR,
448 _("Cannot make stable symlinks across "
449 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
450 ctx->stable_symlinks = FALSE;
453 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
455 const char *r;
457 r = strrchr (src_path, PATH_SEP);
458 if (r != NULL)
460 char *p;
461 vfs_path_t *q;
463 p = g_strndup (src_path, r - src_path + 1);
464 if (g_path_is_absolute (dst_path))
465 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
466 else
467 q = vfs_path_build_filename (p, dst_path, (char *) NULL);
469 if (vfs_path_tokens_count (q) > 1)
471 char *s;
472 vfs_path_t *tmp_vpath1, *tmp_vpath2;
474 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
475 s = g_strconcat (p, link_target, (char *) NULL);
476 g_strlcpy (link_target, s, sizeof (link_target));
477 g_free (s);
478 tmp_vpath2 = vfs_path_from_str (link_target);
479 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
480 vfs_path_free (tmp_vpath1);
481 vfs_path_free (tmp_vpath2);
482 if (s != NULL)
484 g_strlcpy (link_target, s, sizeof (link_target));
485 g_free (s);
488 g_free (p);
489 vfs_path_free (q);
492 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
494 retry_dst_symlink:
495 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
497 /* Success */
498 return_status = FILE_CONT;
499 goto ret;
502 * if dst_exists, it is obvious that this had failed.
503 * We can delete the old symlink and try again...
505 if (dst_is_symlink && mc_unlink (dst_vpath) == 0
506 && mc_symlink (link_target_vpath, dst_vpath) == 0)
508 /* Success */
509 return_status = FILE_CONT;
510 goto ret;
513 if (ctx->skip_all)
514 return_status = FILE_SKIPALL;
515 else
517 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
518 if (return_status == FILE_SKIPALL)
519 ctx->skip_all = TRUE;
520 if (return_status == FILE_RETRY)
521 goto retry_dst_symlink;
524 ret:
525 vfs_path_free (src_vpath);
526 vfs_path_free (dst_vpath);
527 vfs_path_free (link_target_vpath);
528 return return_status;
531 /* --------------------------------------------------------------------------------------------- */
533 * do_compute_dir_size:
535 * Computes the number of bytes used by the files in a directory
538 static FileProgressStatus
539 do_compute_dir_size (const vfs_path_t * dirname_vpath, dirsize_status_msg_t * dsm,
540 size_t * dir_count, size_t * ret_marked, uintmax_t * ret_total,
541 gboolean compute_symlinks)
543 static guint64 timestamp = 0;
544 /* update with 25 FPS rate */
545 static const guint64 delay = G_USEC_PER_SEC / 25;
547 status_msg_t *sm = STATUS_MSG (dsm);
548 int res;
549 struct stat s;
550 DIR *dir;
551 struct dirent *dirent;
552 FileProgressStatus ret = FILE_CONT;
554 if (!compute_symlinks)
556 res = mc_lstat (dirname_vpath, &s);
557 if (res != 0)
558 return ret;
560 /* don't scan symlink to directory */
561 if (S_ISLNK (s.st_mode))
563 (*ret_marked)++;
564 *ret_total += (uintmax_t) s.st_size;
565 return ret;
569 (*dir_count)++;
571 dir = mc_opendir (dirname_vpath);
572 if (dir == NULL)
573 return ret;
575 while (ret == FILE_CONT && (dirent = mc_readdir (dir)) != NULL)
577 vfs_path_t *tmp_vpath;
579 if (DIR_IS_DOT (dirent->d_name) || DIR_IS_DOTDOT (dirent->d_name))
580 continue;
582 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, (char *) NULL);
584 res = mc_lstat (tmp_vpath, &s);
585 if (res == 0)
587 if (S_ISDIR (s.st_mode))
588 ret =
589 do_compute_dir_size (tmp_vpath, dsm, dir_count, ret_marked, ret_total,
590 compute_symlinks);
591 else
593 ret = FILE_CONT;
595 (*ret_marked)++;
596 *ret_total += (uintmax_t) s.st_size;
599 if (ret == FILE_CONT && sm->update != NULL && mc_time_elapsed (&timestamp, delay))
601 dsm->dirname_vpath = tmp_vpath;
602 dsm->dir_count = *dir_count;
603 dsm->total_size = *ret_total;
604 ret = sm->update (sm);
608 vfs_path_free (tmp_vpath);
611 mc_closedir (dir);
612 return ret;
615 /* --------------------------------------------------------------------------------------------- */
617 * panel_compute_totals:
619 * compute the number of files and the number of bytes
620 * used up by the whole selection, recursing directories
621 * as required. In addition, it checks to see if it will
622 * overwrite any files by doing the copy.
625 static FileProgressStatus
626 panel_compute_totals (const WPanel * panel, dirsize_status_msg_t * sm, size_t * ret_count,
627 uintmax_t * ret_total, gboolean compute_symlinks)
629 int i;
630 size_t dir_count = 0;
632 for (i = 0; i < panel->dir.len; i++)
634 struct stat *s;
636 if (!panel->dir.list[i].f.marked)
637 continue;
639 s = &panel->dir.list[i].st;
641 if (S_ISDIR (s->st_mode))
643 vfs_path_t *p;
644 FileProgressStatus status;
646 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, (char *) NULL);
647 status = compute_dir_size (p, sm, &dir_count, ret_count, ret_total, compute_symlinks);
648 vfs_path_free (p);
650 if (status != FILE_CONT)
651 return status;
653 else
655 (*ret_count)++;
656 *ret_total += (uintmax_t) s->st_size;
660 return FILE_CONT;
663 /* --------------------------------------------------------------------------------------------- */
665 /** Initialize variables for progress bars */
666 static FileProgressStatus
667 panel_operate_init_totals (const WPanel * panel, const vfs_path_t * source,
668 const struct stat *source_stat, file_op_context_t * ctx,
669 gboolean compute_totals, filegui_dialog_type_t dialog_type)
671 FileProgressStatus status;
673 #ifdef ENABLE_BACKGROUND
674 if (mc_global.we_are_background)
675 return FILE_CONT;
676 #endif
678 if (verbose && compute_totals)
680 dirsize_status_msg_t dsm;
682 memset (&dsm, 0, sizeof (dsm));
683 dsm.allow_skip = TRUE;
684 status_msg_init (STATUS_MSG (&dsm), _("Directory scanning"), 0, dirsize_status_init_cb,
685 dirsize_status_update_cb, dirsize_status_deinit_cb);
687 ctx->progress_count = 0;
688 ctx->progress_bytes = 0;
690 if (source == NULL)
691 status = panel_compute_totals (panel, &dsm, &ctx->progress_count, &ctx->progress_bytes,
692 ctx->follow_links);
693 else if (S_ISDIR (source_stat->st_mode))
695 size_t dir_count = 0;
697 status = compute_dir_size (source, &dsm, &dir_count, &ctx->progress_count,
698 &ctx->progress_bytes, ctx->follow_links);
700 else
702 ctx->progress_count++;
703 ctx->progress_bytes += (uintmax_t) source_stat->st_size;
704 status = FILE_CONT;
707 status_msg_deinit (STATUS_MSG (&dsm));
709 ctx->progress_totals_computed = (status == FILE_CONT);
711 if (status == FILE_SKIP)
712 status = FILE_CONT;
714 else
716 status = FILE_CONT;
717 ctx->progress_count = panel->marked;
718 ctx->progress_bytes = panel->total;
719 ctx->progress_totals_computed = FALSE;
722 /* destroy already created UI for single file rename operation */
723 file_op_context_destroy_ui (ctx);
725 file_op_context_create_ui (ctx, TRUE, dialog_type);
727 return status;
730 /* --------------------------------------------------------------------------------------------- */
732 static FileProgressStatus
733 progress_update_one (file_op_total_context_t * tctx, file_op_context_t * ctx, off_t add)
735 struct timeval tv_current;
736 static struct timeval tv_start = { 0, 0 };
738 tctx->progress_count++;
739 tctx->progress_bytes += (uintmax_t) add;
741 if (tv_start.tv_sec == 0)
743 gettimeofday (&tv_start, (struct timezone *) NULL);
745 gettimeofday (&tv_current, (struct timezone *) NULL);
746 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
748 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
750 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
751 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
753 tv_start.tv_sec = tv_current.tv_sec;
756 return check_progress_buttons (ctx);
759 /* --------------------------------------------------------------------------------------------- */
761 static FileProgressStatus
762 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
764 char *msg;
765 int result = 0;
766 const char *head_msg;
768 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
770 msg = g_strdup_printf (fmt, a, b);
771 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
772 g_free (msg);
773 do_refresh ();
775 return (result == 1) ? FILE_ABORT : FILE_SKIP;
778 /* --------------------------------------------------------------------------------------------- */
780 static FileProgressStatus
781 warn_same_file (const char *fmt, const char *a, const char *b)
783 #ifdef ENABLE_BACKGROUND
784 /* *INDENT-OFF* */
785 union
787 void *p;
788 FileProgressStatus (*f) (enum OperationMode, const char *fmt, const char *a, const char *b);
789 } pntr;
790 /* *INDENT-ON* */
792 pntr.f = real_warn_same_file;
794 if (mc_global.we_are_background)
795 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
796 #endif
797 return real_warn_same_file (Foreground, fmt, a, b);
800 /* --------------------------------------------------------------------------------------------- */
802 static gboolean
803 check_same_file (const char *a, const struct stat *ast, const char *b, const struct stat *bst,
804 FileProgressStatus * status)
806 if (ast->st_dev != bst->st_dev || ast->st_ino != bst->st_ino)
807 return FALSE;
809 if (S_ISDIR (ast->st_mode))
810 *status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), a, b);
811 else
812 *status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), a, b);
814 return TRUE;
817 /* --------------------------------------------------------------------------------------------- */
819 static void
820 get_times (const struct stat *sb, mc_timesbuf_t * times)
822 #ifdef HAVE_UTIMENSAT
823 (*times)[0] = sb->st_atim;
824 (*times)[1] = sb->st_mtim;
825 #else
826 times->actime = sb->st_atime;
827 times->modtime = sb->st_mtime;
828 #endif
831 /* --------------------------------------------------------------------------------------------- */
832 /* {{{ Query/status report routines */
834 static FileProgressStatus
835 real_do_file_error (enum OperationMode mode, const char *error)
837 int result;
838 const char *msg;
840 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
841 result =
842 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
844 switch (result)
846 case 0:
847 do_refresh ();
848 return FILE_SKIP;
850 case 1:
851 do_refresh ();
852 return FILE_SKIPALL;
854 case 2:
855 do_refresh ();
856 return FILE_RETRY;
858 case 3:
859 default:
860 return FILE_ABORT;
864 /* --------------------------------------------------------------------------------------------- */
866 static FileProgressStatus
867 real_query_recursive (file_op_context_t * ctx, enum OperationMode mode, const char *s)
869 if (ctx->recursive_result < RECURSIVE_ALWAYS)
871 const char *msg;
872 char *text;
874 msg = mode == Foreground
875 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
876 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
877 text = g_strdup_printf (msg, path_trunc (s, 30));
879 if (safe_delete)
880 query_set_sel (1);
882 ctx->recursive_result =
883 query_dialog (op_names[OP_DELETE], text, D_ERROR, 5, _("&Yes"), _("&No"), _("A&ll"),
884 _("Non&e"), _("&Abort"));
885 g_free (text);
887 if (ctx->recursive_result != RECURSIVE_ABORT)
888 do_refresh ();
891 switch (ctx->recursive_result)
893 case RECURSIVE_YES:
894 case RECURSIVE_ALWAYS:
895 return FILE_CONT;
897 case RECURSIVE_NO:
898 case RECURSIVE_NEVER:
899 return FILE_SKIP;
901 case RECURSIVE_ABORT:
902 default:
903 return FILE_ABORT;
907 /* --------------------------------------------------------------------------------------------- */
909 #ifdef ENABLE_BACKGROUND
910 static FileProgressStatus
911 do_file_error (const char *str)
913 /* *INDENT-OFF* */
914 union
916 void *p;
917 FileProgressStatus (*f) (enum OperationMode, const char *);
918 } pntr;
919 /* *INDENT-ON* */
921 pntr.f = real_do_file_error;
923 if (mc_global.we_are_background)
924 return parent_call (pntr.p, NULL, 1, strlen (str), str);
925 else
926 return real_do_file_error (Foreground, str);
929 /* --------------------------------------------------------------------------------------------- */
931 static FileProgressStatus
932 query_recursive (file_op_context_t * ctx, const char *s)
934 /* *INDENT-OFF* */
935 union
937 void *p;
938 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *);
939 } pntr;
940 /* *INDENT-ON* */
942 pntr.f = real_query_recursive;
944 if (mc_global.we_are_background)
945 return parent_call (pntr.p, ctx, 1, strlen (s), s);
946 else
947 return real_query_recursive (ctx, Foreground, s);
950 /* --------------------------------------------------------------------------------------------- */
952 static FileProgressStatus
953 query_replace (file_op_context_t * ctx, const char *destname, struct stat *_s_stat,
954 struct stat *_d_stat)
956 /* *INDENT-OFF* */
957 union
959 void *p;
960 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *,
961 struct stat *, struct stat *);
962 } pntr;
963 /* *INDENT-ON* */
965 pntr.f = file_progress_real_query_replace;
967 if (mc_global.we_are_background)
968 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
969 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
970 else
971 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
974 #else
975 /* --------------------------------------------------------------------------------------------- */
977 static FileProgressStatus
978 do_file_error (const char *str)
980 return real_do_file_error (Foreground, str);
983 /* --------------------------------------------------------------------------------------------- */
985 static FileProgressStatus
986 query_recursive (file_op_context_t * ctx, const char *s)
988 return real_query_recursive (ctx, Foreground, s);
991 /* --------------------------------------------------------------------------------------------- */
993 static FileProgressStatus
994 query_replace (file_op_context_t * ctx, const char *destname, struct stat *_s_stat,
995 struct stat *_d_stat)
997 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
1000 #endif /* !ENABLE_BACKGROUND */
1002 /* --------------------------------------------------------------------------------------------- */
1003 /** Report error with two files */
1005 static FileProgressStatus
1006 files_error (const char *format, const char *file1, const char *file2)
1008 char buf[BUF_MEDIUM];
1009 char *nfile1 = g_strdup (path_trunc (file1, 15));
1010 char *nfile2 = g_strdup (path_trunc (file2, 15));
1012 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
1014 g_free (nfile1);
1015 g_free (nfile2);
1017 return do_file_error (buf);
1020 /* }}} */
1022 /* --------------------------------------------------------------------------------------------- */
1024 static void
1025 copy_file_file_display_progress (file_op_total_context_t * tctx, file_op_context_t * ctx,
1026 struct timeval tv_current, struct timeval tv_transfer_start,
1027 off_t file_size, off_t n_read_total)
1029 long dt;
1031 /* 1. Update rotating dash after some time */
1032 rotate_dash (TRUE);
1034 /* 3. Compute ETA */
1035 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
1037 if (n_read_total == 0)
1038 ctx->eta_secs = 0.0;
1039 else
1041 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
1042 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
1045 /* 4. Compute BPS rate */
1046 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
1047 if (ctx->bps_time < 1)
1048 ctx->bps_time = 1;
1049 ctx->bps = n_read_total / ctx->bps_time;
1051 /* 5. Compute total ETA and BPS */
1052 if (ctx->progress_bytes != 0)
1054 uintmax_t remain_bytes;
1056 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
1057 #if 1
1059 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
1061 if (total_secs < 1)
1062 total_secs = 1;
1064 tctx->bps = tctx->copied_bytes / total_secs;
1065 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
1067 #else
1068 /* broken on lot of little files */
1069 tctx->bps_count++;
1070 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
1071 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
1072 #endif
1076 /* --------------------------------------------------------------------------------------------- */
1078 static gboolean
1079 try_remove_file (file_op_context_t * ctx, const vfs_path_t * vpath, FileProgressStatus * status)
1081 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
1083 *status = file_error (_("Cannot remove file \"%s\"\n%s"), vfs_path_as_str (vpath));
1084 if (*status == FILE_RETRY)
1085 continue;
1086 if (*status == FILE_SKIPALL)
1087 ctx->skip_all = TRUE;
1088 return FALSE;
1091 return TRUE;
1094 /* --------------------------------------------------------------------------------------------- */
1096 /* {{{ Move routines */
1099 * Move single file or one of many files from one location to another.
1101 * @panel pointer to panel in case of single file, NULL otherwise
1102 * @tctx file operation total context object
1103 * @ctx file operation context object
1104 * @s source file name
1105 * @d destination file name
1107 * @return operation result
1109 static FileProgressStatus
1110 move_file_file (const WPanel * panel, file_op_total_context_t * tctx, file_op_context_t * ctx,
1111 const char *s, const char *d)
1113 struct stat src_stats, dst_stats;
1114 FileProgressStatus return_status = FILE_CONT;
1115 gboolean copy_done = FALSE;
1116 gboolean old_ask_overwrite;
1117 vfs_path_t *src_vpath, *dst_vpath;
1119 src_vpath = vfs_path_from_str (s);
1120 dst_vpath = vfs_path_from_str (d);
1122 file_progress_show_source (ctx, src_vpath);
1123 file_progress_show_target (ctx, dst_vpath);
1125 /* FIXME: do we really need to check buttons in case of single file? */
1126 if (check_progress_buttons (ctx) == FILE_ABORT)
1128 return_status = FILE_ABORT;
1129 goto ret;
1132 mc_refresh ();
1134 while (mc_lstat (src_vpath, &src_stats) != 0)
1136 /* Source doesn't exist */
1137 if (ctx->skip_all)
1138 return_status = FILE_SKIPALL;
1139 else
1141 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
1142 if (return_status == FILE_SKIPALL)
1143 ctx->skip_all = TRUE;
1146 if (return_status != FILE_RETRY)
1147 goto ret;
1150 if (mc_lstat (dst_vpath, &dst_stats) == 0)
1152 if (check_same_file (s, &src_stats, d, &dst_stats, &return_status))
1153 goto ret;
1155 if (S_ISDIR (dst_stats.st_mode))
1157 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
1158 do_refresh ();
1159 return_status = FILE_SKIP;
1160 goto ret;
1163 if (confirm_overwrite)
1165 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1166 if (return_status != FILE_CONT)
1167 goto ret;
1169 /* Ok to overwrite */
1172 if (!ctx->do_append)
1174 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
1176 return_status = make_symlink (ctx, s, d);
1177 if (return_status == FILE_CONT)
1178 goto retry_src_remove;
1179 goto ret;
1182 if (mc_rename (src_vpath, dst_vpath) == 0)
1184 /* FIXME: do we really need to update progress in case of single file? */
1185 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
1186 goto ret;
1189 #if 0
1190 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1191 one nfs to the same, but on the server it is on two different
1192 filesystems. Then nfs returns EIO instead of EXDEV.
1193 Hope it will not hurt if we always in case of error try to copy/delete. */
1194 else
1195 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1197 if (errno != EXDEV)
1199 if (ctx->skip_all)
1200 return_status = FILE_SKIPALL;
1201 else
1203 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
1204 if (return_status == FILE_SKIPALL)
1205 ctx->skip_all = TRUE;
1206 if (return_status == FILE_RETRY)
1207 goto retry_rename;
1210 goto ret;
1212 #endif
1214 /* Failed rename -> copy the file instead */
1215 if (panel != NULL)
1217 /* In case of single file, calculate totals. In case of many files,
1218 totals are calcuated already. */
1219 return_status =
1220 panel_operate_init_totals (panel, src_vpath, &src_stats, ctx, TRUE,
1221 FILEGUI_DIALOG_ONE_ITEM);
1222 if (return_status != FILE_CONT)
1223 goto ret;
1226 old_ask_overwrite = tctx->ask_overwrite;
1227 tctx->ask_overwrite = FALSE;
1228 return_status = copy_file_file (tctx, ctx, s, d);
1229 tctx->ask_overwrite = old_ask_overwrite;
1230 if (return_status != FILE_CONT)
1231 goto ret;
1233 copy_done = TRUE;
1235 /* FIXME: there is no need to update progress and check buttons
1236 at the finish of single file operation. */
1237 if (panel == NULL)
1239 file_progress_show_source (ctx, NULL);
1240 file_progress_show (ctx, 0, 0, "", FALSE);
1242 return_status = check_progress_buttons (ctx);
1243 if (return_status != FILE_CONT)
1244 goto ret;
1247 mc_refresh ();
1249 retry_src_remove:
1250 if (!try_remove_file (ctx, src_vpath, &return_status) && panel == NULL)
1251 goto ret;
1253 if (!copy_done)
1254 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
1256 ret:
1257 vfs_path_free (src_vpath);
1258 vfs_path_free (dst_vpath);
1260 return return_status;
1263 /* }}} */
1265 /* --------------------------------------------------------------------------------------------- */
1266 /* {{{ Erase routines */
1267 /** Don't update progress status if progress_count==NULL */
1269 static FileProgressStatus
1270 erase_file (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * vpath)
1272 struct stat buf;
1273 FileProgressStatus return_status;
1275 file_progress_show_deleting (ctx, vfs_path_as_str (vpath), &tctx->progress_count);
1276 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1277 if (check_progress_buttons (ctx) == FILE_ABORT)
1278 return FILE_ABORT;
1280 mc_refresh ();
1282 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
1284 /* ignore, most likely the mc_unlink fails, too */
1285 buf.st_size = 0;
1288 if (!try_remove_file (ctx, vpath, &return_status) && return_status == FILE_ABORT)
1289 return FILE_ABORT;
1291 if (tctx->progress_count == 0)
1292 return FILE_CONT;
1294 return check_progress_buttons (ctx);
1297 /* --------------------------------------------------------------------------------------------- */
1299 static FileProgressStatus
1300 try_erase_dir (file_op_context_t * ctx, const char *dir)
1302 FileProgressStatus return_status = FILE_CONT;
1304 while (my_rmdir (dir) != 0 && !ctx->skip_all)
1306 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), dir);
1307 if (return_status == FILE_SKIPALL)
1308 ctx->skip_all = TRUE;
1309 if (return_status != FILE_RETRY)
1310 break;
1313 return return_status;
1316 /* --------------------------------------------------------------------------------------------- */
1319 Recursive remove of files
1320 abort->cancel stack
1321 skip ->warn every level, gets default
1322 skipall->remove as much as possible
1324 static FileProgressStatus
1325 recursive_erase (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * vpath)
1327 struct dirent *next;
1328 DIR *reading;
1329 const char *s;
1330 FileProgressStatus return_status = FILE_CONT;
1332 reading = mc_opendir (vpath);
1333 if (reading == NULL)
1334 return FILE_RETRY;
1336 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1338 vfs_path_t *tmp_vpath;
1339 struct stat buf;
1341 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
1342 continue;
1344 tmp_vpath = vfs_path_append_new (vpath, next->d_name, (char *) NULL);
1345 if (mc_lstat (tmp_vpath, &buf) != 0)
1347 mc_closedir (reading);
1348 vfs_path_free (tmp_vpath);
1349 return FILE_RETRY;
1351 if (S_ISDIR (buf.st_mode))
1352 return_status = recursive_erase (tctx, ctx, tmp_vpath);
1353 else
1354 return_status = erase_file (tctx, ctx, tmp_vpath);
1355 vfs_path_free (tmp_vpath);
1357 mc_closedir (reading);
1359 if (return_status == FILE_ABORT)
1360 return FILE_ABORT;
1362 s = vfs_path_as_str (vpath);
1364 file_progress_show_deleting (ctx, s, NULL);
1365 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1366 if (check_progress_buttons (ctx) == FILE_ABORT)
1367 return FILE_ABORT;
1369 mc_refresh ();
1371 return try_erase_dir (ctx, s);
1374 /* --------------------------------------------------------------------------------------------- */
1375 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1376 in the directory path points to, 0 else. */
1378 static int
1379 check_dir_is_empty (const vfs_path_t * vpath)
1381 DIR *dir;
1382 struct dirent *d;
1383 int i = 1;
1385 dir = mc_opendir (vpath);
1386 if (dir == NULL)
1387 return -1;
1389 for (d = mc_readdir (dir); d != NULL; d = mc_readdir (dir))
1390 if (!DIR_IS_DOT (d->d_name) && !DIR_IS_DOTDOT (d->d_name))
1392 i = 0;
1393 break;
1396 mc_closedir (dir);
1397 return i;
1400 /* --------------------------------------------------------------------------------------------- */
1402 static FileProgressStatus
1403 erase_dir_iff_empty (file_op_context_t * ctx, const vfs_path_t * vpath, size_t count)
1405 const char *s;
1407 s = vfs_path_as_str (vpath);
1409 file_progress_show_deleting (ctx, s, NULL);
1410 file_progress_show_count (ctx, count, ctx->progress_count);
1411 if (check_progress_buttons (ctx) == FILE_ABORT)
1412 return FILE_ABORT;
1414 mc_refresh ();
1416 if (check_dir_is_empty (vpath) != 1)
1417 return FILE_CONT;
1419 /* not empty or error */
1420 return try_erase_dir (ctx, s);
1424 /* --------------------------------------------------------------------------------------------- */
1426 static void
1427 erase_dir_after_copy (file_op_total_context_t * tctx, file_op_context_t * ctx,
1428 const vfs_path_t * vpath, FileProgressStatus * status)
1430 if (ctx->erase_at_end)
1432 /* Reset progress count before delete to avoid counting files twice */
1433 tctx->progress_count = tctx->prev_progress_count;
1435 while (erase_list != NULL && *status != FILE_ABORT)
1437 struct link *lp = (struct link *) erase_list->data;
1439 if (S_ISDIR (lp->st_mode))
1440 *status = erase_dir_iff_empty (ctx, lp->src_vpath, tctx->progress_count);
1441 else
1442 *status = erase_file (tctx, ctx, lp->src_vpath);
1444 erase_list = g_slist_remove (erase_list, lp);
1445 free_link (lp);
1448 /* Save progress counter before move next directory */
1449 tctx->prev_progress_count = tctx->progress_count;
1452 erase_dir_iff_empty (ctx, vpath, tctx->progress_count);
1455 /* }}} */
1457 /* --------------------------------------------------------------------------------------------- */
1460 * Move single directory or one of many directories from one location to another.
1462 * @panel pointer to panel in case of single directory, NULL otherwise
1463 * @tctx file operation total context object
1464 * @ctx file operation context object
1465 * @s source directory name
1466 * @d destination directory name
1468 * @return operation result
1470 static FileProgressStatus
1471 do_move_dir_dir (const WPanel * panel, file_op_total_context_t * tctx, file_op_context_t * ctx,
1472 const char *s, const char *d)
1474 struct stat sbuf, dbuf;
1475 FileProgressStatus return_status = FILE_CONT;
1476 gboolean move_over = FALSE;
1477 gboolean dstat_ok;
1478 vfs_path_t *src_vpath, *dst_vpath;
1480 src_vpath = vfs_path_from_str (s);
1481 dst_vpath = vfs_path_from_str (d);
1483 file_progress_show_source (ctx, src_vpath);
1484 file_progress_show_target (ctx, dst_vpath);
1486 /* FIXME: do we really need to check buttons in case of single directory? */
1487 if (panel != NULL && check_progress_buttons (ctx) == FILE_ABORT)
1489 return_status = FILE_ABORT;
1490 goto ret_fast;
1493 mc_refresh ();
1495 mc_stat (src_vpath, &sbuf);
1497 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
1499 if (dstat_ok && check_same_file (s, &sbuf, d, &dbuf, &return_status))
1500 goto ret_fast;
1502 if (!dstat_ok)
1503 ; /* destination doesn't exist */
1504 else if (!ctx->dive_into_subdirs)
1505 move_over = TRUE;
1506 else
1508 vfs_path_t *tmp;
1510 tmp = dst_vpath;
1511 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
1512 vfs_path_free (tmp);
1515 d = vfs_path_as_str (dst_vpath);
1517 /* Check if the user inputted an existing dir */
1518 retry_dst_stat:
1519 if (mc_stat (dst_vpath, &dbuf) == 0)
1521 if (move_over)
1523 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, TRUE, TRUE, NULL);
1525 if (return_status != FILE_CONT)
1526 goto ret;
1527 goto oktoret;
1529 else if (ctx->skip_all)
1530 return_status = FILE_SKIPALL;
1531 else
1533 if (S_ISDIR (dbuf.st_mode))
1534 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), d);
1535 else
1536 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), d);
1537 if (return_status == FILE_SKIPALL)
1538 ctx->skip_all = TRUE;
1539 if (return_status == FILE_RETRY)
1540 goto retry_dst_stat;
1543 goto ret_fast;
1546 retry_rename:
1547 if (mc_rename (src_vpath, dst_vpath) == 0)
1549 return_status = FILE_CONT;
1550 goto ret;
1553 if (errno != EXDEV)
1555 if (!ctx->skip_all)
1557 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
1558 if (return_status == FILE_SKIPALL)
1559 ctx->skip_all = TRUE;
1560 if (return_status == FILE_RETRY)
1561 goto retry_rename;
1563 goto ret;
1566 /* Failed because of filesystem boundary -> copy dir instead */
1567 if (panel != NULL)
1569 /* In case of single directory, calculate totals. In case of many directories,
1570 totals are calcuated already. */
1571 return_status =
1572 panel_operate_init_totals (panel, src_vpath, &sbuf, ctx, FALSE,
1573 FILEGUI_DIALOG_ONE_ITEM);
1574 if (return_status != FILE_CONT)
1575 goto ret;
1578 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, FALSE, TRUE, NULL);
1580 if (return_status != FILE_CONT)
1581 goto ret;
1583 oktoret:
1584 /* FIXME: there is no need to update progress and check buttons
1585 at the finish of single directory operation. */
1586 if (panel == NULL)
1588 file_progress_show_source (ctx, NULL);
1589 file_progress_show_target (ctx, NULL);
1590 file_progress_show (ctx, 0, 0, "", FALSE);
1592 return_status = check_progress_buttons (ctx);
1593 if (return_status != FILE_CONT)
1594 goto ret;
1597 mc_refresh ();
1599 erase_dir_after_copy (tctx, ctx, src_vpath, &return_status);
1601 ret:
1602 erase_list = free_linklist (erase_list);
1603 ret_fast:
1604 vfs_path_free (src_vpath);
1605 vfs_path_free (dst_vpath);
1606 return return_status;
1609 /* --------------------------------------------------------------------------------------------- */
1611 /* {{{ Panel operate routines */
1614 * Return currently selected entry name or the name of the first marked
1615 * entry if there is one.
1618 static const char *
1619 panel_get_file (const WPanel * panel)
1621 if (get_current_type () == view_tree)
1623 WTree *tree;
1624 const vfs_path_t *selected_name;
1626 tree = (WTree *) get_panel_widget (get_current_index ());
1627 selected_name = tree_selected_name (tree);
1628 return vfs_path_as_str (selected_name);
1631 if (panel->marked != 0)
1633 int i;
1635 for (i = 0; i < panel->dir.len; i++)
1636 if (panel->dir.list[i].f.marked)
1637 return panel->dir.list[i].fname;
1640 return panel->dir.list[panel->selected].fname;
1643 /* --------------------------------------------------------------------------------------------- */
1645 static const char *
1646 check_single_entry (const WPanel * panel, gboolean force_single, struct stat *src_stat)
1648 const char *source;
1649 gboolean ok;
1651 if (force_single)
1652 source = selection (panel)->fname;
1653 else
1654 source = panel_get_file (panel);
1656 ok = !DIR_IS_DOTDOT (source);
1658 if (!ok)
1659 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
1660 else
1662 vfs_path_t *source_vpath;
1664 source_vpath = vfs_path_from_str (source);
1666 /* Update stat to get actual info */
1667 ok = mc_lstat (source_vpath, src_stat) == 0;
1668 if (!ok)
1670 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
1671 path_trunc (source, 30), unix_error_string (errno));
1673 /* Directory was changed outside MC. Reload it forced */
1674 if (!panel->is_panelized)
1676 panel_update_flags_t flags = UP_RELOAD;
1678 /* don't update panelized panel */
1679 if (get_other_type () == view_listing && other_panel->is_panelized)
1680 flags |= UP_ONLY_CURRENT;
1682 update_panels (flags, UP_KEEPSEL);
1686 vfs_path_free (source_vpath);
1689 return ok ? source : NULL;
1692 /* --------------------------------------------------------------------------------------------- */
1694 * Generate user prompt for panel operation.
1695 * src_stat must be not NULL for single source, and NULL for multiple sources
1698 static char *
1699 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1700 const struct stat *src_stat)
1702 char *sp;
1703 char *format_string;
1704 const char *cp;
1706 static gboolean i18n_flag = FALSE;
1707 if (!i18n_flag)
1709 size_t i;
1711 for (i = G_N_ELEMENTS (op_names1); i-- != 0;)
1712 op_names1[i] = Q_ (op_names1[i]);
1714 #ifdef ENABLE_NLS
1715 for (i = G_N_ELEMENTS (prompt_parts); i-- != 0;)
1716 prompt_parts[i] = _(prompt_parts[i]);
1718 one_format = _(one_format);
1719 many_format = _(many_format);
1720 #endif /* ENABLE_NLS */
1721 i18n_flag = TRUE;
1724 /* Possible prompts:
1725 * OP_COPY:
1726 * "Copy file \"%s\" with source mask:"
1727 * "Copy %d files with source mask:"
1728 * "Copy directory \"%s\" with source mask:"
1729 * "Copy %d directories with source mask:"
1730 * "Copy %d files/directories with source mask:"
1731 * OP_MOVE:
1732 * "Move file \"%s\" with source mask:"
1733 * "Move %d files with source mask:"
1734 * "Move directory \"%s\" with source mask:"
1735 * "Move %d directories with source mask:"
1736 * "Move %d files/directories with source mask:"
1737 * OP_DELETE:
1738 * "Delete file \"%s\"?"
1739 * "Delete %d files?"
1740 * "Delete directory \"%s\"?"
1741 * "Delete %d directories?"
1742 * "Delete %d files/directories?"
1745 cp = (src_stat != NULL ? one_format : many_format);
1747 /* 1. Substitute %o */
1748 format_string = str_replace_all (cp, "%o", op_names1[(int) operation]);
1750 /* 2. Substitute %n */
1751 cp = operation == OP_DELETE ? "\n" : " ";
1752 sp = format_string;
1753 format_string = str_replace_all (sp, "%n", cp);
1754 g_free (sp);
1756 /* 3. Substitute %f */
1757 if (src_stat != NULL)
1758 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1759 else if (panel->marked == panel->dirs_marked)
1760 cp = prompt_parts[3];
1761 else
1762 cp = panel->dirs_marked != 0 ? prompt_parts[4] : prompt_parts[1];
1764 sp = format_string;
1765 format_string = str_replace_all (sp, "%f", cp);
1766 g_free (sp);
1768 /* 4. Substitute %m */
1769 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1770 sp = format_string;
1771 format_string = str_replace_all (sp, "%m", cp);
1772 g_free (sp);
1774 return format_string;
1777 /* --------------------------------------------------------------------------------------------- */
1779 static char *
1780 do_confirm_copy_move (const WPanel * panel, FileOperation operation, gboolean force_single,
1781 const char *source, struct stat *src_stat, file_op_context_t * ctx,
1782 gboolean * do_bg)
1784 const char *tmp_dest_dir;
1785 char *dest_dir;
1786 char *format;
1787 char *ret;
1789 /* Forced single operations default to the original name */
1790 if (force_single)
1791 tmp_dest_dir = source;
1792 else if (get_other_type () == view_listing)
1793 tmp_dest_dir = vfs_path_as_str (other_panel->cwd_vpath);
1794 else
1795 tmp_dest_dir = vfs_path_as_str (panel->cwd_vpath);
1798 * Add trailing backslash only when do non-local ops.
1799 * It saves user from occasional file renames (when destination
1800 * dir is deleted)
1802 if (!force_single && tmp_dest_dir != NULL && tmp_dest_dir[0] != '\0'
1803 && !IS_PATH_SEP (tmp_dest_dir[strlen (tmp_dest_dir) - 1]))
1805 /* add trailing separator */
1806 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
1808 else
1810 /* just copy */
1811 dest_dir = g_strdup (tmp_dest_dir);
1814 if (dest_dir == NULL)
1815 return NULL;
1817 if (source == NULL)
1818 src_stat = NULL;
1820 /* Generate confirmation prompt */
1821 format = panel_operate_generate_prompt (panel, operation, src_stat);
1823 ret = file_mask_dialog (ctx, operation, source != NULL, format,
1824 source != NULL ? source : (const void *) &panel->marked, dest_dir,
1825 do_bg);
1827 g_free (format);
1828 g_free (dest_dir);
1830 return ret;
1833 /* --------------------------------------------------------------------------------------------- */
1835 static gboolean
1836 do_confirm_erase (const WPanel * panel, const char *source, struct stat *src_stat)
1838 int i;
1839 char *format;
1840 char fmd_buf[BUF_MEDIUM];
1842 if (source == NULL)
1843 src_stat = NULL;
1845 /* Generate confirmation prompt */
1846 format = panel_operate_generate_prompt (panel, OP_DELETE, src_stat);
1848 if (source == NULL)
1849 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
1850 else
1852 const int fmd_xlen = 64;
1854 i = fmd_xlen - str_term_width1 (format) - 4;
1855 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
1858 g_free (format);
1860 if (safe_delete)
1861 query_set_sel (1);
1863 i = query_dialog (op_names[OP_DELETE], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
1865 return (i == 0);
1868 /* --------------------------------------------------------------------------------------------- */
1870 static FileProgressStatus
1871 operate_single_file (const WPanel * panel, FileOperation operation, file_op_total_context_t * tctx,
1872 file_op_context_t * ctx, const char *src, struct stat *src_stat,
1873 const char *dest, filegui_dialog_type_t dialog_type)
1875 FileProgressStatus value;
1876 vfs_path_t *src_vpath;
1877 gboolean is_file;
1879 if (g_path_is_absolute (src))
1880 src_vpath = vfs_path_from_str (src);
1881 else
1882 src_vpath = vfs_path_append_new (panel->cwd_vpath, src, (char *) NULL);
1884 is_file = !S_ISDIR (src_stat->st_mode);
1885 /* Is link to directory? */
1886 if (is_file)
1888 gboolean is_link;
1890 is_link = file_is_symlink_to_dir (src_vpath, src_stat, NULL);
1891 is_file = !(is_link && ctx->follow_links);
1895 if (operation == OP_DELETE)
1897 value = panel_operate_init_totals (panel, src_vpath, src_stat, ctx, !is_file, dialog_type);
1898 if (value == FILE_CONT)
1900 if (is_file)
1901 value = erase_file (tctx, ctx, src_vpath);
1902 else
1903 value = erase_dir (tctx, ctx, src_vpath);
1906 else
1908 char *temp;
1910 src = vfs_path_as_str (src_vpath);
1912 temp = build_dest (ctx, src, dest, &value);
1913 if (temp != NULL)
1915 dest = temp;
1917 switch (operation)
1919 case OP_COPY:
1920 /* we use file_mask_op_follow_links only with OP_COPY */
1921 ctx->stat_func (src_vpath, src_stat);
1923 value =
1924 panel_operate_init_totals (panel, src_vpath, src_stat, ctx, !is_file,
1925 dialog_type);
1926 if (value == FILE_CONT)
1928 is_file = !S_ISDIR (src_stat->st_mode);
1929 /* Is link to directory? */
1930 if (is_file)
1932 gboolean is_link;
1934 is_link = file_is_symlink_to_dir (src_vpath, src_stat, NULL);
1935 is_file = !(is_link && ctx->follow_links);
1938 if (is_file)
1939 value = copy_file_file (tctx, ctx, src, dest);
1940 else
1941 value = copy_dir_dir (tctx, ctx, src, dest, TRUE, FALSE, FALSE, NULL);
1943 break;
1945 case OP_MOVE:
1946 #ifdef ENABLE_BACKGROUND
1947 /* create UI to show confirmation dialog */
1948 if (!mc_global.we_are_background)
1949 file_op_context_create_ui (ctx, TRUE, FILEGUI_DIALOG_ONE_ITEM);
1950 #endif
1951 if (is_file)
1952 value = move_file_file (panel, tctx, ctx, src, dest);
1953 else
1954 value = do_move_dir_dir (panel, tctx, ctx, src, dest);
1955 break;
1957 default:
1958 /* Unknown file operation */
1959 abort ();
1962 g_free (temp);
1966 vfs_path_free (src_vpath);
1968 return value;
1971 /* --------------------------------------------------------------------------------------------- */
1973 static FileProgressStatus
1974 operate_one_file (const WPanel * panel, FileOperation operation, file_op_total_context_t * tctx,
1975 file_op_context_t * ctx, const char *src, struct stat *src_stat, const char *dest)
1977 FileProgressStatus value = FILE_CONT;
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);
1988 if (operation == OP_DELETE)
1990 if (is_file)
1991 value = erase_file (tctx, ctx, src_vpath);
1992 else
1993 value = erase_dir (tctx, ctx, src_vpath);
1995 else
1997 char *temp;
1999 src = vfs_path_as_str (src_vpath);
2001 temp = build_dest (ctx, src, dest, &value);
2002 if (temp != NULL)
2004 dest = temp;
2006 switch (operation)
2008 case OP_COPY:
2009 /* we use file_mask_op_follow_links only with OP_COPY */
2010 ctx->stat_func (src_vpath, src_stat);
2011 is_file = !S_ISDIR (src_stat->st_mode);
2013 if (is_file)
2014 value = copy_file_file (tctx, ctx, src, dest);
2015 else
2016 value = copy_dir_dir (tctx, ctx, src, dest, TRUE, FALSE, FALSE, NULL);
2017 dest_dirs = free_linklist (dest_dirs);
2018 break;
2020 case OP_MOVE:
2021 if (is_file)
2022 value = move_file_file (NULL, tctx, ctx, src, dest);
2023 else
2024 value = do_move_dir_dir (NULL, tctx, ctx, src, dest);
2025 break;
2027 default:
2028 /* Unknown file operation */
2029 abort ();
2032 g_free (temp);
2036 vfs_path_free (src_vpath);
2038 return value;
2041 /* --------------------------------------------------------------------------------------------- */
2043 #ifdef ENABLE_BACKGROUND
2044 static int
2045 end_bg_process (file_op_context_t * ctx, enum OperationMode mode)
2047 int pid = ctx->pid;
2049 (void) mode;
2050 ctx->pid = 0;
2052 unregister_task_with_pid (pid);
2053 /* file_op_context_destroy(ctx); */
2054 return 1;
2056 #endif
2057 /* }}} */
2059 /* --------------------------------------------------------------------------------------------- */
2060 /*** public functions ****************************************************************************/
2061 /* --------------------------------------------------------------------------------------------- */
2063 /* Is file symlink to directory or not.
2065 * @param path file or directory
2066 * @param st result of mc_lstat(vpath). If NULL, mc_lstat(vpath) is performed here
2067 * @param stale_link TRUE if file is stale link to directory
2069 * @return TRUE if file symlink to directory, ELSE otherwise.
2071 gboolean
2072 file_is_symlink_to_dir (const vfs_path_t * vpath, struct stat * st, gboolean * stale_link)
2074 struct stat st2;
2075 gboolean stale = FALSE;
2076 gboolean res = FALSE;
2078 if (st == NULL)
2080 st = &st2;
2082 if (mc_lstat (vpath, st) != 0)
2083 goto ret;
2086 if (S_ISLNK (st->st_mode))
2088 struct stat st3;
2090 stale = (mc_stat (vpath, &st3) != 0);
2092 if (!stale)
2093 res = (S_ISDIR (st3.st_mode) != 0);
2096 ret:
2097 if (stale_link != NULL)
2098 *stale_link = stale;
2100 return res;
2103 /* --------------------------------------------------------------------------------------------- */
2105 FileProgressStatus
2106 copy_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx,
2107 const char *src_path, const char *dst_path)
2109 uid_t src_uid = (uid_t) (-1);
2110 gid_t src_gid = (gid_t) (-1);
2112 int src_desc, dest_desc = -1;
2113 mode_t src_mode = 0; /* The mode of the source file */
2114 struct stat src_stat, dst_stat;
2115 mc_timesbuf_t times;
2116 gboolean dst_exists = FALSE, appending = FALSE;
2117 off_t file_size = -1;
2118 FileProgressStatus return_status, temp_status;
2119 struct timeval tv_transfer_start;
2120 dest_status_t dst_status = DEST_NONE;
2121 int open_flags;
2122 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
2123 char *buf = NULL;
2125 /* FIXME: We should not be using global variables! */
2126 ctx->do_reget = 0;
2127 return_status = FILE_RETRY;
2129 dst_vpath = vfs_path_from_str (dst_path);
2130 src_vpath = vfs_path_from_str (src_path);
2132 file_progress_show_source (ctx, src_vpath);
2133 file_progress_show_target (ctx, dst_vpath);
2135 if (check_progress_buttons (ctx) == FILE_ABORT)
2137 return_status = FILE_ABORT;
2138 goto ret_fast;
2141 mc_refresh ();
2143 while (mc_stat (dst_vpath, &dst_stat) == 0)
2145 if (S_ISDIR (dst_stat.st_mode))
2147 if (ctx->skip_all)
2148 return_status = FILE_SKIPALL;
2149 else
2151 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
2152 if (return_status == FILE_SKIPALL)
2153 ctx->skip_all = TRUE;
2154 if (return_status == FILE_RETRY)
2155 continue;
2157 goto ret_fast;
2160 dst_exists = TRUE;
2161 break;
2164 while ((*ctx->stat_func) (src_vpath, &src_stat) != 0)
2166 if (ctx->skip_all)
2167 return_status = FILE_SKIPALL;
2168 else
2170 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
2171 if (return_status == FILE_SKIPALL)
2172 ctx->skip_all = TRUE;
2175 if (return_status != FILE_RETRY)
2176 goto ret_fast;
2179 if (dst_exists)
2181 /* Destination already exists */
2182 if (check_same_file (src_path, &src_stat, dst_path, &dst_stat, &return_status))
2183 goto ret_fast;
2185 /* Should we replace destination? */
2186 if (tctx->ask_overwrite)
2188 ctx->do_reget = 0;
2189 return_status = query_replace (ctx, dst_path, &src_stat, &dst_stat);
2190 if (return_status != FILE_CONT)
2191 goto ret_fast;
2195 if (!ctx->do_append)
2197 /* Check the hardlinks */
2198 if (!ctx->follow_links && check_hardlinks (src_vpath, &src_stat, dst_vpath))
2200 /* We have made a hardlink - no more processing is necessary */
2201 return_status = FILE_CONT;
2202 goto ret_fast;
2205 if (S_ISLNK (src_stat.st_mode))
2207 return_status = make_symlink (ctx, src_path, dst_path);
2208 goto ret_fast;
2211 if (S_ISCHR (src_stat.st_mode) || S_ISBLK (src_stat.st_mode) || S_ISFIFO (src_stat.st_mode)
2212 || S_ISNAM (src_stat.st_mode) || S_ISSOCK (src_stat.st_mode))
2214 dev_t rdev = 0;
2216 #ifdef HAVE_STRUCT_STAT_ST_RDEV
2217 rdev = src_stat.st_rdev;
2218 #endif
2220 while (mc_mknod (dst_vpath, src_stat.st_mode & ctx->umask_kill, rdev) < 0
2221 && !ctx->skip_all)
2223 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
2224 if (return_status == FILE_RETRY)
2225 continue;
2226 if (return_status == FILE_SKIPALL)
2227 ctx->skip_all = TRUE;
2228 goto ret_fast;
2230 /* Success */
2232 while (ctx->preserve_uidgid
2233 && mc_chown (dst_vpath, src_stat.st_uid, src_stat.st_gid) != 0 && !ctx->skip_all)
2235 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
2236 if (temp_status == FILE_SKIP)
2237 break;
2238 if (temp_status == FILE_SKIPALL)
2239 ctx->skip_all = TRUE;
2240 if (temp_status != FILE_RETRY)
2242 return_status = temp_status;
2243 goto ret_fast;
2247 while (ctx->preserve && mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill) != 0
2248 && !ctx->skip_all)
2250 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
2251 if (temp_status == FILE_SKIP)
2252 break;
2253 if (temp_status == FILE_SKIPALL)
2254 ctx->skip_all = TRUE;
2255 if (temp_status != FILE_RETRY)
2257 return_status = temp_status;
2258 goto ret_fast;
2262 return_status = FILE_CONT;
2263 goto ret_fast;
2267 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
2269 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
2271 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
2272 if (return_status == FILE_RETRY)
2273 continue;
2274 if (return_status == FILE_SKIPALL)
2275 ctx->skip_all = TRUE;
2276 if (return_status == FILE_SKIP)
2277 break;
2278 ctx->do_append = FALSE;
2279 goto ret_fast;
2282 if (ctx->do_reget != 0)
2284 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
2286 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
2287 ctx->do_reget = 0;
2288 ctx->do_append = FALSE;
2292 while (mc_fstat (src_desc, &src_stat) != 0)
2294 if (ctx->skip_all)
2295 return_status = FILE_SKIPALL;
2296 else
2298 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
2299 if (return_status == FILE_RETRY)
2300 continue;
2301 if (return_status == FILE_SKIPALL)
2302 ctx->skip_all = TRUE;
2303 ctx->do_append = FALSE;
2305 goto ret;
2308 src_mode = src_stat.st_mode;
2309 src_uid = src_stat.st_uid;
2310 src_gid = src_stat.st_gid;
2311 get_times (&src_stat, &times);
2312 file_size = src_stat.st_size;
2314 open_flags = O_WRONLY;
2315 if (dst_exists)
2317 if (ctx->do_append)
2318 open_flags |= O_APPEND;
2319 else
2320 open_flags |= O_CREAT | O_TRUNC;
2322 else
2324 open_flags |= O_CREAT | O_EXCL;
2327 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
2329 if (errno != EEXIST)
2331 if (ctx->skip_all)
2332 return_status = FILE_SKIPALL;
2333 else
2335 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
2336 if (return_status == FILE_RETRY)
2337 continue;
2338 if (return_status == FILE_SKIPALL)
2339 ctx->skip_all = TRUE;
2340 ctx->do_append = FALSE;
2343 goto ret;
2345 dst_status = DEST_SHORT; /* file opened, but not fully copied */
2347 appending = ctx->do_append;
2348 ctx->do_append = FALSE;
2350 /* Try clone the file first. */
2351 if (vfs_clone_file (dest_desc, src_desc) == 0)
2353 dst_status = DEST_FULL;
2354 return_status = FILE_CONT;
2355 goto ret;
2358 /* Find out the optimal buffer size. */
2359 while (mc_fstat (dest_desc, &dst_stat) != 0)
2361 if (ctx->skip_all)
2362 return_status = FILE_SKIPALL;
2363 else
2365 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
2366 if (return_status == FILE_RETRY)
2367 continue;
2368 if (return_status == FILE_SKIPALL)
2369 ctx->skip_all = TRUE;
2371 goto ret;
2374 /* try preallocate space; if fail, try copy anyway */
2375 while (mc_global.vfs.preallocate_space &&
2376 vfs_preallocate (dest_desc, file_size, appending ? dst_stat.st_size : 0) != 0)
2378 if (ctx->skip_all)
2380 /* cannot allocate, start the file copying anyway */
2381 return_status = FILE_CONT;
2382 break;
2385 return_status =
2386 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
2388 if (return_status == FILE_SKIPALL)
2389 ctx->skip_all = TRUE;
2391 if (ctx->skip_all || return_status == FILE_SKIP)
2393 /* skip the space allocation error, start file copying */
2394 return_status = FILE_CONT;
2395 break;
2398 if (return_status == FILE_ABORT)
2400 mc_close (dest_desc);
2401 dest_desc = -1;
2402 mc_unlink (dst_vpath);
2403 dst_status = DEST_NONE;
2404 goto ret;
2407 /* return_status == FILE_RETRY -- try allocate space again */
2410 ctx->eta_secs = 0.0;
2411 ctx->bps = 0;
2413 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
2414 file_progress_show (ctx, 0, file_size, "", TRUE);
2415 else
2416 file_progress_show (ctx, 1, 1, "", TRUE);
2417 return_status = check_progress_buttons (ctx);
2418 mc_refresh ();
2420 if (return_status == FILE_CONT)
2422 size_t bufsize;
2423 off_t n_read_total = 0;
2424 struct timeval tv_current, tv_last_update, tv_last_input;
2425 int secs, update_secs;
2426 const char *stalled_msg = "";
2427 gboolean is_first_time = TRUE;
2429 tv_last_update = tv_transfer_start;
2431 bufsize = io_blksize (dst_stat);
2432 buf = g_malloc (bufsize);
2434 while (TRUE)
2436 ssize_t n_read = -1, n_written;
2438 /* src_read */
2439 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0) == 0)
2440 while ((n_read = mc_read (src_desc, buf, bufsize)) < 0 && !ctx->skip_all)
2442 return_status = file_error (_("Cannot read source file \"%s\"\n%s"), src_path);
2443 if (return_status == FILE_RETRY)
2444 continue;
2445 if (return_status == FILE_SKIPALL)
2446 ctx->skip_all = TRUE;
2447 goto ret;
2450 if (n_read == 0)
2451 break;
2453 gettimeofday (&tv_current, NULL);
2455 if (n_read > 0)
2457 char *t = buf;
2459 n_read_total += n_read;
2461 /* Windows NT ftp servers report that files have no
2462 * permissions: -------, so if we happen to have actually
2463 * read something, we should fix the permissions.
2465 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
2466 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
2467 gettimeofday (&tv_last_input, NULL);
2469 /* dst_write */
2470 while ((n_written = mc_write (dest_desc, t, (size_t) n_read)) < n_read)
2472 gboolean write_errno_nospace;
2474 if (n_written > 0)
2476 n_read -= n_written;
2477 t += n_written;
2478 continue;
2481 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
2483 if (ctx->skip_all)
2484 return_status = FILE_SKIPALL;
2485 else
2486 return_status =
2487 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
2489 if (return_status == FILE_SKIP)
2491 if (write_errno_nospace)
2492 goto ret;
2493 break;
2495 if (return_status == FILE_SKIPALL)
2497 ctx->skip_all = TRUE;
2498 if (write_errno_nospace)
2499 goto ret;
2501 if (return_status != FILE_RETRY)
2502 goto ret;
2506 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
2508 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
2509 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
2511 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
2513 copy_file_file_display_progress (tctx, ctx,
2514 tv_current,
2515 tv_transfer_start, file_size, n_read_total);
2516 tv_last_update = tv_current;
2518 is_first_time = FALSE;
2520 if (update_secs > FILEOP_STALLING_INTERVAL)
2522 stalled_msg = _("(stalled)");
2526 gboolean force_update;
2528 force_update =
2529 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
2531 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2533 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2534 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
2537 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
2538 force_update);
2540 mc_refresh ();
2542 return_status = check_progress_buttons (ctx);
2544 if (return_status != FILE_CONT)
2546 mc_refresh ();
2547 goto ret;
2551 dst_status = DEST_FULL; /* copy successful, don't remove target file */
2554 ret:
2555 g_free (buf);
2557 rotate_dash (FALSE);
2558 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
2560 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
2561 if (temp_status == FILE_RETRY)
2562 continue;
2563 if (temp_status == FILE_ABORT)
2564 return_status = temp_status;
2565 if (temp_status == FILE_SKIPALL)
2566 ctx->skip_all = TRUE;
2567 break;
2570 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
2572 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
2573 if (temp_status == FILE_RETRY)
2574 continue;
2575 if (temp_status == FILE_SKIPALL)
2576 ctx->skip_all = TRUE;
2577 return_status = temp_status;
2578 break;
2581 if (dst_status == DEST_SHORT)
2583 /* Query to remove short file */
2584 if (query_dialog (Q_ ("DialogTitle|Copy"), _("Incomplete file was retrieved. Keep it?"),
2585 D_ERROR, 2, _("&Delete"), _("&Keep")) == 0)
2586 mc_unlink (dst_vpath);
2588 else if (dst_status == DEST_FULL)
2590 /* Copy has succeeded */
2591 if (!appending && ctx->preserve_uidgid)
2593 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
2595 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
2596 if (temp_status == FILE_RETRY)
2597 continue;
2598 if (temp_status == FILE_SKIPALL)
2600 ctx->skip_all = TRUE;
2601 return_status = FILE_CONT;
2603 if (temp_status == FILE_SKIP)
2604 return_status = FILE_CONT;
2605 break;
2609 if (!appending)
2611 if (ctx->preserve)
2613 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
2615 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
2616 if (temp_status == FILE_RETRY)
2617 continue;
2618 if (temp_status == FILE_SKIPALL)
2620 ctx->skip_all = TRUE;
2621 return_status = FILE_CONT;
2623 if (temp_status == FILE_SKIP)
2624 return_status = FILE_CONT;
2625 break;
2628 else if (!dst_exists)
2630 src_mode = umask (-1);
2631 umask (src_mode);
2632 src_mode = 0100666 & ~src_mode;
2633 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
2635 mc_utime (dst_vpath, &times);
2639 if (return_status == FILE_CONT)
2640 return_status = progress_update_one (tctx, ctx, file_size);
2642 ret_fast:
2643 vfs_path_free (src_vpath);
2644 vfs_path_free (dst_vpath);
2645 return return_status;
2648 /* --------------------------------------------------------------------------------------------- */
2650 * I think these copy_*_* functions should have a return type.
2651 * anyway, this function *must* have two directories as arguments.
2653 /* FIXME: This function needs to check the return values of the
2654 function calls */
2656 FileProgressStatus
2657 copy_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d,
2658 gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs)
2660 struct dirent *next;
2661 struct stat buf, cbuf;
2662 DIR *reading;
2663 FileProgressStatus return_status = FILE_CONT;
2664 struct link *lp;
2665 vfs_path_t *src_vpath, *dst_vpath;
2666 gboolean do_mkdir = TRUE;
2668 src_vpath = vfs_path_from_str (s);
2669 dst_vpath = vfs_path_from_str (d);
2671 /* First get the mode of the source dir */
2673 retry_src_stat:
2674 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
2676 if (ctx->skip_all)
2677 return_status = FILE_SKIPALL;
2678 else
2680 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
2681 if (return_status == FILE_RETRY)
2682 goto retry_src_stat;
2683 if (return_status == FILE_SKIPALL)
2684 ctx->skip_all = TRUE;
2686 goto ret_fast;
2689 if (is_in_linklist (dest_dirs, src_vpath, &cbuf))
2691 /* Don't copy a directory we created before (we don't want to copy
2692 infinitely if a directory is copied into itself) */
2693 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2694 return_status = FILE_CONT;
2695 goto ret_fast;
2698 /* Hmm, hardlink to directory??? - Norbert */
2699 /* FIXME: In this step we should do something in case the destination already exist */
2700 /* Check the hardlinks */
2701 if (ctx->preserve && check_hardlinks (src_vpath, &cbuf, dst_vpath))
2703 /* We have made a hardlink - no more processing is necessary */
2704 goto ret_fast;
2707 if (!S_ISDIR (cbuf.st_mode))
2709 if (ctx->skip_all)
2710 return_status = FILE_SKIPALL;
2711 else
2713 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
2714 if (return_status == FILE_RETRY)
2715 goto retry_src_stat;
2716 if (return_status == FILE_SKIPALL)
2717 ctx->skip_all = TRUE;
2719 goto ret_fast;
2722 if (is_in_linklist (parent_dirs, src_vpath, &cbuf))
2724 /* we found a cyclic symbolic link */
2725 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
2726 return_status = FILE_SKIP;
2727 goto ret_fast;
2730 lp = g_new0 (struct link, 1);
2731 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
2732 lp->ino = cbuf.st_ino;
2733 lp->dev = cbuf.st_dev;
2734 parent_dirs = g_slist_prepend (parent_dirs, lp);
2736 retry_dst_stat:
2737 /* Now, check if the dest dir exists, if not, create it. */
2738 if (mc_stat (dst_vpath, &buf) != 0)
2740 /* Here the dir doesn't exist : make it ! */
2741 if (move_over && mc_rename (src_vpath, dst_vpath) == 0)
2743 return_status = FILE_CONT;
2744 goto ret;
2747 else
2750 * If the destination directory exists, we want to copy the whole
2751 * directory, but we only want this to happen once.
2753 * Escape sequences added to the * to compiler warnings.
2754 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2755 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2757 if (!S_ISDIR (buf.st_mode))
2759 if (ctx->skip_all)
2760 return_status = FILE_SKIPALL;
2761 else
2763 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2764 if (return_status == FILE_SKIPALL)
2765 ctx->skip_all = TRUE;
2766 if (return_status == FILE_RETRY)
2767 goto retry_dst_stat;
2769 goto ret;
2771 /* Dive into subdir if exists */
2772 if (toplevel && ctx->dive_into_subdirs)
2774 vfs_path_t *tmp;
2776 tmp = dst_vpath;
2777 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
2778 vfs_path_free (tmp);
2781 else
2782 do_mkdir = FALSE;
2785 d = vfs_path_as_str (dst_vpath);
2787 if (do_mkdir)
2789 while (my_mkdir (dst_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2791 if (ctx->skip_all)
2792 return_status = FILE_SKIPALL;
2793 else
2795 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), d);
2796 if (return_status == FILE_SKIPALL)
2797 ctx->skip_all = TRUE;
2799 if (return_status != FILE_RETRY)
2800 goto ret;
2803 lp = g_new0 (struct link, 1);
2804 mc_stat (dst_vpath, &buf);
2805 lp->vfs = vfs_path_get_by_index (dst_vpath, -1)->class;
2806 lp->ino = buf.st_ino;
2807 lp->dev = buf.st_dev;
2808 dest_dirs = g_slist_prepend (dest_dirs, lp);
2811 if (ctx->preserve_uidgid)
2813 while (mc_chown (dst_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2815 if (ctx->skip_all)
2816 return_status = FILE_SKIPALL;
2817 else
2819 return_status = file_error (_("Cannot chown target directory \"%s\"\n%s"), d);
2820 if (return_status == FILE_SKIPALL)
2821 ctx->skip_all = TRUE;
2823 if (return_status != FILE_RETRY)
2824 goto ret;
2828 /* open the source dir for reading */
2829 reading = mc_opendir (src_vpath);
2830 if (reading == NULL)
2831 goto ret;
2833 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2835 char *path;
2836 vfs_path_t *tmp_vpath;
2839 * Now, we don't want '.' and '..' to be created / copied at any time
2841 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
2842 continue;
2844 /* get the filename and add it to the src directory */
2845 path = mc_build_filename (s, next->d_name, (char *) NULL);
2846 tmp_vpath = vfs_path_from_str (path);
2848 (*ctx->stat_func) (tmp_vpath, &buf);
2849 if (S_ISDIR (buf.st_mode))
2851 char *mdpath;
2853 mdpath = mc_build_filename (d, next->d_name, (char *) NULL);
2855 * From here, we just intend to recursively copy subdirs, not
2856 * the double functionality of copying different when the target
2857 * dir already exists. So, we give the recursive call the flag 0
2858 * meaning no toplevel.
2860 return_status =
2861 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2862 g_free (mdpath);
2864 else
2866 char *dest_file;
2868 dest_file = mc_build_filename (d, x_basename (path), (char *) NULL);
2869 return_status = copy_file_file (tctx, ctx, path, dest_file);
2870 g_free (dest_file);
2873 g_free (path);
2875 if (do_delete && return_status == FILE_CONT)
2877 if (ctx->erase_at_end)
2879 lp = g_new0 (struct link, 1);
2880 lp->src_vpath = tmp_vpath;
2881 lp->st_mode = buf.st_mode;
2882 erase_list = g_slist_append (erase_list, lp);
2883 tmp_vpath = NULL;
2885 else if (S_ISDIR (buf.st_mode))
2886 return_status = erase_dir_iff_empty (ctx, tmp_vpath, tctx->progress_count);
2887 else
2888 return_status = erase_file (tctx, ctx, tmp_vpath);
2890 vfs_path_free (tmp_vpath);
2892 mc_closedir (reading);
2894 if (ctx->preserve)
2896 mc_timesbuf_t times;
2898 mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill);
2899 get_times (&cbuf, &times);
2900 mc_utime (dst_vpath, &times);
2902 else
2904 cbuf.st_mode = umask (-1);
2905 umask (cbuf.st_mode);
2906 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2907 mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill);
2910 ret:
2911 free_link (parent_dirs->data);
2912 g_slist_free_1 (parent_dirs);
2913 ret_fast:
2914 vfs_path_free (src_vpath);
2915 vfs_path_free (dst_vpath);
2916 return return_status;
2919 /* }}} */
2921 /* --------------------------------------------------------------------------------------------- */
2922 /* {{{ Move routines */
2924 FileProgressStatus
2925 move_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d)
2927 return do_move_dir_dir (NULL, tctx, ctx, s, d);
2930 /* }}} */
2932 /* --------------------------------------------------------------------------------------------- */
2933 /* {{{ Erase routines */
2935 FileProgressStatus
2936 erase_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * s_vpath)
2938 file_progress_show_deleting (ctx, vfs_path_as_str (s_vpath), NULL);
2939 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2940 if (check_progress_buttons (ctx) == FILE_ABORT)
2941 return FILE_ABORT;
2943 mc_refresh ();
2945 /* The old way to detect a non empty directory was:
2946 error = my_rmdir (s);
2947 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2948 For the linux user space nfs server (nfs-server-2.2beta29-2)
2949 we would have to check also for EIO. I hope the new way is
2950 fool proof. (Norbert)
2952 if (check_dir_is_empty (s_vpath) == 0)
2953 { /* not empty */
2954 FileProgressStatus error;
2956 error = query_recursive (ctx, vfs_path_as_str (s_vpath));
2957 if (error == FILE_CONT)
2958 error = recursive_erase (tctx, ctx, s_vpath);
2959 return error;
2962 return try_erase_dir (ctx, vfs_path_as_str (s_vpath));
2965 /* }}} */
2967 /* --------------------------------------------------------------------------------------------- */
2968 /* {{{ Panel operate routines */
2970 void
2971 dirsize_status_init_cb (status_msg_t * sm)
2973 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
2974 Widget *wd = WIDGET (sm->dlg);
2976 const char *b1_name = N_("&Abort");
2977 const char *b2_name = N_("&Skip");
2978 int b_width, ui_width;
2980 #ifdef ENABLE_NLS
2981 b1_name = _(b1_name);
2982 b2_name = _(b2_name);
2983 #endif
2985 b_width = str_term_width1 (b1_name) + 4;
2986 if (dsm->allow_skip)
2987 b_width += str_term_width1 (b2_name) + 4 + 1;
2989 ui_width = MAX (COLS / 2, b_width + 6);
2990 dsm->dirname = label_new (2, 3, "");
2991 add_widget (sm->dlg, dsm->dirname);
2992 dsm->count_size = label_new (3, 3, "");
2993 add_widget (sm->dlg, dsm->count_size);
2994 add_widget (sm->dlg, hline_new (4, -1, -1));
2996 dsm->abort_button = WIDGET (button_new (5, 3, FILE_ABORT, NORMAL_BUTTON, b1_name, NULL));
2997 add_widget (sm->dlg, dsm->abort_button);
2998 if (dsm->allow_skip)
3000 dsm->skip_button = WIDGET (button_new (5, 3, FILE_SKIP, NORMAL_BUTTON, b2_name, NULL));
3001 add_widget (sm->dlg, dsm->skip_button);
3002 widget_select (dsm->skip_button);
3005 widget_set_size (wd, wd->y, wd->x, 8, ui_width);
3006 dirsize_status_locate_buttons (dsm);
3009 /* --------------------------------------------------------------------------------------------- */
3012 dirsize_status_update_cb (status_msg_t * sm)
3014 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
3015 Widget *wd = WIDGET (sm->dlg);
3017 /* update second (longer label) */
3018 label_set_textv (dsm->count_size, _("Directories: %zu, total size: %s"),
3019 dsm->dir_count, size_trunc_sep (dsm->total_size, panels_options.kilobyte_si));
3021 /* enlarge dialog if required */
3022 if (WIDGET (dsm->count_size)->cols + 6 > wd->cols)
3024 dlg_set_size (sm->dlg, wd->lines, WIDGET (dsm->count_size)->cols + 6);
3025 dirsize_status_locate_buttons (dsm);
3026 dlg_redraw (sm->dlg);
3029 /* adjust first label */
3030 label_set_text (dsm->dirname, str_trunc (vfs_path_as_str (dsm->dirname_vpath), wd->cols - 6));
3032 switch (status_msg_common_update (sm))
3034 case B_CANCEL:
3035 case FILE_ABORT:
3036 return FILE_ABORT;
3037 case FILE_SKIP:
3038 return FILE_SKIP;
3039 default:
3040 return FILE_CONT;
3044 /* --------------------------------------------------------------------------------------------- */
3046 void
3047 dirsize_status_deinit_cb (status_msg_t * sm)
3049 (void) sm;
3051 /* schedule to update passive panel */
3052 if (get_other_type () == view_listing)
3053 other_panel->dirty = 1;
3056 /* --------------------------------------------------------------------------------------------- */
3058 * compute_dir_size:
3060 * Computes the number of bytes used by the files in a directory
3063 FileProgressStatus
3064 compute_dir_size (const vfs_path_t * dirname_vpath, dirsize_status_msg_t * sm,
3065 size_t * ret_dir_count, size_t * ret_marked_count, uintmax_t * ret_total,
3066 gboolean compute_symlinks)
3068 return do_compute_dir_size (dirname_vpath, sm, ret_dir_count, ret_marked_count, ret_total,
3069 compute_symlinks);
3072 /* --------------------------------------------------------------------------------------------- */
3074 * panel_operate:
3076 * Performs one of the operations on the selection on the source_panel
3077 * (copy, delete, move).
3079 * Returns TRUE if did change the directory
3080 * structure, Returns FALSE if user aborted
3082 * force_single forces operation on the current entry and affects
3083 * default destination. Current filename is used as default.
3086 gboolean
3087 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
3089 WPanel *panel = PANEL (source_panel);
3090 const gboolean single_entry = force_single || (panel->marked <= 1)
3091 || (get_current_type () == view_tree);
3093 const char *source = NULL;
3094 char *dest = NULL;
3095 vfs_path_t *dest_vpath = NULL;
3096 char *save_cwd = NULL, *save_dest = NULL;
3097 struct stat src_stat;
3098 gboolean ret_val = TRUE;
3099 int i;
3100 FileProgressStatus value;
3101 file_op_context_t *ctx;
3102 file_op_total_context_t *tctx;
3103 vfs_path_t *tmp_vpath;
3104 filegui_dialog_type_t dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3106 gboolean do_bg = FALSE; /* do background operation? */
3108 static gboolean i18n_flag = FALSE;
3109 if (!i18n_flag)
3111 for (i = G_N_ELEMENTS (op_names); i-- != 0;)
3112 op_names[i] = Q_ (op_names[i]);
3113 i18n_flag = TRUE;
3116 linklist = free_linklist (linklist);
3117 dest_dirs = free_linklist (dest_dirs);
3119 if (single_entry)
3121 source = check_single_entry (panel, force_single, &src_stat);
3123 if (source == NULL)
3124 return FALSE;
3127 ctx = file_op_context_new (operation);
3129 /* Show confirmation dialog */
3130 if (operation != OP_DELETE)
3132 dest =
3133 do_confirm_copy_move (panel, operation, force_single, source, &src_stat, ctx, &do_bg);
3135 if (dest == NULL)
3137 ret_val = FALSE;
3138 goto ret_fast;
3141 dest_vpath = vfs_path_from_str (dest);
3143 else if (confirm_delete && !do_confirm_erase (panel, source, &src_stat))
3145 ret_val = FALSE;
3146 goto ret_fast;
3149 tctx = file_op_total_context_new ();
3150 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
3152 #ifdef ENABLE_BACKGROUND
3153 /* Did the user select to do a background operation? */
3154 if (do_bg)
3156 int v;
3158 v = do_background (ctx,
3159 g_strconcat (op_names[operation], ": ",
3160 vfs_path_as_str (panel->cwd_vpath), (char *) NULL));
3161 if (v == -1)
3162 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
3164 /* If we are the parent */
3165 if (v == 1)
3167 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
3169 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
3170 vfs_path_free (dest_vpath);
3171 g_free (dest);
3172 /* file_op_context_destroy (ctx); */
3173 return FALSE;
3176 else
3177 #endif /* ENABLE_BACKGROUND */
3179 if (operation == OP_DELETE)
3180 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
3181 else if (single_entry && S_ISDIR (selection (panel)->st.st_mode))
3182 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3183 else if (single_entry || force_single)
3184 dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3185 else
3186 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3189 /* Initialize things */
3190 /* We do not want to trash cache every time file is
3191 created/touched. However, this will make our cache contain
3192 invalid data. */
3193 if ((dest != NULL)
3194 && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3195 save_dest = g_strdup (dest);
3197 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
3198 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3199 save_cwd = g_strdup (vfs_path_as_str (panel->cwd_vpath));
3201 /* Now, let's do the job */
3203 /* This code is only called by the tree and panel code */
3204 if (single_entry)
3206 /* We now have ETA in all cases */
3208 /* One file: FIXME mc_chdir will take user out of any vfs */
3209 if ((operation != OP_COPY) && (get_current_type () == view_tree))
3211 vfs_path_t *vpath;
3212 int chdir_retcode;
3214 vpath = vfs_path_from_str (PATH_SEP_STR);
3215 chdir_retcode = mc_chdir (vpath);
3216 vfs_path_free (vpath);
3217 if (chdir_retcode < 0)
3219 ret_val = FALSE;
3220 goto clean_up;
3224 value =
3225 operate_single_file (panel, operation, tctx, ctx, source, &src_stat, dest, dialog_type);
3227 if ((value == FILE_CONT) && !force_single)
3228 unmark_files (panel);
3230 else
3232 /* Many files */
3234 /* Check destination for copy or move operation */
3235 while (operation != OP_DELETE)
3237 int dst_result;
3238 struct stat dst_stat;
3240 dst_result = mc_stat (dest_vpath, &dst_stat);
3242 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
3243 break;
3245 if (ctx->skip_all
3246 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
3247 goto clean_up;
3250 /* TODO: the good way is required to skip directories scanning in case of rename/move
3251 * of several directories. Since reqular expression can be used for destination,
3252 * some directory movements can be a cross-filesystem and directory scanning is useful
3253 * for those directories only. */
3255 if (panel_operate_init_totals (panel, NULL, NULL, ctx, file_op_compute_totals, dialog_type)
3256 == FILE_CONT)
3258 /* Loop for every file, perform the actual copy operation */
3259 for (i = 0; i < panel->dir.len; i++)
3261 const char *source2;
3263 if (!panel->dir.list[i].f.marked)
3264 continue; /* Skip the unmarked ones */
3266 source2 = panel->dir.list[i].fname;
3267 src_stat = panel->dir.list[i].st;
3269 value = operate_one_file (panel, operation, tctx, ctx, source2, &src_stat, dest);
3271 if (value == FILE_ABORT)
3272 break;
3274 if (value == FILE_CONT)
3275 do_file_mark (panel, i, 0);
3277 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3279 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3280 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3283 if (operation != OP_DELETE)
3284 file_progress_show (ctx, 0, 0, "", FALSE);
3286 if (check_progress_buttons (ctx) == FILE_ABORT)
3287 break;
3289 mc_refresh ();
3290 } /* Loop for every file */
3292 } /* Many entries */
3294 clean_up:
3295 /* Clean up */
3296 if (save_cwd != NULL)
3298 tmp_vpath = vfs_path_from_str (save_cwd);
3299 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3300 vfs_path_free (tmp_vpath);
3301 g_free (save_cwd);
3304 if (save_dest != NULL)
3306 tmp_vpath = vfs_path_from_str (save_dest);
3307 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3308 vfs_path_free (tmp_vpath);
3309 g_free (save_dest);
3312 linklist = free_linklist (linklist);
3313 dest_dirs = free_linklist (dest_dirs);
3314 g_free (dest);
3315 vfs_path_free (dest_vpath);
3316 MC_PTR_FREE (ctx->dest_mask);
3318 #ifdef ENABLE_BACKGROUND
3319 /* Let our parent know we are saying bye bye */
3320 if (mc_global.we_are_background)
3322 int cur_pid = getpid ();
3323 /* Send pid to parent with child context, it is fork and
3324 don't modify real parent ctx */
3325 ctx->pid = cur_pid;
3326 parent_call ((void *) end_bg_process, ctx, 0);
3328 vfs_shut ();
3329 my_exit (EXIT_SUCCESS);
3331 #endif /* ENABLE_BACKGROUND */
3333 file_op_total_context_destroy (tctx);
3334 ret_fast:
3335 file_op_context_destroy (ctx);
3337 return ret_val;
3340 /* }}} */
3342 /* --------------------------------------------------------------------------------------------- */
3343 /* {{{ Query/status report routines */
3344 /** Report error with one file */
3345 FileProgressStatus
3346 file_error (const char *format, const char *file)
3348 char buf[BUF_MEDIUM];
3350 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3352 return do_file_error (buf);
3355 /* --------------------------------------------------------------------------------------------- */
3358 Cause emacs to enter folding mode for this file:
3359 Local variables:
3360 end: