Ticket #3577: Fix preallocation if appending during file copy
[midnight-commander.git] / src / filemanager / file.c
blobd42e85a8007be5790e2081228328f2064766c84e
1 /*
2 File management.
4 Copyright (C) 1994-2016
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() */
86 #include "file.h"
88 /* }}} */
90 /*** global variables ****************************************************************************/
92 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
93 const char *op_names[3] = {
94 N_("DialogTitle|Copy"),
95 N_("DialogTitle|Move"),
96 N_("DialogTitle|Delete")
99 /*** file scope macro definitions ****************************************************************/
101 /* Hack: the vfs code should not rely on this */
102 #define WITH_FULL_PATHS 1
104 #define FILEOP_UPDATE_INTERVAL 2
105 #define FILEOP_STALLING_INTERVAL 4
107 /*** file scope type declarations ****************************************************************/
109 /* This is a hard link cache */
110 struct link
112 const struct vfs_class *vfs;
113 dev_t dev;
114 ino_t ino;
115 short linkcount;
116 mode_t st_mode;
117 vfs_path_t *src_vpath;
118 vfs_path_t *dst_vpath;
121 /* Status of the destination file */
122 typedef enum
124 DEST_NONE = 0, /* Not created */
125 DEST_SHORT = 1, /* Created, not fully copied */
126 DEST_FULL = 2 /* Created, fully copied */
127 } dest_status_t;
130 * This array introduced to avoid translation problems. The former (op_names)
131 * is assumed to be nouns, suitable in dialog box titles; this one should
132 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
133 * (I don't use spaces around the words, because someday they could be
134 * dropped, when widgets get smarter)
137 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
138 static const char *op_names1[] = {
139 N_("FileOperation|Copy"),
140 N_("FileOperation|Move"),
141 N_("FileOperation|Delete")
145 * These are formats for building a prompt. Parts encoded as follows:
146 * %o - operation from op_names1
147 * %f - file/files or files/directories, as appropriate
148 * %m - "with source mask" or question mark for delete
149 * %s - source name (truncated)
150 * %d - number of marked files
151 * %n - the '\n' symbol to form two-line prompt for delete or space for other operations
153 /* xgettext:no-c-format */
154 static const char *one_format = N_("%o %f%n\"%s\"%m");
155 /* xgettext:no-c-format */
156 static const char *many_format = N_("%o %d %f%m");
158 static const char *prompt_parts[] = {
159 N_("file"),
160 N_("files"),
161 N_("directory"),
162 N_("directories"),
163 N_("files/directories"),
164 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
165 N_(" with source mask:")
168 /*** file scope variables ************************************************************************/
170 /* the hard link cache */
171 static GSList *linklist = NULL;
173 /* the files-to-be-erased list */
174 static GSList *erase_list = NULL;
177 * In copy_dir_dir we use two additional single linked lists: The first -
178 * variable name 'parent_dirs' - holds information about already copied
179 * directories and is used to detect cyclic symbolic links.
180 * The second ('dest_dirs' below) holds information about just created
181 * target directories and is used to detect when an directory is copied
182 * into itself (we don't want to copy infinitly).
183 * Both lists don't use the linkcount and name structure members of struct
184 * link.
186 static GSList *dest_dirs = NULL;
188 static FileProgressStatus transform_error = FILE_CONT;
190 /* --------------------------------------------------------------------------------------------- */
191 /*** file scope functions ************************************************************************/
192 /* --------------------------------------------------------------------------------------------- */
194 static void
195 dirsize_status_locate_buttons (dirsize_status_msg_t * dsm)
197 status_msg_t *sm = STATUS_MSG (dsm);
198 Widget *wd = WIDGET (sm->dlg);
199 int y, x;
201 y = wd->y + 5;
202 x = wd->x;
204 if (!dsm->allow_skip)
206 /* single button: "Abort" */
207 x += (wd->cols - dsm->abort_button->cols) / 2;
208 widget_set_size (dsm->abort_button, y, x,
209 dsm->abort_button->lines, dsm->abort_button->cols);
211 else
213 /* two buttons: "Abort" and "Skip" */
214 int cols;
216 cols = dsm->abort_button->cols + dsm->skip_button->cols + 1;
217 x += (wd->cols - cols) / 2;
218 widget_set_size (dsm->abort_button, y, x, dsm->abort_button->lines,
219 dsm->abort_button->cols);
220 x += dsm->abort_button->cols + 1;
221 widget_set_size (dsm->skip_button, y, x, dsm->skip_button->lines, dsm->skip_button->cols);
225 /* --------------------------------------------------------------------------------------------- */
227 static char *
228 transform_source (file_op_context_t * ctx, const vfs_path_t * source_vpath)
230 char *s, *q;
231 char *fnsource;
233 s = g_strdup (vfs_path_as_str (source_vpath));
235 /* We remove \n from the filename since regex routines would use \n as an anchor */
236 /* this is just to be allowed to maniupulate file names with \n on it */
237 for (q = s; *q != '\0'; q++)
238 if (*q == '\n')
239 *q = ' ';
241 fnsource = (char *) x_basename (s);
243 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
245 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
246 if (ctx->search_handle->error != MC_SEARCH_E_OK)
248 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
249 q = NULL;
250 transform_error = FILE_ABORT;
253 else
255 q = NULL;
256 transform_error = FILE_SKIP;
259 g_free (s);
260 return q;
263 /* --------------------------------------------------------------------------------------------- */
265 static void
266 free_link (void *data)
268 struct link *lp = (struct link *) data;
270 vfs_path_free (lp->src_vpath);
271 vfs_path_free (lp->dst_vpath);
272 g_free (lp);
275 /* --------------------------------------------------------------------------------------------- */
277 static inline void *
278 free_linklist (GSList * lp)
280 g_slist_free_full (lp, free_link);
282 return NULL;
285 /* --------------------------------------------------------------------------------------------- */
287 static gboolean
288 is_in_linklist (const GSList * lp, const vfs_path_t * vpath, const struct stat *sb)
290 const struct vfs_class *class;
291 ino_t ino = sb->st_ino;
292 dev_t dev = sb->st_dev;
294 class = vfs_path_get_last_path_vfs (vpath);
296 for (; lp != NULL; lp = g_slist_next (lp))
298 const struct link *lnk = (const struct link *) lp->data;
300 if (lnk->vfs == class && lnk->ino == ino && lnk->dev == dev)
301 return TRUE;
303 return FALSE;
306 /* --------------------------------------------------------------------------------------------- */
308 * Check and made hardlink
310 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
311 * and a hardlink was successfully made
314 static gboolean
315 check_hardlinks (const vfs_path_t * src_vpath, const vfs_path_t * dst_vpath, struct stat *pstat)
317 GSList *lp;
318 struct link *lnk;
320 const struct vfs_class *my_vfs;
321 ino_t ino = pstat->st_ino;
322 dev_t dev = pstat->st_dev;
323 struct stat link_stat;
325 if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
326 return FALSE;
328 my_vfs = vfs_path_get_by_index (src_vpath, -1)->class;
330 for (lp = linklist; lp != NULL; lp = g_slist_next (lp))
332 lnk = (struct link *) lp->data;
334 if (lnk->vfs == my_vfs && lnk->ino == ino && lnk->dev == dev)
336 const struct vfs_class *lp_name_class;
337 int stat_result;
339 lp_name_class = vfs_path_get_last_path_vfs (lnk->src_vpath);
340 stat_result = mc_stat (lnk->src_vpath, &link_stat);
342 if (stat_result == 0 && link_stat.st_ino == ino
343 && link_stat.st_dev == dev && lp_name_class == my_vfs)
345 const struct vfs_class *p_class, *dst_name_class;
347 dst_name_class = vfs_path_get_last_path_vfs (dst_vpath);
348 p_class = vfs_path_get_last_path_vfs (lnk->dst_vpath);
350 if (dst_name_class == p_class &&
351 mc_stat (lnk->dst_vpath, &link_stat) == 0 &&
352 mc_link (lnk->dst_vpath, dst_vpath) == 0)
353 return TRUE;
356 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
357 return FALSE;
361 lnk = g_new0 (struct link, 1);
362 if (lnk != NULL)
364 lnk->vfs = my_vfs;
365 lnk->ino = ino;
366 lnk->dev = dev;
367 lnk->src_vpath = vfs_path_clone (src_vpath);
368 lnk->dst_vpath = vfs_path_clone (dst_vpath);
369 linklist = g_slist_prepend (linklist, lnk);
372 return FALSE;
375 /* --------------------------------------------------------------------------------------------- */
377 * Duplicate the contents of the symbolic link src_path in dst_path.
378 * Try to make a stable symlink if the option "stable symlink" was
379 * set in the file mask dialog.
380 * If dst_path is an existing symlink it will be deleted silently
381 * (upper levels take already care of existing files at dst_path).
384 static FileProgressStatus
385 make_symlink (file_op_context_t * ctx, const char *src_path, const char *dst_path)
387 char link_target[MC_MAXPATHLEN];
388 int len;
389 FileProgressStatus return_status;
390 struct stat sb;
391 vfs_path_t *src_vpath;
392 vfs_path_t *dst_vpath;
393 gboolean dst_is_symlink;
394 vfs_path_t *link_target_vpath = NULL;
396 src_vpath = vfs_path_from_str (src_path);
397 dst_vpath = vfs_path_from_str (dst_path);
398 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
400 retry_src_readlink:
401 len = mc_readlink (src_vpath, link_target, MC_MAXPATHLEN - 1);
402 if (len < 0)
404 if (ctx->skip_all)
405 return_status = FILE_SKIPALL;
406 else
408 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
409 if (return_status == FILE_SKIPALL)
410 ctx->skip_all = TRUE;
411 if (return_status == FILE_RETRY)
412 goto retry_src_readlink;
414 goto ret;
416 link_target[len] = 0;
418 if (ctx->stable_symlinks)
421 if (!vfs_file_is_local (src_vpath) || !vfs_file_is_local (dst_vpath))
423 message (D_ERROR, MSG_ERROR,
424 _("Cannot make stable symlinks across"
425 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
426 ctx->stable_symlinks = FALSE;
430 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
432 const char *r = strrchr (src_path, PATH_SEP);
434 if (r)
436 char *p;
437 vfs_path_t *q;
439 p = g_strndup (src_path, r - src_path + 1);
440 if (g_path_is_absolute (dst_path))
441 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
442 else
443 q = vfs_path_build_filename (p, dst_path, (char *) NULL);
445 if (vfs_path_tokens_count (q) > 1)
447 char *s;
448 vfs_path_t *tmp_vpath1, *tmp_vpath2;
450 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
451 s = g_strconcat (p, link_target, (char *) NULL);
452 g_free (p);
453 g_strlcpy (link_target, s, sizeof (link_target));
454 g_free (s);
455 tmp_vpath2 = vfs_path_from_str (link_target);
456 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
457 vfs_path_free (tmp_vpath1);
458 vfs_path_free (tmp_vpath2);
459 if (s)
461 g_strlcpy (link_target, s, sizeof (link_target));
462 g_free (s);
465 else
466 g_free (p);
467 vfs_path_free (q);
470 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
472 retry_dst_symlink:
473 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
475 /* Success */
476 return_status = FILE_CONT;
477 goto ret;
480 * if dst_exists, it is obvious that this had failed.
481 * We can delete the old symlink and try again...
483 if (dst_is_symlink)
485 if (mc_unlink (dst_vpath) == 0)
486 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
488 /* Success */
489 return_status = FILE_CONT;
490 goto ret;
493 if (ctx->skip_all)
494 return_status = FILE_SKIPALL;
495 else
497 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
498 if (return_status == FILE_SKIPALL)
499 ctx->skip_all = TRUE;
500 if (return_status == FILE_RETRY)
501 goto retry_dst_symlink;
504 ret:
505 vfs_path_free (src_vpath);
506 vfs_path_free (dst_vpath);
507 vfs_path_free (link_target_vpath);
508 return return_status;
511 /* --------------------------------------------------------------------------------------------- */
513 * do_compute_dir_size:
515 * Computes the number of bytes used by the files in a directory
518 static FileProgressStatus
519 do_compute_dir_size (const vfs_path_t * dirname_vpath, dirsize_status_msg_t * dsm,
520 size_t * dir_count, size_t * ret_marked, uintmax_t * ret_total,
521 gboolean compute_symlinks)
523 static guint64 timestamp = 0;
524 /* update with 25 FPS rate */
525 static const guint64 delay = G_USEC_PER_SEC / 25;
527 status_msg_t *sm = STATUS_MSG (dsm);
528 int res;
529 struct stat s;
530 DIR *dir;
531 struct dirent *dirent;
532 FileProgressStatus ret = FILE_CONT;
534 if (!compute_symlinks)
536 res = mc_lstat (dirname_vpath, &s);
537 if (res != 0)
538 return ret;
540 /* don't scan symlink to directory */
541 if (S_ISLNK (s.st_mode))
543 (*ret_marked)++;
544 *ret_total += (uintmax_t) s.st_size;
545 return ret;
549 (*dir_count)++;
551 dir = mc_opendir (dirname_vpath);
552 if (dir == NULL)
553 return ret;
555 while (ret == FILE_CONT && (dirent = mc_readdir (dir)) != NULL)
557 vfs_path_t *tmp_vpath;
559 if (DIR_IS_DOT (dirent->d_name) || DIR_IS_DOTDOT (dirent->d_name))
560 continue;
562 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, NULL);
564 res = mc_lstat (tmp_vpath, &s);
565 if (res == 0)
567 if (S_ISDIR (s.st_mode))
568 ret =
569 do_compute_dir_size (tmp_vpath, dsm, dir_count, ret_marked, ret_total,
570 compute_symlinks);
571 else
573 ret = FILE_CONT;
575 (*ret_marked)++;
576 *ret_total += (uintmax_t) s.st_size;
579 if (ret == FILE_CONT && sm->update != NULL && mc_time_elapsed (&timestamp, delay))
581 dsm->dirname_vpath = tmp_vpath;
582 dsm->dir_count = *dir_count;
583 dsm->total_size = *ret_total;
584 ret = sm->update (sm);
588 vfs_path_free (tmp_vpath);
591 mc_closedir (dir);
592 return ret;
595 /* --------------------------------------------------------------------------------------------- */
597 static FileProgressStatus
598 progress_update_one (file_op_total_context_t * tctx, file_op_context_t * ctx, off_t add)
600 struct timeval tv_current;
601 static struct timeval tv_start = { 0, 0 };
603 tctx->progress_count++;
604 tctx->progress_bytes += (uintmax_t) add;
606 if (tv_start.tv_sec == 0)
608 gettimeofday (&tv_start, (struct timezone *) NULL);
610 gettimeofday (&tv_current, (struct timezone *) NULL);
611 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
613 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
615 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
616 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
618 tv_start.tv_sec = tv_current.tv_sec;
621 return check_progress_buttons (ctx);
624 /* --------------------------------------------------------------------------------------------- */
626 static FileProgressStatus
627 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
629 char *msg;
630 int result = 0;
631 const char *head_msg;
633 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
635 msg = g_strdup_printf (fmt, a, b);
636 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
637 g_free (msg);
638 do_refresh ();
640 return (result == 1) ? FILE_ABORT : FILE_SKIP;
643 /* --------------------------------------------------------------------------------------------- */
645 static FileProgressStatus
646 warn_same_file (const char *fmt, const char *a, const char *b)
648 #ifdef ENABLE_BACKGROUND
649 /* *INDENT-OFF* */
650 union
652 void *p;
653 FileProgressStatus (*f) (enum OperationMode, const char *fmt, const char *a, const char *b);
654 } pntr;
655 /* *INDENT-ON* */
657 pntr.f = real_warn_same_file;
659 if (mc_global.we_are_background)
660 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
661 #endif
662 return real_warn_same_file (Foreground, fmt, a, b);
665 /* --------------------------------------------------------------------------------------------- */
666 /* {{{ Query/status report routines */
668 static FileProgressStatus
669 real_do_file_error (enum OperationMode mode, const char *error)
671 int result;
672 const char *msg;
674 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
675 result =
676 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
678 switch (result)
680 case 0:
681 do_refresh ();
682 return FILE_SKIP;
684 case 1:
685 do_refresh ();
686 return FILE_SKIPALL;
688 case 2:
689 do_refresh ();
690 return FILE_RETRY;
692 case 3:
693 default:
694 return FILE_ABORT;
698 /* --------------------------------------------------------------------------------------------- */
700 static FileProgressStatus
701 real_query_recursive (file_op_context_t * ctx, enum OperationMode mode, const char *s)
703 if (ctx->recursive_result < RECURSIVE_ALWAYS)
705 const char *msg;
706 char *text;
708 msg = mode == Foreground
709 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
710 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
711 text = g_strdup_printf (msg, path_trunc (s, 30));
713 if (safe_delete)
714 query_set_sel (1);
716 ctx->recursive_result =
717 query_dialog (op_names[OP_DELETE], text, D_ERROR, 5, _("&Yes"), _("&No"), _("A&ll"),
718 _("Non&e"), _("&Abort"));
719 g_free (text);
721 if (ctx->recursive_result != RECURSIVE_ABORT)
722 do_refresh ();
725 switch (ctx->recursive_result)
727 case RECURSIVE_YES:
728 case RECURSIVE_ALWAYS:
729 return FILE_CONT;
731 case RECURSIVE_NO:
732 case RECURSIVE_NEVER:
733 return FILE_SKIP;
735 case RECURSIVE_ABORT:
736 default:
737 return FILE_ABORT;
741 /* --------------------------------------------------------------------------------------------- */
743 #ifdef ENABLE_BACKGROUND
744 static FileProgressStatus
745 do_file_error (const char *str)
747 /* *INDENT-OFF* */
748 union
750 void *p;
751 FileProgressStatus (*f) (enum OperationMode, const char *);
752 } pntr;
753 /* *INDENT-ON* */
755 pntr.f = real_do_file_error;
757 if (mc_global.we_are_background)
758 return parent_call (pntr.p, NULL, 1, strlen (str), str);
759 else
760 return real_do_file_error (Foreground, str);
763 /* --------------------------------------------------------------------------------------------- */
765 static FileProgressStatus
766 query_recursive (file_op_context_t * ctx, const char *s)
768 /* *INDENT-OFF* */
769 union
771 void *p;
772 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *);
773 } pntr;
774 /* *INDENT-ON* */
776 pntr.f = real_query_recursive;
778 if (mc_global.we_are_background)
779 return parent_call (pntr.p, ctx, 1, strlen (s), s);
780 else
781 return real_query_recursive (ctx, Foreground, s);
784 /* --------------------------------------------------------------------------------------------- */
786 static FileProgressStatus
787 query_replace (file_op_context_t * ctx, const char *destname, struct stat *_s_stat,
788 struct stat *_d_stat)
790 /* *INDENT-OFF* */
791 union
793 void *p;
794 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *,
795 struct stat *, struct stat *);
796 } pntr;
797 /* *INDENT-ON* */
799 pntr.f = file_progress_real_query_replace;
801 if (mc_global.we_are_background)
802 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
803 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
804 else
805 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
808 #else
809 /* --------------------------------------------------------------------------------------------- */
811 static FileProgressStatus
812 do_file_error (const char *str)
814 return real_do_file_error (Foreground, str);
817 /* --------------------------------------------------------------------------------------------- */
819 static FileProgressStatus
820 query_recursive (file_op_context_t * ctx, const char *s)
822 return real_query_recursive (ctx, Foreground, s);
825 /* --------------------------------------------------------------------------------------------- */
827 static FileProgressStatus
828 query_replace (file_op_context_t * ctx, const char *destname, struct stat *_s_stat,
829 struct stat *_d_stat)
831 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
834 #endif /* !ENABLE_BACKGROUND */
836 /* --------------------------------------------------------------------------------------------- */
837 /** Report error with two files */
839 static FileProgressStatus
840 files_error (const char *format, const char *file1, const char *file2)
842 char buf[BUF_MEDIUM];
843 char *nfile1 = g_strdup (path_trunc (file1, 15));
844 char *nfile2 = g_strdup (path_trunc (file2, 15));
846 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
848 g_free (nfile1);
849 g_free (nfile2);
851 return do_file_error (buf);
854 /* }}} */
856 /* --------------------------------------------------------------------------------------------- */
858 static void
859 copy_file_file_display_progress (file_op_total_context_t * tctx, file_op_context_t * ctx,
860 struct timeval tv_current, struct timeval tv_transfer_start,
861 off_t file_size, off_t n_read_total)
863 long dt;
865 /* 1. Update rotating dash after some time */
866 rotate_dash (TRUE);
868 /* 3. Compute ETA */
869 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
871 if (n_read_total == 0)
872 ctx->eta_secs = 0.0;
873 else
875 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
876 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
879 /* 4. Compute BPS rate */
880 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
881 if (ctx->bps_time < 1)
882 ctx->bps_time = 1;
883 ctx->bps = n_read_total / ctx->bps_time;
885 /* 5. Compute total ETA and BPS */
886 if (ctx->progress_bytes != 0)
888 uintmax_t remain_bytes;
890 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
891 #if 1
893 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
895 if (total_secs < 1)
896 total_secs = 1;
898 tctx->bps = tctx->copied_bytes / total_secs;
899 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
901 #else
902 /* broken on lot of little files */
903 tctx->bps_count++;
904 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
905 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
906 #endif
910 /* --------------------------------------------------------------------------------------------- */
912 /* {{{ Move routines */
913 static FileProgressStatus
914 move_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s,
915 const char *d)
917 struct stat src_stats, dst_stats;
918 FileProgressStatus return_status = FILE_CONT;
919 gboolean copy_done = FALSE;
920 gboolean old_ask_overwrite;
921 vfs_path_t *src_vpath, *dst_vpath;
923 src_vpath = vfs_path_from_str (s);
924 dst_vpath = vfs_path_from_str (d);
926 file_progress_show_source (ctx, src_vpath);
927 file_progress_show_target (ctx, dst_vpath);
929 if (check_progress_buttons (ctx) == FILE_ABORT)
931 return_status = FILE_ABORT;
932 goto ret;
935 mc_refresh ();
937 while (mc_lstat (src_vpath, &src_stats) != 0)
939 /* Source doesn't exist */
940 if (ctx->skip_all)
941 return_status = FILE_SKIPALL;
942 else
944 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
945 if (return_status == FILE_SKIPALL)
946 ctx->skip_all = TRUE;
949 if (return_status != FILE_RETRY)
950 goto ret;
953 if (mc_lstat (dst_vpath, &dst_stats) == 0)
955 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
957 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
958 goto ret;
961 if (S_ISDIR (dst_stats.st_mode))
963 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
964 do_refresh ();
965 return_status = FILE_SKIP;
966 goto ret;
969 if (confirm_overwrite)
971 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
972 if (return_status != FILE_CONT)
973 goto ret;
975 /* Ok to overwrite */
978 if (!ctx->do_append)
980 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
982 return_status = make_symlink (ctx, s, d);
983 if (return_status == FILE_CONT)
984 goto retry_src_remove;
985 goto ret;
988 if (mc_rename (src_vpath, dst_vpath) == 0)
990 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
991 goto ret;
994 #if 0
995 /* Comparison to EXDEV seems not to work in nfs if you're moving from
996 one nfs to the same, but on the server it is on two different
997 filesystems. Then nfs returns EIO instead of EXDEV.
998 Hope it will not hurt if we always in case of error try to copy/delete. */
999 else
1000 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1002 if (errno != EXDEV)
1004 if (ctx->skip_all)
1005 return_status = FILE_SKIPALL;
1006 else
1008 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
1009 if (return_status == FILE_SKIPALL)
1010 ctx->skip_all = TRUE;
1011 if (return_status == FILE_RETRY)
1012 goto retry_rename;
1015 goto ret;
1017 #endif
1019 /* Failed because filesystem boundary -> copy the file instead */
1020 old_ask_overwrite = tctx->ask_overwrite;
1021 tctx->ask_overwrite = FALSE;
1022 return_status = copy_file_file (tctx, ctx, s, d);
1023 tctx->ask_overwrite = old_ask_overwrite;
1024 if (return_status != FILE_CONT)
1025 goto ret;
1027 copy_done = TRUE;
1029 file_progress_show_source (ctx, NULL);
1030 file_progress_show (ctx, 0, 0, "", FALSE);
1032 return_status = check_progress_buttons (ctx);
1033 if (return_status != FILE_CONT)
1034 goto ret;
1035 mc_refresh ();
1037 retry_src_remove:
1038 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
1040 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
1041 if (return_status == FILE_RETRY)
1042 goto retry_src_remove;
1043 if (return_status == FILE_SKIPALL)
1044 ctx->skip_all = TRUE;
1045 goto ret;
1048 if (!copy_done)
1049 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
1051 ret:
1052 vfs_path_free (src_vpath);
1053 vfs_path_free (dst_vpath);
1055 return return_status;
1058 /* }}} */
1060 /* --------------------------------------------------------------------------------------------- */
1061 /* {{{ Erase routines */
1062 /** Don't update progress status if progress_count==NULL */
1064 static FileProgressStatus
1065 erase_file (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * vpath)
1067 struct stat buf;
1069 file_progress_show_deleting (ctx, vfs_path_as_str (vpath), &tctx->progress_count);
1070 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1071 if (check_progress_buttons (ctx) == FILE_ABORT)
1072 return FILE_ABORT;
1074 mc_refresh ();
1076 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
1078 /* ignore, most likely the mc_unlink fails, too */
1079 buf.st_size = 0;
1082 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
1084 int return_status;
1086 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), vfs_path_as_str (vpath));
1087 if (return_status == FILE_ABORT)
1088 return return_status;
1089 if (return_status == FILE_RETRY)
1090 continue;
1091 if (return_status == FILE_SKIPALL)
1092 ctx->skip_all = TRUE;
1093 break;
1096 if (tctx->progress_count == 0)
1097 return FILE_CONT;
1099 return check_progress_buttons (ctx);
1102 /* --------------------------------------------------------------------------------------------- */
1105 Recursive remove of files
1106 abort->cancel stack
1107 skip ->warn every level, gets default
1108 skipall->remove as much as possible
1110 static FileProgressStatus
1111 recursive_erase (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * vpath)
1113 struct dirent *next;
1114 DIR *reading;
1115 const char *s;
1116 FileProgressStatus return_status = FILE_CONT;
1118 reading = mc_opendir (vpath);
1119 if (reading == NULL)
1120 return FILE_RETRY;
1122 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1124 vfs_path_t *tmp_vpath;
1125 struct stat buf;
1127 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
1128 continue;
1130 tmp_vpath = vfs_path_append_new (vpath, next->d_name, NULL);
1131 if (mc_lstat (tmp_vpath, &buf) != 0)
1133 mc_closedir (reading);
1134 vfs_path_free (tmp_vpath);
1135 return FILE_RETRY;
1137 if (S_ISDIR (buf.st_mode))
1138 return_status = recursive_erase (tctx, ctx, tmp_vpath);
1139 else
1140 return_status = erase_file (tctx, ctx, tmp_vpath);
1141 vfs_path_free (tmp_vpath);
1143 mc_closedir (reading);
1145 if (return_status == FILE_ABORT)
1146 return FILE_ABORT;
1148 s = vfs_path_as_str (vpath);
1150 file_progress_show_deleting (ctx, s, NULL);
1151 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1152 if (check_progress_buttons (ctx) == FILE_ABORT)
1153 return FILE_ABORT;
1155 mc_refresh ();
1157 while (my_rmdir (s) != 0 && !ctx->skip_all)
1159 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1160 if (return_status == FILE_RETRY)
1161 continue;
1162 if (return_status == FILE_ABORT)
1163 break;
1164 if (return_status == FILE_SKIPALL)
1165 ctx->skip_all = TRUE;
1166 break;
1169 return return_status;
1172 /* --------------------------------------------------------------------------------------------- */
1173 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1174 in the directory path points to, 0 else. */
1176 static int
1177 check_dir_is_empty (const vfs_path_t * vpath)
1179 DIR *dir;
1180 struct dirent *d;
1181 int i = 1;
1183 dir = mc_opendir (vpath);
1184 if (dir == NULL)
1185 return -1;
1187 for (d = mc_readdir (dir); d != NULL; d = mc_readdir (dir))
1188 if (!DIR_IS_DOT (d->d_name) && !DIR_IS_DOTDOT (d->d_name))
1190 i = 0;
1191 break;
1194 mc_closedir (dir);
1195 return i;
1198 /* --------------------------------------------------------------------------------------------- */
1200 static FileProgressStatus
1201 erase_dir_iff_empty (file_op_context_t * ctx, const vfs_path_t * vpath, size_t count)
1203 FileProgressStatus error = FILE_CONT;
1204 const char *s;
1206 s = vfs_path_as_str (vpath);
1208 file_progress_show_deleting (ctx, s, NULL);
1209 file_progress_show_count (ctx, count, ctx->progress_count);
1210 if (check_progress_buttons (ctx) == FILE_ABORT)
1211 return FILE_ABORT;
1213 mc_refresh ();
1215 if (check_dir_is_empty (vpath) == 1) /* not empty or error */
1217 while (my_rmdir (s) != 0 && !ctx->skip_all)
1219 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1220 if (error == FILE_SKIPALL)
1221 ctx->skip_all = TRUE;
1222 if (error != FILE_RETRY)
1223 break;
1227 return error;
1230 /* }}} */
1232 /* --------------------------------------------------------------------------------------------- */
1233 /* {{{ Panel operate routines */
1236 * Return currently selected entry name or the name of the first marked
1237 * entry if there is one.
1240 static char *
1241 panel_get_file (WPanel * panel)
1243 if (get_current_type () == view_tree)
1245 WTree *tree;
1246 vfs_path_t *selected_name;
1248 tree = (WTree *) get_panel_widget (get_current_index ());
1249 selected_name = tree_selected_name (tree);
1250 return g_strdup (vfs_path_as_str (selected_name));
1253 if (panel->marked != 0)
1255 int i;
1257 for (i = 0; i < panel->dir.len; i++)
1258 if (panel->dir.list[i].f.marked)
1259 return g_strdup (panel->dir.list[i].fname);
1261 return g_strdup (panel->dir.list[panel->selected].fname);
1264 /* --------------------------------------------------------------------------------------------- */
1266 * panel_compute_totals:
1268 * compute the number of files and the number of bytes
1269 * used up by the whole selection, recursing directories
1270 * as required. In addition, it checks to see if it will
1271 * overwrite any files by doing the copy.
1274 static FileProgressStatus
1275 panel_compute_totals (const WPanel * panel, dirsize_status_msg_t * sm, size_t * ret_count,
1276 uintmax_t * ret_total, gboolean compute_symlinks)
1278 int i;
1279 size_t dir_count = 0;
1281 for (i = 0; i < panel->dir.len; i++)
1283 struct stat *s;
1285 if (!panel->dir.list[i].f.marked)
1286 continue;
1288 s = &panel->dir.list[i].st;
1290 if (S_ISDIR (s->st_mode))
1292 vfs_path_t *p;
1293 FileProgressStatus status;
1295 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, NULL);
1296 status = compute_dir_size (p, sm, &dir_count, ret_count, ret_total, compute_symlinks);
1297 vfs_path_free (p);
1299 if (status != FILE_CONT)
1300 return status;
1302 else
1304 (*ret_count)++;
1305 *ret_total += (uintmax_t) s->st_size;
1309 return FILE_CONT;
1312 /* --------------------------------------------------------------------------------------------- */
1314 /** Initialize variables for progress bars */
1315 static FileProgressStatus
1316 panel_operate_init_totals (const WPanel * panel, const char *source, file_op_context_t * ctx,
1317 filegui_dialog_type_t dialog_type)
1319 FileProgressStatus status;
1321 #ifdef ENABLE_BACKGROUND
1322 if (mc_global.we_are_background)
1323 return FILE_CONT;
1324 #endif
1326 if (verbose && file_op_compute_totals)
1328 dirsize_status_msg_t dsm;
1330 memset (&dsm, 0, sizeof (dsm));
1331 dsm.allow_skip = TRUE;
1332 status_msg_init (STATUS_MSG (&dsm), _("Directory scanning"), 0, dirsize_status_init_cb,
1333 dirsize_status_update_cb, dirsize_status_deinit_cb);
1335 ctx->progress_count = 0;
1336 ctx->progress_bytes = 0;
1338 if (source == NULL)
1339 status = panel_compute_totals (panel, &dsm, &ctx->progress_count, &ctx->progress_bytes,
1340 ctx->follow_links);
1341 else
1343 vfs_path_t *p;
1344 size_t dir_count = 0;
1346 p = vfs_path_from_str (source);
1347 status = compute_dir_size (p, &dsm, &dir_count, &ctx->progress_count,
1348 &ctx->progress_bytes, ctx->follow_links);
1349 vfs_path_free (p);
1352 status_msg_deinit (STATUS_MSG (&dsm));
1354 ctx->progress_totals_computed = (status == FILE_CONT);
1356 if (status == FILE_SKIP)
1357 status = FILE_CONT;
1359 else
1361 status = FILE_CONT;
1362 ctx->progress_count = panel->marked;
1363 ctx->progress_bytes = panel->total;
1364 ctx->progress_totals_computed = FALSE;
1367 file_op_context_create_ui (ctx, TRUE, dialog_type);
1369 return status;
1372 /* --------------------------------------------------------------------------------------------- */
1374 * Generate user prompt for panel operation.
1375 * src_stat must be not NULL for single source, and NULL for multiple sources
1378 static char *
1379 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1380 const struct stat *src_stat)
1382 char *sp;
1383 char *format_string;
1384 const char *cp;
1386 static gboolean i18n_flag = FALSE;
1387 if (!i18n_flag)
1389 size_t i;
1391 for (i = G_N_ELEMENTS (op_names1); i-- != 0;)
1392 op_names1[i] = Q_ (op_names1[i]);
1394 #ifdef ENABLE_NLS
1395 for (i = G_N_ELEMENTS (prompt_parts); i-- != 0;)
1396 prompt_parts[i] = _(prompt_parts[i]);
1398 one_format = _(one_format);
1399 many_format = _(many_format);
1400 #endif /* ENABLE_NLS */
1401 i18n_flag = TRUE;
1404 /* Possible prompts:
1405 * OP_COPY:
1406 * "Copy file \"%s\" with source mask:"
1407 * "Copy %d files with source mask:"
1408 * "Copy directory \"%s\" with source mask:"
1409 * "Copy %d directories with source mask:"
1410 * "Copy %d files/directories with source mask:"
1411 * OP_MOVE:
1412 * "Move file \"%s\" with source mask:"
1413 * "Move %d files with source mask:"
1414 * "Move directory \"%s\" with source mask:"
1415 * "Move %d directories with source mask:"
1416 * "Move %d files/directories with source mask:"
1417 * OP_DELETE:
1418 * "Delete file \"%s\"?"
1419 * "Delete %d files?"
1420 * "Delete directory \"%s\"?"
1421 * "Delete %d directories?"
1422 * "Delete %d files/directories?"
1425 sp = (char *) (src_stat != NULL ? one_format : many_format);
1427 /* 1. Substitute %o */
1428 format_string = str_replace_all (sp, "%o", op_names1[(int) operation]);
1430 /* 2. Substitute %n */
1431 cp = operation == OP_DELETE ? "\n" : " ";
1432 sp = format_string;
1433 format_string = str_replace_all (sp, "%n", cp);
1434 g_free (sp);
1436 /* 3. Substitute %f */
1437 if (src_stat != NULL)
1438 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1439 else if (panel->marked == panel->dirs_marked)
1440 cp = prompt_parts[3];
1441 else
1442 cp = panel->dirs_marked != 0 ? prompt_parts[4] : prompt_parts[1];
1444 sp = format_string;
1445 format_string = str_replace_all (sp, "%f", cp);
1446 g_free (sp);
1448 /* 4. Substitute %m */
1449 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1450 sp = format_string;
1451 format_string = str_replace_all (sp, "%m", cp);
1452 g_free (sp);
1454 return format_string;
1457 /* --------------------------------------------------------------------------------------------- */
1459 #ifdef ENABLE_BACKGROUND
1460 static int
1461 end_bg_process (file_op_context_t * ctx, enum OperationMode mode)
1463 int pid = ctx->pid;
1465 (void) mode;
1466 ctx->pid = 0;
1468 unregister_task_with_pid (pid);
1469 /* file_op_context_destroy(ctx); */
1470 return 1;
1472 #endif
1473 /* }}} */
1475 /* --------------------------------------------------------------------------------------------- */
1476 /*** public functions ****************************************************************************/
1477 /* --------------------------------------------------------------------------------------------- */
1479 FileProgressStatus
1480 copy_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx,
1481 const char *src_path, const char *dst_path)
1483 uid_t src_uid = (uid_t) (-1);
1484 gid_t src_gid = (gid_t) (-1);
1486 int src_desc, dest_desc = -1;
1487 int n_read, n_written;
1488 mode_t src_mode = 0; /* The mode of the source file */
1489 struct stat sb, sb2;
1490 struct utimbuf utb;
1491 gboolean dst_exists = FALSE, appending = FALSE;
1492 off_t file_size = -1;
1493 FileProgressStatus return_status, temp_status;
1494 struct timeval tv_transfer_start;
1495 dest_status_t dst_status = DEST_NONE;
1496 int open_flags;
1497 gboolean is_first_time = TRUE;
1498 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1500 /* FIXME: We should not be using global variables! */
1501 ctx->do_reget = 0;
1502 return_status = FILE_RETRY;
1504 dst_vpath = vfs_path_from_str (dst_path);
1505 src_vpath = vfs_path_from_str (src_path);
1507 file_progress_show_source (ctx, src_vpath);
1508 file_progress_show_target (ctx, dst_vpath);
1510 if (check_progress_buttons (ctx) == FILE_ABORT)
1512 return_status = FILE_ABORT;
1513 goto ret_fast;
1516 mc_refresh ();
1518 while (mc_stat (dst_vpath, &sb2) == 0)
1520 if (S_ISDIR (sb2.st_mode))
1522 if (ctx->skip_all)
1523 return_status = FILE_SKIPALL;
1524 else
1526 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1527 if (return_status == FILE_SKIPALL)
1528 ctx->skip_all = TRUE;
1529 if (return_status == FILE_RETRY)
1530 continue;
1532 goto ret_fast;
1535 dst_exists = TRUE;
1536 break;
1539 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1541 if (ctx->skip_all)
1542 return_status = FILE_SKIPALL;
1543 else
1545 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1546 if (return_status == FILE_SKIPALL)
1547 ctx->skip_all = TRUE;
1550 if (return_status != FILE_RETRY)
1551 goto ret_fast;
1554 if (dst_exists)
1556 /* Destination already exists */
1557 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1559 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1560 src_path, dst_path);
1561 goto ret_fast;
1564 /* Should we replace destination? */
1565 if (tctx->ask_overwrite)
1567 ctx->do_reget = 0;
1568 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1569 if (return_status != FILE_CONT)
1570 goto ret_fast;
1574 if (!ctx->do_append)
1576 /* Check the hardlinks */
1577 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &sb))
1579 /* We have made a hardlink - no more processing is necessary */
1580 return_status = FILE_CONT;
1581 goto ret_fast;
1584 if (S_ISLNK (sb.st_mode))
1586 return_status = make_symlink (ctx, src_path, dst_path);
1587 goto ret_fast;
1590 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1591 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1593 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1594 && !ctx->skip_all)
1596 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1597 if (return_status == FILE_RETRY)
1598 continue;
1599 if (return_status == FILE_SKIPALL)
1600 ctx->skip_all = TRUE;
1601 goto ret_fast;
1603 /* Success */
1605 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1606 && !ctx->skip_all)
1608 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1609 if (temp_status == FILE_SKIP)
1610 break;
1611 if (temp_status == FILE_SKIPALL)
1612 ctx->skip_all = TRUE;
1613 if (temp_status != FILE_RETRY)
1615 return_status = temp_status;
1616 goto ret_fast;
1620 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1621 && !ctx->skip_all)
1623 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1624 if (temp_status == FILE_SKIP)
1625 break;
1626 if (temp_status == FILE_SKIPALL)
1627 ctx->skip_all = TRUE;
1628 if (temp_status != FILE_RETRY)
1630 return_status = temp_status;
1631 goto ret_fast;
1635 return_status = FILE_CONT;
1636 goto ret_fast;
1640 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1642 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1644 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1645 if (return_status == FILE_RETRY)
1646 continue;
1647 if (return_status == FILE_SKIPALL)
1648 ctx->skip_all = TRUE;
1649 if (return_status == FILE_SKIP)
1650 break;
1651 ctx->do_append = 0;
1652 goto ret_fast;
1655 if (ctx->do_reget != 0)
1657 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1659 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1660 ctx->do_reget = 0;
1661 ctx->do_append = FALSE;
1665 while (mc_fstat (src_desc, &sb) != 0)
1667 if (ctx->skip_all)
1668 return_status = FILE_SKIPALL;
1669 else
1671 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1672 if (return_status == FILE_RETRY)
1673 continue;
1674 if (return_status == FILE_SKIPALL)
1675 ctx->skip_all = TRUE;
1676 ctx->do_append = FALSE;
1678 goto ret;
1681 src_mode = sb.st_mode;
1682 src_uid = sb.st_uid;
1683 src_gid = sb.st_gid;
1684 utb.actime = sb.st_atime;
1685 utb.modtime = sb.st_mtime;
1686 file_size = sb.st_size;
1688 open_flags = O_WRONLY;
1689 if (dst_exists)
1691 if (ctx->do_append != 0)
1692 open_flags |= O_APPEND;
1693 else
1694 open_flags |= O_CREAT | O_TRUNC;
1696 else
1698 open_flags |= O_CREAT | O_EXCL;
1701 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
1703 if (errno != EEXIST)
1705 if (ctx->skip_all)
1706 return_status = FILE_SKIPALL;
1707 else
1709 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1710 if (return_status == FILE_RETRY)
1711 continue;
1712 if (return_status == FILE_SKIPALL)
1713 ctx->skip_all = TRUE;
1714 ctx->do_append = FALSE;
1717 goto ret;
1719 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1721 appending = ctx->do_append;
1722 ctx->do_append = FALSE;
1724 /* Find out the optimal buffer size. */
1725 while (mc_fstat (dest_desc, &sb) != 0)
1727 if (ctx->skip_all)
1728 return_status = FILE_SKIPALL;
1729 else
1731 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1732 if (return_status == FILE_RETRY)
1733 continue;
1734 if (return_status == FILE_SKIPALL)
1735 ctx->skip_all = TRUE;
1737 goto ret;
1740 /* try preallocate space; if fail, try copy anyway */
1741 while (vfs_preallocate (dest_desc, file_size, appending ? sb.st_size : 0) != 0)
1743 if (ctx->skip_all)
1745 /* cannot allocate, start the file copying anyway */
1746 return_status = FILE_CONT;
1747 break;
1750 return_status =
1751 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1753 if (return_status == FILE_SKIPALL)
1754 ctx->skip_all = TRUE;
1756 if (ctx->skip_all || return_status == FILE_SKIP)
1758 /* skip the space allocation error, start file copying */
1759 return_status = FILE_CONT;
1760 break;
1763 if (return_status == FILE_ABORT)
1765 mc_close (dest_desc);
1766 dest_desc = -1;
1767 mc_unlink (dst_vpath);
1768 dst_status = DEST_NONE;
1769 goto ret;
1772 /* return_status == FILE_RETRY -- try allocate space again */
1775 ctx->eta_secs = 0.0;
1776 ctx->bps = 0;
1778 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1779 file_progress_show (ctx, 0, file_size, "", TRUE);
1780 else
1781 file_progress_show (ctx, 1, 1, "", TRUE);
1782 return_status = check_progress_buttons (ctx);
1783 mc_refresh ();
1785 if (return_status != FILE_CONT)
1786 goto ret;
1789 off_t n_read_total = 0;
1790 struct timeval tv_current, tv_last_update, tv_last_input;
1791 int secs, update_secs;
1792 const char *stalled_msg = "";
1794 tv_last_update = tv_transfer_start;
1796 while (TRUE)
1798 char buf[BUF_8K];
1800 /* src_read */
1801 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1802 n_read = -1;
1803 else
1804 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1806 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1807 if (return_status == FILE_RETRY)
1808 continue;
1809 if (return_status == FILE_SKIPALL)
1810 ctx->skip_all = TRUE;
1811 goto ret;
1813 if (n_read == 0)
1814 break;
1816 gettimeofday (&tv_current, NULL);
1818 if (n_read > 0)
1820 char *t = buf;
1821 n_read_total += n_read;
1823 /* Windows NT ftp servers report that files have no
1824 * permissions: -------, so if we happen to have actually
1825 * read something, we should fix the permissions.
1827 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1828 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1829 gettimeofday (&tv_last_input, NULL);
1831 /* dst_write */
1832 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read)
1834 gboolean write_errno_nospace;
1836 if (n_written > 0)
1838 n_read -= n_written;
1839 t += n_written;
1840 continue;
1843 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
1845 if (ctx->skip_all)
1846 return_status = FILE_SKIPALL;
1847 else
1848 return_status =
1849 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1851 if (return_status == FILE_SKIP)
1853 if (write_errno_nospace)
1854 goto ret;
1855 break;
1857 if (return_status == FILE_SKIPALL)
1859 ctx->skip_all = TRUE;
1860 if (write_errno_nospace)
1861 goto ret;
1863 if (return_status != FILE_RETRY)
1864 goto ret;
1868 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1870 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1871 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1873 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1875 copy_file_file_display_progress (tctx, ctx,
1876 tv_current,
1877 tv_transfer_start, file_size, n_read_total);
1878 tv_last_update = tv_current;
1880 is_first_time = FALSE;
1882 if (update_secs > FILEOP_STALLING_INTERVAL)
1884 stalled_msg = _("(stalled)");
1888 gboolean force_update;
1890 force_update =
1891 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1893 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1895 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1896 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1899 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1900 force_update);
1902 mc_refresh ();
1904 return_status = check_progress_buttons (ctx);
1906 if (return_status != FILE_CONT)
1908 mc_refresh ();
1909 goto ret;
1914 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1916 ret:
1917 rotate_dash (FALSE);
1918 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1920 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1921 if (temp_status == FILE_RETRY)
1922 continue;
1923 if (temp_status == FILE_ABORT)
1924 return_status = temp_status;
1925 if (temp_status == FILE_SKIPALL)
1926 ctx->skip_all = TRUE;
1927 break;
1930 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1932 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1933 if (temp_status == FILE_RETRY)
1934 continue;
1935 if (temp_status == FILE_SKIPALL)
1936 ctx->skip_all = TRUE;
1937 return_status = temp_status;
1938 break;
1941 if (dst_status == DEST_SHORT)
1943 /* Query to remove short file */
1944 if (query_dialog (Q_ ("DialogTitle|Copy"), _("Incomplete file was retrieved. Keep it?"),
1945 D_ERROR, 2, _("&Delete"), _("&Keep")) == 0)
1946 mc_unlink (dst_vpath);
1948 else if (dst_status == DEST_FULL)
1950 /* Copy has succeeded */
1951 if (!appending && ctx->preserve_uidgid)
1953 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1955 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1956 if (temp_status == FILE_RETRY)
1957 continue;
1958 if (temp_status == FILE_SKIPALL)
1960 ctx->skip_all = TRUE;
1961 return_status = FILE_CONT;
1963 if (temp_status == FILE_SKIP)
1964 return_status = FILE_CONT;
1965 break;
1969 if (!appending)
1971 if (ctx->preserve)
1973 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1975 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1976 if (temp_status == FILE_RETRY)
1977 continue;
1978 if (temp_status == FILE_SKIPALL)
1980 ctx->skip_all = TRUE;
1981 return_status = FILE_CONT;
1983 if (temp_status == FILE_SKIP)
1984 return_status = FILE_CONT;
1985 break;
1988 else if (!dst_exists)
1990 src_mode = umask (-1);
1991 umask (src_mode);
1992 src_mode = 0100666 & ~src_mode;
1993 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
1995 mc_utime (dst_vpath, &utb);
1999 if (return_status == FILE_CONT)
2000 return_status = progress_update_one (tctx, ctx, file_size);
2002 ret_fast:
2003 vfs_path_free (src_vpath);
2004 vfs_path_free (dst_vpath);
2005 return return_status;
2008 /* --------------------------------------------------------------------------------------------- */
2010 * I think these copy_*_* functions should have a return type.
2011 * anyway, this function *must* have two directories as arguments.
2013 /* FIXME: This function needs to check the return values of the
2014 function calls */
2016 FileProgressStatus
2017 copy_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d,
2018 gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs)
2020 struct dirent *next;
2021 struct stat buf, cbuf;
2022 DIR *reading;
2023 FileProgressStatus return_status = FILE_CONT;
2024 struct link *lp;
2025 vfs_path_t *src_vpath, *dst_vpath;
2026 gboolean do_mkdir = TRUE;
2028 src_vpath = vfs_path_from_str (s);
2029 dst_vpath = vfs_path_from_str (d);
2031 /* First get the mode of the source dir */
2033 retry_src_stat:
2034 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
2036 if (ctx->skip_all)
2037 return_status = FILE_SKIPALL;
2038 else
2040 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
2041 if (return_status == FILE_RETRY)
2042 goto retry_src_stat;
2043 if (return_status == FILE_SKIPALL)
2044 ctx->skip_all = TRUE;
2046 goto ret_fast;
2049 if (is_in_linklist (dest_dirs, src_vpath, &cbuf))
2051 /* Don't copy a directory we created before (we don't want to copy
2052 infinitely if a directory is copied into itself) */
2053 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2054 return_status = FILE_CONT;
2055 goto ret_fast;
2058 /* Hmm, hardlink to directory??? - Norbert */
2059 /* FIXME: In this step we should do something
2060 in case the destination already exist */
2061 /* Check the hardlinks */
2062 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &cbuf))
2064 /* We have made a hardlink - no more processing is necessary */
2065 goto ret_fast;
2068 if (!S_ISDIR (cbuf.st_mode))
2070 if (ctx->skip_all)
2071 return_status = FILE_SKIPALL;
2072 else
2074 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
2075 if (return_status == FILE_RETRY)
2076 goto retry_src_stat;
2077 if (return_status == FILE_SKIPALL)
2078 ctx->skip_all = TRUE;
2080 goto ret_fast;
2083 if (is_in_linklist (parent_dirs, src_vpath, &cbuf))
2085 /* we found a cyclic symbolic link */
2086 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
2087 return_status = FILE_SKIP;
2088 goto ret_fast;
2091 lp = g_new0 (struct link, 1);
2092 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
2093 lp->ino = cbuf.st_ino;
2094 lp->dev = cbuf.st_dev;
2095 parent_dirs = g_slist_prepend (parent_dirs, lp);
2097 retry_dst_stat:
2098 /* Now, check if the dest dir exists, if not, create it. */
2099 if (mc_stat (dst_vpath, &buf) != 0)
2101 /* Here the dir doesn't exist : make it ! */
2102 if (move_over && mc_rename (src_vpath, dst_vpath) == 0)
2104 return_status = FILE_CONT;
2105 goto ret;
2108 else
2111 * If the destination directory exists, we want to copy the whole
2112 * directory, but we only want this to happen once.
2114 * Escape sequences added to the * to compiler warnings.
2115 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2116 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2118 if (!S_ISDIR (buf.st_mode))
2120 if (ctx->skip_all)
2121 return_status = FILE_SKIPALL;
2122 else
2124 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2125 if (return_status == FILE_SKIPALL)
2126 ctx->skip_all = TRUE;
2127 if (return_status == FILE_RETRY)
2128 goto retry_dst_stat;
2130 goto ret;
2132 /* Dive into subdir if exists */
2133 if (toplevel && ctx->dive_into_subdirs)
2135 vfs_path_t *tmp;
2137 tmp = dst_vpath;
2138 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), NULL);
2139 vfs_path_free (tmp);
2142 else
2143 do_mkdir = FALSE;
2146 d = vfs_path_as_str (dst_vpath);
2148 if (do_mkdir)
2150 while (my_mkdir (dst_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2152 if (ctx->skip_all)
2153 return_status = FILE_SKIPALL;
2154 else
2156 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), d);
2157 if (return_status == FILE_SKIPALL)
2158 ctx->skip_all = TRUE;
2160 if (return_status != FILE_RETRY)
2161 goto ret;
2164 lp = g_new0 (struct link, 1);
2165 mc_stat (dst_vpath, &buf);
2166 lp->vfs = vfs_path_get_by_index (dst_vpath, -1)->class;
2167 lp->ino = buf.st_ino;
2168 lp->dev = buf.st_dev;
2169 dest_dirs = g_slist_prepend (dest_dirs, lp);
2172 if (ctx->preserve_uidgid)
2174 while (mc_chown (dst_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2176 if (ctx->skip_all)
2177 return_status = FILE_SKIPALL;
2178 else
2180 return_status = file_error (_("Cannot chown target directory \"%s\"\n%s"), d);
2181 if (return_status == FILE_SKIPALL)
2182 ctx->skip_all = TRUE;
2184 if (return_status != FILE_RETRY)
2185 goto ret;
2189 /* open the source dir for reading */
2190 reading = mc_opendir (src_vpath);
2191 if (reading == NULL)
2192 goto ret;
2194 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2196 char *path;
2197 vfs_path_t *tmp_vpath;
2200 * Now, we don't want '.' and '..' to be created / copied at any time
2202 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
2203 continue;
2205 /* get the filename and add it to the src directory */
2206 path = mc_build_filename (s, next->d_name, NULL);
2207 tmp_vpath = vfs_path_from_str (path);
2209 (*ctx->stat_func) (tmp_vpath, &buf);
2210 if (S_ISDIR (buf.st_mode))
2212 char *mdpath;
2214 mdpath = mc_build_filename (d, next->d_name, NULL);
2216 * From here, we just intend to recursively copy subdirs, not
2217 * the double functionality of copying different when the target
2218 * dir already exists. So, we give the recursive call the flag 0
2219 * meaning no toplevel.
2221 return_status =
2222 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2223 g_free (mdpath);
2225 else
2227 char *dest_file;
2229 dest_file = mc_build_filename (d, x_basename (path), NULL);
2230 return_status = copy_file_file (tctx, ctx, path, dest_file);
2231 g_free (dest_file);
2234 g_free (path);
2236 if (do_delete && return_status == FILE_CONT)
2238 if (ctx->erase_at_end)
2240 lp = g_new0 (struct link, 1);
2241 lp->src_vpath = tmp_vpath;
2242 lp->st_mode = buf.st_mode;
2243 erase_list = g_slist_append (erase_list, lp);
2244 tmp_vpath = NULL;
2246 else if (S_ISDIR (buf.st_mode))
2247 return_status = erase_dir_iff_empty (ctx, tmp_vpath, tctx->progress_count);
2248 else
2249 return_status = erase_file (tctx, ctx, tmp_vpath);
2251 vfs_path_free (tmp_vpath);
2253 mc_closedir (reading);
2255 if (ctx->preserve)
2257 struct utimbuf utb;
2259 mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill);
2260 utb.actime = cbuf.st_atime;
2261 utb.modtime = cbuf.st_mtime;
2262 mc_utime (dst_vpath, &utb);
2264 else
2266 cbuf.st_mode = umask (-1);
2267 umask (cbuf.st_mode);
2268 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2269 mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill);
2272 ret:
2273 free_link (parent_dirs->data);
2274 g_slist_free_1 (parent_dirs);
2275 ret_fast:
2276 vfs_path_free (src_vpath);
2277 vfs_path_free (dst_vpath);
2278 return return_status;
2281 /* }}} */
2283 /* --------------------------------------------------------------------------------------------- */
2284 /* {{{ Move routines */
2286 FileProgressStatus
2287 move_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d)
2289 struct stat sbuf, dbuf;
2290 FileProgressStatus return_status = FILE_CONT;
2291 gboolean move_over = FALSE;
2292 gboolean dstat_ok;
2293 vfs_path_t *src_vpath, *dst_vpath;
2295 src_vpath = vfs_path_from_str (s);
2296 dst_vpath = vfs_path_from_str (d);
2298 file_progress_show_source (ctx, src_vpath);
2299 file_progress_show_target (ctx, dst_vpath);
2301 if (check_progress_buttons (ctx) == FILE_ABORT)
2303 return_status = FILE_ABORT;
2304 goto ret_fast;
2307 mc_refresh ();
2309 mc_stat (src_vpath, &sbuf);
2311 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2312 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2314 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2315 goto ret_fast;
2318 if (!dstat_ok)
2319 ; /* destination doesn't exist */
2320 else if (!ctx->dive_into_subdirs)
2321 move_over = TRUE;
2322 else
2324 vfs_path_t *tmp;
2326 tmp = dst_vpath;
2327 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), NULL);
2328 vfs_path_free (tmp);
2331 d = vfs_path_as_str (dst_vpath);
2333 /* Check if the user inputted an existing dir */
2334 retry_dst_stat:
2335 if (mc_stat (dst_vpath, &dbuf) == 0)
2337 if (move_over)
2339 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, TRUE, TRUE, NULL);
2341 if (return_status != FILE_CONT)
2342 goto ret;
2343 goto oktoret;
2345 else if (ctx->skip_all)
2346 return_status = FILE_SKIPALL;
2347 else
2349 if (S_ISDIR (dbuf.st_mode))
2350 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), d);
2351 else
2352 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), d);
2353 if (return_status == FILE_SKIPALL)
2354 ctx->skip_all = TRUE;
2355 if (return_status == FILE_RETRY)
2356 goto retry_dst_stat;
2359 goto ret_fast;
2362 retry_rename:
2363 if (mc_rename (src_vpath, dst_vpath) == 0)
2365 return_status = FILE_CONT;
2366 goto ret;
2369 if (errno != EXDEV)
2371 if (!ctx->skip_all)
2373 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2374 if (return_status == FILE_SKIPALL)
2375 ctx->skip_all = TRUE;
2376 if (return_status == FILE_RETRY)
2377 goto retry_rename;
2379 goto ret;
2381 /* Failed because of filesystem boundary -> copy dir instead */
2382 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, FALSE, TRUE, NULL);
2384 if (return_status != FILE_CONT)
2385 goto ret;
2386 oktoret:
2387 file_progress_show_source (ctx, NULL);
2388 file_progress_show_target (ctx, NULL);
2389 file_progress_show (ctx, 0, 0, "", FALSE);
2391 return_status = check_progress_buttons (ctx);
2392 if (return_status != FILE_CONT)
2393 goto ret;
2395 mc_refresh ();
2396 if (ctx->erase_at_end)
2398 /* Reset progress count before delete to avoid counting files twice */
2399 tctx->progress_count = tctx->prev_progress_count;
2401 while (erase_list != NULL && return_status != FILE_ABORT)
2403 struct link *lp = (struct link *) erase_list->data;
2405 if (S_ISDIR (lp->st_mode))
2406 return_status = erase_dir_iff_empty (ctx, lp->src_vpath, tctx->progress_count);
2407 else
2408 return_status = erase_file (tctx, ctx, lp->src_vpath);
2410 erase_list = g_slist_remove (erase_list, lp);
2411 free_link (lp);
2414 /* Save progress counter before move next directory */
2415 tctx->prev_progress_count = tctx->progress_count;
2417 erase_dir_iff_empty (ctx, src_vpath, tctx->progress_count);
2419 ret:
2420 erase_list = free_linklist (erase_list);
2421 ret_fast:
2422 vfs_path_free (src_vpath);
2423 vfs_path_free (dst_vpath);
2424 return return_status;
2427 /* }}} */
2429 /* --------------------------------------------------------------------------------------------- */
2430 /* {{{ Erase routines */
2432 FileProgressStatus
2433 erase_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * s_vpath)
2435 FileProgressStatus error;
2437 file_progress_show_deleting (ctx, vfs_path_as_str (s_vpath), NULL);
2438 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2439 if (check_progress_buttons (ctx) == FILE_ABORT)
2440 return FILE_ABORT;
2442 mc_refresh ();
2444 /* The old way to detect a non empty directory was:
2445 error = my_rmdir (s);
2446 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2447 For the linux user space nfs server (nfs-server-2.2beta29-2)
2448 we would have to check also for EIO. I hope the new way is
2449 fool proof. (Norbert)
2451 error = check_dir_is_empty (s_vpath);
2452 if (error == 0)
2453 { /* not empty */
2454 error = query_recursive (ctx, vfs_path_as_str (s_vpath));
2455 if (error == FILE_CONT)
2456 error = recursive_erase (tctx, ctx, s_vpath);
2457 return error;
2460 while (my_rmdir (vfs_path_as_str (s_vpath)) == -1 && !ctx->skip_all)
2462 error = file_error (_("Cannot remove directory \"%s\"\n%s"), vfs_path_as_str (s_vpath));
2463 if (error != FILE_RETRY)
2464 return error;
2467 return FILE_CONT;
2470 /* }}} */
2472 /* --------------------------------------------------------------------------------------------- */
2473 /* {{{ Panel operate routines */
2475 void
2476 dirsize_status_init_cb (status_msg_t * sm)
2478 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
2479 Widget *wd = WIDGET (sm->dlg);
2481 const char *b1_name = N_("&Abort");
2482 const char *b2_name = N_("&Skip");
2483 int b_width, ui_width;
2485 #ifdef ENABLE_NLS
2486 b1_name = _(b1_name);
2487 b2_name = _(b2_name);
2488 #endif
2490 b_width = str_term_width1 (b1_name) + 4;
2491 if (dsm->allow_skip)
2492 b_width += str_term_width1 (b2_name) + 4 + 1;
2494 ui_width = max (COLS / 2, b_width + 6);
2495 dsm->dirname = label_new (2, 3, "");
2496 add_widget (sm->dlg, dsm->dirname);
2497 dsm->count_size = label_new (3, 3, "");
2498 add_widget (sm->dlg, dsm->count_size);
2499 add_widget (sm->dlg, hline_new (4, -1, -1));
2501 dsm->abort_button = WIDGET (button_new (5, 3, FILE_ABORT, NORMAL_BUTTON, b1_name, NULL));
2502 add_widget (sm->dlg, dsm->abort_button);
2503 if (dsm->allow_skip)
2505 dsm->skip_button = WIDGET (button_new (5, 3, FILE_SKIP, NORMAL_BUTTON, b2_name, NULL));
2506 add_widget (sm->dlg, dsm->skip_button);
2507 dlg_select_widget (dsm->skip_button);
2510 widget_set_size (wd, wd->y, wd->x, 8, ui_width);
2511 dirsize_status_locate_buttons (dsm);
2514 /* --------------------------------------------------------------------------------------------- */
2517 dirsize_status_update_cb (status_msg_t * sm)
2519 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
2520 Widget *wd = WIDGET (sm->dlg);
2522 /* update second (longer label) */
2523 label_set_textv (dsm->count_size, _("Directories: %zd, total size: %s"),
2524 dsm->dir_count, size_trunc_sep (dsm->total_size, panels_options.kilobyte_si));
2526 /* enlarge dialog if required */
2527 if (WIDGET (dsm->count_size)->cols + 6 > wd->cols)
2529 dlg_set_size (sm->dlg, wd->lines, WIDGET (dsm->count_size)->cols + 6);
2530 dirsize_status_locate_buttons (dsm);
2531 dlg_redraw (sm->dlg);
2534 /* adjust first label */
2535 label_set_text (dsm->dirname, str_trunc (vfs_path_as_str (dsm->dirname_vpath), wd->cols - 6));
2537 switch (status_msg_common_update (sm))
2539 case B_CANCEL:
2540 case FILE_ABORT:
2541 return FILE_ABORT;
2542 case FILE_SKIP:
2543 return FILE_SKIP;
2544 default:
2545 return FILE_CONT;
2549 /* --------------------------------------------------------------------------------------------- */
2551 void
2552 dirsize_status_deinit_cb (status_msg_t * sm)
2554 (void) sm;
2556 /* schedule to update passive panel */
2557 if (get_other_type () == view_listing)
2558 other_panel->dirty = 1;
2561 /* --------------------------------------------------------------------------------------------- */
2563 * compute_dir_size:
2565 * Computes the number of bytes used by the files in a directory
2568 FileProgressStatus
2569 compute_dir_size (const vfs_path_t * dirname_vpath, dirsize_status_msg_t * sm,
2570 size_t * ret_dir_count, size_t * ret_marked_count, uintmax_t * ret_total,
2571 gboolean compute_symlinks)
2573 return do_compute_dir_size (dirname_vpath, sm, ret_dir_count, ret_marked_count, ret_total,
2574 compute_symlinks);
2577 /* --------------------------------------------------------------------------------------------- */
2579 * panel_operate:
2581 * Performs one of the operations on the selection on the source_panel
2582 * (copy, delete, move).
2584 * Returns TRUE if did change the directory
2585 * structure, Returns FALSE if user aborted
2587 * force_single forces operation on the current entry and affects
2588 * default destination. Current filename is used as default.
2591 gboolean
2592 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2594 WPanel *panel = PANEL (source_panel);
2595 const gboolean single_entry = force_single || (panel->marked <= 1)
2596 || (get_current_type () == view_tree);
2598 char *source = NULL;
2599 #ifdef WITH_FULL_PATHS
2600 vfs_path_t *source_with_vpath = NULL;
2601 #else
2602 #define source_with_path source
2603 #endif /* !WITH_FULL_PATHS */
2604 char *dest = NULL;
2605 vfs_path_t *dest_vpath = NULL;
2606 char *temp = NULL;
2607 char *save_cwd = NULL, *save_dest = NULL;
2608 struct stat src_stat;
2609 gboolean ret_val = TRUE;
2610 int i;
2611 FileProgressStatus value;
2612 file_op_context_t *ctx;
2613 file_op_total_context_t *tctx;
2614 vfs_path_t *tmp_vpath;
2615 filegui_dialog_type_t dialog_type = FILEGUI_DIALOG_ONE_ITEM;
2617 gboolean do_bg = FALSE; /* do background operation? */
2619 static gboolean i18n_flag = FALSE;
2620 if (!i18n_flag)
2622 for (i = G_N_ELEMENTS (op_names); i-- != 0;)
2623 op_names[i] = Q_ (op_names[i]);
2624 i18n_flag = TRUE;
2627 linklist = free_linklist (linklist);
2628 dest_dirs = free_linklist (dest_dirs);
2630 if (single_entry)
2632 vfs_path_t *source_vpath;
2634 if (force_single)
2635 source = g_strdup (selection (panel)->fname);
2636 else
2637 source = panel_get_file (panel);
2639 if (DIR_IS_DOTDOT (source))
2641 g_free (source);
2642 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2643 return FALSE;
2646 source_vpath = vfs_path_from_str (source);
2647 /* Update stat to get actual info */
2648 if (mc_lstat (source_vpath, &src_stat) != 0)
2650 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2651 path_trunc (source, 30), unix_error_string (errno));
2653 /* Directory was changed outside MC. Reload it forced */
2654 if (!panel->is_panelized)
2656 panel_update_flags_t flags = UP_RELOAD;
2658 /* don't update panelized panel */
2659 if (get_other_type () == view_listing && other_panel->is_panelized)
2660 flags |= UP_ONLY_CURRENT;
2662 update_panels (flags, UP_KEEPSEL);
2664 vfs_path_free (source_vpath);
2665 return FALSE;
2667 vfs_path_free (source_vpath);
2670 ctx = file_op_context_new (operation);
2672 /* Show confirmation dialog */
2673 if (operation != OP_DELETE)
2675 char *tmp_dest_dir, *dest_dir;
2676 char *format;
2678 /* Forced single operations default to the original name */
2679 if (force_single)
2680 tmp_dest_dir = g_strdup (source);
2681 else if (get_other_type () == view_listing)
2682 tmp_dest_dir = g_strdup (vfs_path_as_str (other_panel->cwd_vpath));
2683 else
2684 tmp_dest_dir = g_strdup (vfs_path_as_str (panel->cwd_vpath));
2686 * Add trailing backslash only when do non-local ops.
2687 * It saves user from occasional file renames (when destination
2688 * dir is deleted)
2690 if (!force_single && tmp_dest_dir[0] != '\0'
2691 && !IS_PATH_SEP (tmp_dest_dir[strlen (tmp_dest_dir) - 1]))
2693 /* add trailing separator */
2694 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
2695 g_free (tmp_dest_dir);
2697 else
2699 /* just copy */
2700 dest_dir = tmp_dest_dir;
2702 if (dest_dir == NULL)
2704 ret_val = FALSE;
2705 goto ret_fast;
2708 /* Generate confirmation prompt */
2709 format =
2710 panel_operate_generate_prompt (panel, operation, source != NULL ? &src_stat : NULL);
2712 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2713 source != NULL ? (void *) source
2714 : (void *) &panel->marked, dest_dir, &do_bg);
2716 g_free (format);
2717 g_free (dest_dir);
2719 if (dest == NULL || dest[0] == '\0')
2721 g_free (dest);
2722 ret_val = FALSE;
2723 goto ret_fast;
2725 dest_vpath = vfs_path_from_str (dest);
2727 else if (confirm_delete)
2729 char *format;
2730 char fmd_buf[BUF_MEDIUM];
2732 /* Generate confirmation prompt */
2733 format =
2734 panel_operate_generate_prompt (panel, OP_DELETE, source != NULL ? &src_stat : NULL);
2736 if (source == NULL)
2737 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2738 else
2740 const int fmd_xlen = 64;
2741 i = fmd_xlen - str_term_width1 (format) - 4;
2742 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2745 g_free (format);
2747 if (safe_delete)
2748 query_set_sel (1);
2750 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2752 if (i != 0)
2754 ret_val = FALSE;
2755 goto ret_fast;
2759 tctx = file_op_total_context_new ();
2760 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2762 #ifdef ENABLE_BACKGROUND
2763 /* Did the user select to do a background operation? */
2764 if (do_bg)
2766 int v;
2768 v = do_background (ctx,
2769 g_strconcat (op_names[operation], ": ",
2770 vfs_path_as_str (panel->cwd_vpath), (char *) NULL));
2771 if (v == -1)
2772 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2774 /* If we are the parent */
2775 if (v == 1)
2777 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
2779 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
2780 vfs_path_free (dest_vpath);
2781 g_free (dest);
2782 /* file_op_context_destroy (ctx); */
2783 return FALSE;
2786 else
2787 #endif /* ENABLE_BACKGROUND */
2789 if (operation == OP_DELETE)
2790 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2791 else if (single_entry && S_ISDIR (selection (panel)->st.st_mode))
2792 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2793 else if (single_entry || force_single)
2794 dialog_type = FILEGUI_DIALOG_ONE_ITEM;
2795 else
2796 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2799 /* Initialize things */
2800 /* We do not want to trash cache every time file is
2801 created/touched. However, this will make our cache contain
2802 invalid data. */
2803 if ((dest != NULL)
2804 && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
2805 save_dest = g_strdup (dest);
2807 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
2808 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
2809 save_cwd = g_strdup (vfs_path_as_str (panel->cwd_vpath));
2811 /* Now, let's do the job */
2813 /* This code is only called by the tree and panel code */
2814 if (single_entry)
2816 /* We now have ETA in all cases */
2818 /* One file: FIXME mc_chdir will take user out of any vfs */
2819 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2821 vfs_path_t *vpath;
2822 int chdir_retcode;
2824 vpath = vfs_path_from_str (PATH_SEP_STR);
2825 chdir_retcode = mc_chdir (vpath);
2826 vfs_path_free (vpath);
2827 if (chdir_retcode < 0)
2829 ret_val = FALSE;
2830 goto clean_up;
2834 /* The source and src_stat variables have been initialized before */
2835 #ifdef WITH_FULL_PATHS
2836 if (g_path_is_absolute (source))
2837 source_with_vpath = vfs_path_from_str (source);
2838 else
2839 source_with_vpath = vfs_path_append_new (panel->cwd_vpath, source, (char *) NULL);
2840 #endif /* WITH_FULL_PATHS */
2841 if (panel_operate_init_totals (panel, vfs_path_as_str (source_with_vpath), ctx, dialog_type)
2842 == FILE_CONT)
2844 if (operation == OP_DELETE)
2846 if (S_ISDIR (src_stat.st_mode))
2847 value = erase_dir (tctx, ctx, source_with_vpath);
2848 else
2849 value = erase_file (tctx, ctx, source_with_vpath);
2851 else
2853 temp = transform_source (ctx, source_with_vpath);
2854 if (temp == NULL)
2855 value = transform_error;
2856 else
2858 char *repl_dest, *temp2;
2860 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2861 if (ctx->search_handle->error != MC_SEARCH_E_OK)
2863 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
2864 g_free (repl_dest);
2865 goto clean_up;
2868 temp2 = mc_build_filename (repl_dest, temp, NULL);
2869 g_free (temp);
2870 g_free (repl_dest);
2871 g_free (dest);
2872 vfs_path_free (dest_vpath);
2873 dest = temp2;
2874 dest_vpath = vfs_path_from_str (dest);
2876 switch (operation)
2878 case OP_COPY:
2879 /* we use file_mask_op_follow_links only with OP_COPY */
2880 ctx->stat_func (source_with_vpath, &src_stat);
2882 if (S_ISDIR (src_stat.st_mode))
2883 value =
2884 copy_dir_dir (tctx, ctx, vfs_path_as_str (source_with_vpath),
2885 dest, TRUE, FALSE, FALSE, NULL);
2886 else
2887 value =
2888 copy_file_file (tctx, ctx, vfs_path_as_str (source_with_vpath),
2889 dest);
2890 break;
2892 case OP_MOVE:
2893 if (S_ISDIR (src_stat.st_mode))
2894 value =
2895 move_dir_dir (tctx, ctx, vfs_path_as_str (source_with_vpath), dest);
2896 else
2897 value =
2898 move_file_file (tctx, ctx, vfs_path_as_str (source_with_vpath),
2899 dest);
2900 break;
2902 default:
2903 /* Unknown file operation */
2904 abort ();
2907 } /* Copy or move operation */
2909 if ((value == FILE_CONT) && !force_single)
2910 unmark_files (panel);
2913 else
2915 /* Many files */
2917 /* Check destination for copy or move operation */
2918 while (operation != OP_DELETE)
2920 int dst_result;
2921 struct stat dst_stat;
2923 dst_result = mc_stat (dest_vpath, &dst_stat);
2925 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2926 break;
2928 if (ctx->skip_all
2929 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2930 goto clean_up;
2933 if (panel_operate_init_totals (panel, NULL, ctx, dialog_type) == FILE_CONT)
2935 /* Loop for every file, perform the actual copy operation */
2936 for (i = 0; i < panel->dir.len; i++)
2938 const char *source2;
2940 if (!panel->dir.list[i].f.marked)
2941 continue; /* Skip the unmarked ones */
2943 source2 = panel->dir.list[i].fname;
2944 src_stat = panel->dir.list[i].st;
2946 #ifdef WITH_FULL_PATHS
2947 vfs_path_free (source_with_vpath);
2948 if (g_path_is_absolute (source2))
2949 source_with_vpath = vfs_path_from_str (source2);
2950 else
2951 source_with_vpath =
2952 vfs_path_append_new (panel->cwd_vpath, source2, (char *) NULL);
2953 #endif /* WITH_FULL_PATHS */
2955 if (operation == OP_DELETE)
2957 if (S_ISDIR (src_stat.st_mode))
2958 value = erase_dir (tctx, ctx, source_with_vpath);
2959 else
2960 value = erase_file (tctx, ctx, source_with_vpath);
2962 else
2964 temp = transform_source (ctx, source_with_vpath);
2965 if (temp == NULL)
2966 value = transform_error;
2967 else
2969 char *temp2, *repl_dest, *source_with_path_str;
2971 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2972 if (ctx->search_handle->error != MC_SEARCH_E_OK)
2974 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
2975 g_free (repl_dest);
2976 goto clean_up;
2979 temp2 = mc_build_filename (repl_dest, temp, NULL);
2980 g_free (temp);
2981 g_free (repl_dest);
2982 source_with_path_str =
2983 strutils_shell_unescape (vfs_path_as_str (source_with_vpath));
2984 temp = strutils_shell_unescape (temp2);
2985 g_free (temp2);
2987 switch (operation)
2989 case OP_COPY:
2990 /* we use file_mask_op_follow_links only with OP_COPY */
2992 vfs_path_t *vpath;
2994 vpath = vfs_path_from_str (source_with_path_str);
2995 ctx->stat_func (vpath, &src_stat);
2996 vfs_path_free (vpath);
2998 if (S_ISDIR (src_stat.st_mode))
2999 value = copy_dir_dir (tctx, ctx, source_with_path_str, temp,
3000 TRUE, FALSE, FALSE, NULL);
3001 else
3002 value = copy_file_file (tctx, ctx, source_with_path_str, temp);
3003 dest_dirs = free_linklist (dest_dirs);
3004 break;
3006 case OP_MOVE:
3007 if (S_ISDIR (src_stat.st_mode))
3008 value = move_dir_dir (tctx, ctx, source_with_path_str, temp);
3009 else
3010 value = move_file_file (tctx, ctx, source_with_path_str, temp);
3011 break;
3013 default:
3014 /* Unknown file operation */
3015 abort ();
3018 g_free (source_with_path_str);
3019 g_free (temp);
3021 } /* Copy or move operation */
3023 if (value == FILE_ABORT)
3024 break;
3026 if (value == FILE_CONT)
3027 do_file_mark (panel, i, 0);
3029 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3031 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3032 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3035 if (operation != OP_DELETE)
3036 file_progress_show (ctx, 0, 0, "", FALSE);
3038 if (check_progress_buttons (ctx) == FILE_ABORT)
3039 break;
3041 mc_refresh ();
3042 } /* Loop for every file */
3044 } /* Many entries */
3046 clean_up:
3047 /* Clean up */
3048 if (save_cwd != NULL)
3050 tmp_vpath = vfs_path_from_str (save_cwd);
3051 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3052 vfs_path_free (tmp_vpath);
3053 g_free (save_cwd);
3056 if (save_dest != NULL)
3058 tmp_vpath = vfs_path_from_str (save_dest);
3059 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3060 vfs_path_free (tmp_vpath);
3061 g_free (save_dest);
3064 linklist = free_linklist (linklist);
3065 dest_dirs = free_linklist (dest_dirs);
3066 #ifdef WITH_FULL_PATHS
3067 vfs_path_free (source_with_vpath);
3068 #endif /* WITH_FULL_PATHS */
3069 g_free (dest);
3070 vfs_path_free (dest_vpath);
3071 MC_PTR_FREE (ctx->dest_mask);
3073 #ifdef ENABLE_BACKGROUND
3074 /* Let our parent know we are saying bye bye */
3075 if (mc_global.we_are_background)
3077 int cur_pid = getpid ();
3078 /* Send pid to parent with child context, it is fork and
3079 don't modify real parent ctx */
3080 ctx->pid = cur_pid;
3081 parent_call ((void *) end_bg_process, ctx, 0);
3083 vfs_shut ();
3084 my_exit (EXIT_SUCCESS);
3086 #endif /* ENABLE_BACKGROUND */
3088 file_op_total_context_destroy (tctx);
3089 ret_fast:
3090 file_op_context_destroy (ctx);
3091 g_free (source);
3093 return ret_val;
3096 /* }}} */
3098 /* --------------------------------------------------------------------------------------------- */
3099 /* {{{ Query/status report routines */
3100 /** Report error with one file */
3101 FileProgressStatus
3102 file_error (const char *format, const char *file)
3104 char buf[BUF_MEDIUM];
3106 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3108 return do_file_error (buf);
3111 /* --------------------------------------------------------------------------------------------- */
3114 Cause emacs to enter folding mode for this file:
3115 Local variables:
3116 end: