(panel_operate): variable clean up.
[midnight-commander.git] / src / filemanager / file.c
blob6c5cee0732c0450dc57f2a094fe4ed52407ff89c
1 /*
2 File management.
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2011, 2012, 2013
6 The Free Software Foundation, Inc.
8 Written by:
9 Janne Kukonlehto, 1994, 1995
10 Fred Leeflang, 1994, 1995
11 Miguel de Icaza, 1994, 1995, 1996
12 Jakub Jelinek, 1995, 1996
13 Norbert Warmuth, 1997
14 Pavel Machek, 1998
15 Andrew Borodin <aborodin@vmail.ru>, 2011, 2012, 2013
17 The copy code was based in GNU's cp, and was written by:
18 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
20 The move code was based in GNU's mv, and was written by:
21 Mike Parker and David MacKenzie.
23 Janne Kukonlehto added much error recovery to them for being used
24 in an interactive program.
26 This file is part of the Midnight Commander.
28 The Midnight Commander is free software: you can redistribute it
29 and/or modify it under the terms of the GNU General Public License as
30 published by the Free Software Foundation, either version 3 of the License,
31 or (at your option) any later version.
33 The Midnight Commander is distributed in the hope that it will be useful,
34 but WITHOUT ANY WARRANTY; without even the implied warranty of
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 GNU General Public License for more details.
38 You should have received a copy of the GNU General Public License
39 along with this program. If not, see <http://www.gnu.org/licenses/>.
43 * Please note that all dialogs used here must be safe for background
44 * operations.
47 /** \file src/filemanager/file.c
48 * \brief Source: file management
51 /* {{{ Include files */
53 #include <config.h>
55 #include <ctype.h>
56 #include <errno.h>
57 #include <stdlib.h>
58 #include <stdio.h>
59 #include <string.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <unistd.h>
63 #include <fcntl.h>
65 #include "lib/global.h"
66 #include "lib/tty/tty.h"
67 #include "lib/tty/key.h"
68 #include "lib/search.h"
69 #include "lib/strescape.h"
70 #include "lib/strutil.h"
71 #include "lib/util.h"
72 #include "lib/vfs/vfs.h"
73 #include "lib/widget.h"
75 #include "src/setup.h"
76 #ifdef ENABLE_BACKGROUND
77 #include "src/background.h" /* do_background() */
78 #endif
80 /* Needed for current_panel, other_panel and WTree */
81 #include "dir.h"
82 #include "filegui.h"
83 #include "filenot.h"
84 #include "tree.h"
85 #include "midnight.h" /* current_panel */
86 #include "layout.h" /* rotate_dash() */
88 #include "file.h"
90 /* }}} */
92 /*** global variables ****************************************************************************/
94 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
95 const char *op_names[3] = {
96 N_("DialogTitle|Copy"),
97 N_("DialogTitle|Move"),
98 N_("DialogTitle|Delete")
101 /*** file scope macro definitions ****************************************************************/
103 /* Hack: the vfs code should not rely on this */
104 #define WITH_FULL_PATHS 1
106 #define FILEOP_UPDATE_INTERVAL 2
107 #define FILEOP_STALLING_INTERVAL 4
109 /*** file scope type declarations ****************************************************************/
111 /* This is a hard link cache */
112 struct link
114 const struct vfs_class *vfs;
115 dev_t dev;
116 ino_t ino;
117 short linkcount;
118 mode_t st_mode;
119 vfs_path_t *src_vpath;
120 vfs_path_t *dst_vpath;
123 /* Status of the destination file */
124 typedef enum
126 DEST_NONE = 0, /* Not created */
127 DEST_SHORT = 1, /* Created, not fully copied */
128 DEST_FULL = 2 /* Created, fully copied */
129 } dest_status_t;
132 * This array introduced to avoid translation problems. The former (op_names)
133 * is assumed to be nouns, suitable in dialog box titles; this one should
134 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
135 * (I don't use spaces around the words, because someday they could be
136 * dropped, when widgets get smarter)
139 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
140 static const char *op_names1[] = {
141 N_("FileOperation|Copy"),
142 N_("FileOperation|Move"),
143 N_("FileOperation|Delete")
147 * These are formats for building a prompt. Parts encoded as follows:
148 * %o - operation from op_names1
149 * %f - file/files or files/directories, as appropriate
150 * %m - "with source mask" or question mark for delete
151 * %s - source name (truncated)
152 * %d - number of marked files
153 * %n - the '\n' symbol to form two-line prompt for delete or space for other operations
155 /* xgettext:no-c-format */
156 static const char *one_format = N_("%o %f%n\"%s\"%m");
157 /* xgettext:no-c-format */
158 static const char *many_format = N_("%o %d %f%m");
160 static const char *prompt_parts[] = {
161 N_("file"),
162 N_("files"),
163 N_("directory"),
164 N_("directories"),
165 N_("files/directories"),
166 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
167 N_(" with source mask:")
170 /*** file scope variables ************************************************************************/
172 /* the hard link cache */
173 static GSList *linklist = NULL;
175 /* the files-to-be-erased list */
176 static GSList *erase_list = NULL;
179 * In copy_dir_dir we use two additional single linked lists: The first -
180 * variable name 'parent_dirs' - holds information about already copied
181 * directories and is used to detect cyclic symbolic links.
182 * The second ('dest_dirs' below) holds information about just created
183 * target directories and is used to detect when an directory is copied
184 * into itself (we don't want to copy infinitly).
185 * Both lists don't use the linkcount and name structure members of struct
186 * link.
188 static GSList *dest_dirs = NULL;
190 static FileProgressStatus transform_error = FILE_CONT;
192 /*** file scope functions ************************************************************************/
193 /* --------------------------------------------------------------------------------------------- */
195 static char *
196 transform_source (file_op_context_t * ctx, const vfs_path_t * source_vpath)
198 char *s, *q;
199 char *fnsource;
201 s = g_strdup (vfs_path_as_str (source_vpath));
203 /* We remove \n from the filename since regex routines would use \n as an anchor */
204 /* this is just to be allowed to maniupulate file names with \n on it */
205 for (q = s; *q != '\0'; q++)
206 if (*q == '\n')
207 *q = ' ';
209 fnsource = (char *) x_basename (s);
211 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
213 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
214 if (ctx->search_handle->error != MC_SEARCH_E_OK)
216 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
217 q = NULL;
218 transform_error = FILE_ABORT;
221 else
223 q = NULL;
224 transform_error = FILE_SKIP;
227 g_free (s);
228 return q;
231 /* --------------------------------------------------------------------------------------------- */
233 static void
234 free_link (void *data)
236 struct link *lp = (struct link *) data;
238 vfs_path_free (lp->src_vpath);
239 vfs_path_free (lp->dst_vpath);
240 g_free (lp);
243 /* --------------------------------------------------------------------------------------------- */
245 static inline void *
246 free_linklist (GSList * lp)
248 g_slist_free_full (lp, free_link);
250 return NULL;
253 /* --------------------------------------------------------------------------------------------- */
255 static gboolean
256 is_in_linklist (const GSList * lp, const vfs_path_t * vpath, const struct stat *sb)
258 const struct vfs_class *class;
259 ino_t ino = sb->st_ino;
260 dev_t dev = sb->st_dev;
262 class = vfs_path_get_last_path_vfs (vpath);
264 for (; lp != NULL; lp = g_slist_next (lp))
266 const struct link *lnk = (const struct link *) lp->data;
268 if (lnk->vfs == class && lnk->ino == ino && lnk->dev == dev)
269 return TRUE;
271 return FALSE;
274 /* --------------------------------------------------------------------------------------------- */
276 * Check and made hardlink
278 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
279 * and a hardlink was successfully made
282 static gboolean
283 check_hardlinks (const vfs_path_t * src_vpath, const vfs_path_t * dst_vpath, struct stat *pstat)
285 GSList *lp;
286 struct link *lnk;
288 const struct vfs_class *my_vfs;
289 ino_t ino = pstat->st_ino;
290 dev_t dev = pstat->st_dev;
291 struct stat link_stat;
293 if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
294 return FALSE;
296 my_vfs = vfs_path_get_by_index (src_vpath, -1)->class;
298 for (lp = linklist; lp != NULL; lp = g_slist_next (lp))
300 lnk = (struct link *) lp->data;
302 if (lnk->vfs == my_vfs && lnk->ino == ino && lnk->dev == dev)
304 const struct vfs_class *lp_name_class;
305 int stat_result;
307 lp_name_class = vfs_path_get_last_path_vfs (lnk->src_vpath);
308 stat_result = mc_stat (lnk->src_vpath, &link_stat);
310 if (stat_result == 0 && link_stat.st_ino == ino
311 && link_stat.st_dev == dev && lp_name_class == my_vfs)
313 const struct vfs_class *p_class, *dst_name_class;
315 dst_name_class = vfs_path_get_last_path_vfs (dst_vpath);
316 p_class = vfs_path_get_last_path_vfs (lnk->dst_vpath);
318 if (dst_name_class == p_class &&
319 mc_stat (lnk->dst_vpath, &link_stat) == 0 &&
320 mc_link (lnk->dst_vpath, dst_vpath) == 0)
321 return TRUE;
324 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
325 return FALSE;
329 lnk = g_new0 (struct link, 1);
330 if (lnk != NULL)
332 lnk->vfs = my_vfs;
333 lnk->ino = ino;
334 lnk->dev = dev;
335 lnk->src_vpath = vfs_path_clone (src_vpath);
336 lnk->dst_vpath = vfs_path_clone (dst_vpath);
337 linklist = g_slist_prepend (linklist, lnk);
340 return FALSE;
343 /* --------------------------------------------------------------------------------------------- */
345 * Duplicate the contents of the symbolic link src_path in dst_path.
346 * Try to make a stable symlink if the option "stable symlink" was
347 * set in the file mask dialog.
348 * If dst_path is an existing symlink it will be deleted silently
349 * (upper levels take already care of existing files at dst_path).
352 static FileProgressStatus
353 make_symlink (file_op_context_t * ctx, const char *src_path, const char *dst_path)
355 char link_target[MC_MAXPATHLEN];
356 int len;
357 FileProgressStatus return_status;
358 struct stat sb;
359 vfs_path_t *src_vpath;
360 vfs_path_t *dst_vpath;
361 gboolean dst_is_symlink;
362 vfs_path_t *link_target_vpath = NULL;
364 src_vpath = vfs_path_from_str (src_path);
365 dst_vpath = vfs_path_from_str (dst_path);
366 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
368 retry_src_readlink:
369 len = mc_readlink (src_vpath, link_target, MC_MAXPATHLEN - 1);
370 if (len < 0)
372 if (ctx->skip_all)
373 return_status = FILE_SKIPALL;
374 else
376 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
377 if (return_status == FILE_SKIPALL)
378 ctx->skip_all = TRUE;
379 if (return_status == FILE_RETRY)
380 goto retry_src_readlink;
382 goto ret;
384 link_target[len] = 0;
386 if (ctx->stable_symlinks)
389 if (!vfs_file_is_local (src_vpath) || !vfs_file_is_local (dst_vpath))
391 message (D_ERROR, MSG_ERROR,
392 _("Cannot make stable symlinks across"
393 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
394 ctx->stable_symlinks = FALSE;
398 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
400 const char *r = strrchr (src_path, PATH_SEP);
402 if (r)
404 char *p;
405 vfs_path_t *q;
407 p = g_strndup (src_path, r - src_path + 1);
408 if (g_path_is_absolute (dst_path))
409 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
410 else
411 q = vfs_path_build_filename (p, dst_path, (char *) NULL);
413 if (vfs_path_tokens_count (q) > 1)
415 char *s;
416 vfs_path_t *tmp_vpath1, *tmp_vpath2;
418 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
419 s = g_strconcat (p, link_target, (char *) NULL);
420 g_free (p);
421 g_strlcpy (link_target, s, sizeof (link_target));
422 g_free (s);
423 tmp_vpath2 = vfs_path_from_str (link_target);
424 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
425 vfs_path_free (tmp_vpath1);
426 vfs_path_free (tmp_vpath2);
427 if (s)
429 g_strlcpy (link_target, s, sizeof (link_target));
430 g_free (s);
433 else
434 g_free (p);
435 vfs_path_free (q);
438 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
440 retry_dst_symlink:
441 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
443 /* Success */
444 return_status = FILE_CONT;
445 goto ret;
448 * if dst_exists, it is obvious that this had failed.
449 * We can delete the old symlink and try again...
451 if (dst_is_symlink)
453 if (mc_unlink (dst_vpath) == 0)
454 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
456 /* Success */
457 return_status = FILE_CONT;
458 goto ret;
461 if (ctx->skip_all)
462 return_status = FILE_SKIPALL;
463 else
465 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
466 if (return_status == FILE_SKIPALL)
467 ctx->skip_all = TRUE;
468 if (return_status == FILE_RETRY)
469 goto retry_dst_symlink;
472 ret:
473 vfs_path_free (src_vpath);
474 vfs_path_free (dst_vpath);
475 vfs_path_free (link_target_vpath);
476 return return_status;
479 /* --------------------------------------------------------------------------------------------- */
481 * do_compute_dir_size:
483 * Computes the number of bytes used by the files in a directory
486 static FileProgressStatus
487 do_compute_dir_size (const vfs_path_t * dirname_vpath, void *ui,
488 compute_dir_size_callback cback, size_t * dir_count, size_t * ret_marked,
489 uintmax_t * ret_total, gboolean compute_symlinks)
491 static unsigned short int update_ui_count = 0;
493 int res;
494 struct stat s;
495 DIR *dir;
496 struct dirent *dirent;
497 FileProgressStatus ret = FILE_CONT;
499 if (!compute_symlinks)
501 res = mc_lstat (dirname_vpath, &s);
502 if (res != 0)
503 return ret;
505 /* don't scan symlink to directory */
506 if (S_ISLNK (s.st_mode))
508 (*ret_marked)++;
509 *ret_total += (uintmax_t) s.st_size;
510 return ret;
514 (*dir_count)++;
516 dir = mc_opendir (dirname_vpath);
517 if (dir == NULL)
518 return ret;
520 while (ret == FILE_CONT && (dirent = mc_readdir (dir)) != NULL)
522 vfs_path_t *tmp_vpath;
524 if (DIR_IS_DOT (dirent->d_name) || DIR_IS_DOTDOT (dirent->d_name))
525 continue;
527 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, NULL);
529 res = mc_lstat (tmp_vpath, &s);
530 if (res == 0)
532 if (S_ISDIR (s.st_mode))
534 ret =
535 do_compute_dir_size (tmp_vpath, ui, cback, dir_count, ret_marked, ret_total,
536 compute_symlinks);
537 if (ret == FILE_CONT)
538 ret =
539 (cback == NULL) ? FILE_CONT : cback (ui, tmp_vpath, *dir_count, *ret_total);
541 else
543 (*ret_marked)++;
544 *ret_total += (uintmax_t) s.st_size;
546 update_ui_count++;
547 if ((update_ui_count & 31) == 0)
548 ret =
549 (cback == NULL) ? FILE_CONT : cback (ui, dirname_vpath, *dir_count,
550 *ret_total);
554 vfs_path_free (tmp_vpath);
557 mc_closedir (dir);
558 return ret;
561 /* --------------------------------------------------------------------------------------------- */
563 static FileProgressStatus
564 progress_update_one (FileOpTotalContext * tctx, file_op_context_t * ctx, off_t add)
566 struct timeval tv_current;
567 static struct timeval tv_start = { };
569 tctx->progress_count++;
570 tctx->progress_bytes += (uintmax_t) add;
572 if (tv_start.tv_sec == 0)
574 gettimeofday (&tv_start, (struct timezone *) NULL);
576 gettimeofday (&tv_current, (struct timezone *) NULL);
577 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
579 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
581 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
582 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
584 tv_start.tv_sec = tv_current.tv_sec;
587 return check_progress_buttons (ctx);
590 /* --------------------------------------------------------------------------------------------- */
592 static FileProgressStatus
593 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
595 char *msg;
596 int result = 0;
597 const char *head_msg;
599 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
601 msg = g_strdup_printf (fmt, a, b);
602 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
603 g_free (msg);
604 do_refresh ();
606 return (result == 1) ? FILE_ABORT : FILE_SKIP;
609 /* --------------------------------------------------------------------------------------------- */
611 static FileProgressStatus
612 warn_same_file (const char *fmt, const char *a, const char *b)
614 #ifdef ENABLE_BACKGROUND
615 /* *INDENT-OFF* */
616 union
618 void *p;
619 FileProgressStatus (*f) (enum OperationMode, const char *fmt, const char *a, const char *b);
620 } pntr;
621 /* *INDENT-ON* */
623 pntr.f = real_warn_same_file;
625 if (mc_global.we_are_background)
626 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
627 #endif
628 return real_warn_same_file (Foreground, fmt, a, b);
631 /* --------------------------------------------------------------------------------------------- */
632 /* {{{ Query/status report routines */
634 static FileProgressStatus
635 real_do_file_error (enum OperationMode mode, const char *error)
637 int result;
638 const char *msg;
640 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
641 result =
642 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
644 switch (result)
646 case 0:
647 do_refresh ();
648 return FILE_SKIP;
650 case 1:
651 do_refresh ();
652 return FILE_SKIPALL;
654 case 2:
655 do_refresh ();
656 return FILE_RETRY;
658 case 3:
659 default:
660 return FILE_ABORT;
664 /* --------------------------------------------------------------------------------------------- */
666 static FileProgressStatus
667 real_query_recursive (file_op_context_t * ctx, enum OperationMode mode, const char *s)
669 if (ctx->recursive_result < RECURSIVE_ALWAYS)
671 const char *msg;
672 char *text;
674 msg = mode == Foreground
675 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
676 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
677 text = g_strdup_printf (msg, path_trunc (s, 30));
679 if (safe_delete)
680 query_set_sel (1);
682 ctx->recursive_result =
683 (FileCopyMode) query_dialog (op_names[OP_DELETE], text, D_ERROR, 5,
684 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
685 g_free (text);
687 if (ctx->recursive_result != RECURSIVE_ABORT)
688 do_refresh ();
691 switch (ctx->recursive_result)
693 case RECURSIVE_YES:
694 case RECURSIVE_ALWAYS:
695 return FILE_CONT;
697 case RECURSIVE_NO:
698 case RECURSIVE_NEVER:
699 return FILE_SKIP;
701 case RECURSIVE_ABORT:
702 default:
703 return FILE_ABORT;
707 /* --------------------------------------------------------------------------------------------- */
709 #ifdef ENABLE_BACKGROUND
710 static FileProgressStatus
711 do_file_error (const char *str)
713 /* *INDENT-OFF* */
714 union
716 void *p;
717 FileProgressStatus (*f) (enum OperationMode, const char *);
718 } pntr;
719 /* *INDENT-ON* */
721 pntr.f = real_do_file_error;
723 if (mc_global.we_are_background)
724 return parent_call (pntr.p, NULL, 1, strlen (str), str);
725 else
726 return real_do_file_error (Foreground, str);
729 /* --------------------------------------------------------------------------------------------- */
731 static FileProgressStatus
732 query_recursive (file_op_context_t * ctx, const char *s)
734 /* *INDENT-OFF* */
735 union
737 void *p;
738 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *);
739 } pntr;
740 /* *INDENT-ON* */
742 pntr.f = real_query_recursive;
744 if (mc_global.we_are_background)
745 return parent_call (pntr.p, ctx, 1, strlen (s), s);
746 else
747 return real_query_recursive (ctx, Foreground, s);
750 /* --------------------------------------------------------------------------------------------- */
752 static FileProgressStatus
753 query_replace (file_op_context_t * ctx, const char *destname, struct stat *_s_stat,
754 struct stat *_d_stat)
756 /* *INDENT-OFF* */
757 union
759 void *p;
760 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *,
761 struct stat *, struct stat *);
762 } pntr;
763 /* *INDENT-ON* */
765 pntr.f = file_progress_real_query_replace;
767 if (mc_global.we_are_background)
768 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
769 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
770 else
771 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
774 #else
775 /* --------------------------------------------------------------------------------------------- */
777 static FileProgressStatus
778 do_file_error (const char *str)
780 return real_do_file_error (Foreground, str);
783 /* --------------------------------------------------------------------------------------------- */
785 static FileProgressStatus
786 query_recursive (file_op_context_t * ctx, const char *s)
788 return real_query_recursive (ctx, Foreground, s);
791 /* --------------------------------------------------------------------------------------------- */
793 static FileProgressStatus
794 query_replace (file_op_context_t * ctx, const char *destname, struct stat *_s_stat,
795 struct stat *_d_stat)
797 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
800 #endif /* !ENABLE_BACKGROUND */
802 /* --------------------------------------------------------------------------------------------- */
803 /** Report error with two files */
805 static FileProgressStatus
806 files_error (const char *format, const char *file1, const char *file2)
808 char buf[BUF_MEDIUM];
809 char *nfile1 = g_strdup (path_trunc (file1, 15));
810 char *nfile2 = g_strdup (path_trunc (file2, 15));
812 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
814 g_free (nfile1);
815 g_free (nfile2);
817 return do_file_error (buf);
820 /* }}} */
822 /* --------------------------------------------------------------------------------------------- */
824 static void
825 copy_file_file_display_progress (FileOpTotalContext * tctx, file_op_context_t * ctx,
826 struct timeval tv_current, struct timeval tv_transfer_start,
827 off_t file_size, off_t n_read_total)
829 long dt;
831 /* 1. Update rotating dash after some time */
832 rotate_dash (TRUE);
834 /* 3. Compute ETA */
835 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
837 if (n_read_total == 0)
838 ctx->eta_secs = 0.0;
839 else
841 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
842 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
845 /* 4. Compute BPS rate */
846 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
847 if (ctx->bps_time < 1)
848 ctx->bps_time = 1;
849 ctx->bps = n_read_total / ctx->bps_time;
851 /* 5. Compute total ETA and BPS */
852 if (ctx->progress_bytes != 0)
854 uintmax_t remain_bytes;
856 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
857 #if 1
859 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
861 if (total_secs < 1)
862 total_secs = 1;
864 tctx->bps = tctx->copied_bytes / total_secs;
865 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
867 #else
868 /* broken on lot of little files */
869 tctx->bps_count++;
870 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
871 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
872 #endif
876 /* --------------------------------------------------------------------------------------------- */
878 /* {{{ Move routines */
879 static FileProgressStatus
880 move_file_file (FileOpTotalContext * tctx, file_op_context_t * ctx, const char *s, const char *d)
882 struct stat src_stats, dst_stats;
883 FileProgressStatus return_status = FILE_CONT;
884 gboolean copy_done = FALSE;
885 gboolean old_ask_overwrite;
886 vfs_path_t *src_vpath, *dst_vpath;
888 src_vpath = vfs_path_from_str (s);
889 dst_vpath = vfs_path_from_str (d);
891 file_progress_show_source (ctx, src_vpath);
892 file_progress_show_target (ctx, dst_vpath);
894 if (check_progress_buttons (ctx) == FILE_ABORT)
896 return_status = FILE_ABORT;
897 goto ret;
900 mc_refresh ();
902 while (mc_lstat (src_vpath, &src_stats) != 0)
904 /* Source doesn't exist */
905 if (ctx->skip_all)
906 return_status = FILE_SKIPALL;
907 else
909 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
910 if (return_status == FILE_SKIPALL)
911 ctx->skip_all = TRUE;
914 if (return_status != FILE_RETRY)
915 goto ret;
918 if (mc_lstat (dst_vpath, &dst_stats) == 0)
920 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
922 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
923 goto ret;
926 if (S_ISDIR (dst_stats.st_mode))
928 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
929 do_refresh ();
930 return_status = FILE_SKIP;
931 goto ret;
934 if (confirm_overwrite)
936 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
937 if (return_status != FILE_CONT)
938 goto ret;
940 /* Ok to overwrite */
943 if (!ctx->do_append)
945 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
947 return_status = make_symlink (ctx, s, d);
948 if (return_status == FILE_CONT)
949 goto retry_src_remove;
950 goto ret;
953 if (mc_rename (src_vpath, dst_vpath) == 0)
955 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
956 goto ret;
959 #if 0
960 /* Comparison to EXDEV seems not to work in nfs if you're moving from
961 one nfs to the same, but on the server it is on two different
962 filesystems. Then nfs returns EIO instead of EXDEV.
963 Hope it will not hurt if we always in case of error try to copy/delete. */
964 else
965 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
967 if (errno != EXDEV)
969 if (ctx->skip_all)
970 return_status = FILE_SKIPALL;
971 else
973 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
974 if (return_status == FILE_SKIPALL)
975 ctx->skip_all = TRUE;
976 if (return_status == FILE_RETRY)
977 goto retry_rename;
980 goto ret;
982 #endif
984 /* Failed because filesystem boundary -> copy the file instead */
985 old_ask_overwrite = tctx->ask_overwrite;
986 tctx->ask_overwrite = FALSE;
987 return_status = copy_file_file (tctx, ctx, s, d);
988 tctx->ask_overwrite = old_ask_overwrite;
989 if (return_status != FILE_CONT)
990 goto ret;
992 copy_done = TRUE;
994 file_progress_show_source (ctx, NULL);
995 file_progress_show (ctx, 0, 0, "", FALSE);
997 return_status = check_progress_buttons (ctx);
998 if (return_status != FILE_CONT)
999 goto ret;
1000 mc_refresh ();
1002 retry_src_remove:
1003 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
1005 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
1006 if (return_status == FILE_RETRY)
1007 goto retry_src_remove;
1008 if (return_status == FILE_SKIPALL)
1009 ctx->skip_all = TRUE;
1010 goto ret;
1013 if (!copy_done)
1014 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
1016 ret:
1017 vfs_path_free (src_vpath);
1018 vfs_path_free (dst_vpath);
1020 return return_status;
1023 /* }}} */
1025 /* --------------------------------------------------------------------------------------------- */
1026 /* {{{ Erase routines */
1027 /** Don't update progress status if progress_count==NULL */
1029 static FileProgressStatus
1030 erase_file (FileOpTotalContext * tctx, file_op_context_t * ctx, const vfs_path_t * vpath)
1032 struct stat buf;
1034 file_progress_show_deleting (ctx, vfs_path_as_str (vpath), &tctx->progress_count);
1035 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1036 if (check_progress_buttons (ctx) == FILE_ABORT)
1037 return FILE_ABORT;
1039 mc_refresh ();
1041 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
1043 /* ignore, most likely the mc_unlink fails, too */
1044 buf.st_size = 0;
1047 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
1049 int return_status;
1051 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), vfs_path_as_str (vpath));
1052 if (return_status == FILE_ABORT)
1053 return return_status;
1054 if (return_status == FILE_RETRY)
1055 continue;
1056 if (return_status == FILE_SKIPALL)
1057 ctx->skip_all = TRUE;
1058 break;
1061 if (tctx->progress_count == 0)
1062 return FILE_CONT;
1064 return check_progress_buttons (ctx);
1067 /* --------------------------------------------------------------------------------------------- */
1070 Recursive remove of files
1071 abort->cancel stack
1072 skip ->warn every level, gets default
1073 skipall->remove as much as possible
1075 static FileProgressStatus
1076 recursive_erase (FileOpTotalContext * tctx, file_op_context_t * ctx, const vfs_path_t * vpath)
1078 struct dirent *next;
1079 DIR *reading;
1080 const char *s;
1081 FileProgressStatus return_status = FILE_CONT;
1083 reading = mc_opendir (vpath);
1084 if (reading == NULL)
1085 return FILE_RETRY;
1087 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1089 vfs_path_t *tmp_vpath;
1090 struct stat buf;
1092 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
1093 continue;
1095 tmp_vpath = vfs_path_append_new (vpath, next->d_name, NULL);
1096 if (mc_lstat (tmp_vpath, &buf) != 0)
1098 mc_closedir (reading);
1099 vfs_path_free (tmp_vpath);
1100 return FILE_RETRY;
1102 if (S_ISDIR (buf.st_mode))
1103 return_status = recursive_erase (tctx, ctx, tmp_vpath);
1104 else
1105 return_status = erase_file (tctx, ctx, tmp_vpath);
1106 vfs_path_free (tmp_vpath);
1108 mc_closedir (reading);
1110 if (return_status == FILE_ABORT)
1111 return FILE_ABORT;
1113 s = vfs_path_as_str (vpath);
1115 file_progress_show_deleting (ctx, s, NULL);
1116 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1117 if (check_progress_buttons (ctx) == FILE_ABORT)
1118 return FILE_ABORT;
1120 mc_refresh ();
1122 while (my_rmdir (s) != 0 && !ctx->skip_all)
1124 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1125 if (return_status == FILE_RETRY)
1126 continue;
1127 if (return_status == FILE_ABORT)
1128 break;
1129 if (return_status == FILE_SKIPALL)
1130 ctx->skip_all = TRUE;
1131 break;
1134 return return_status;
1137 /* --------------------------------------------------------------------------------------------- */
1138 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1139 in the directory path points to, 0 else. */
1141 static int
1142 check_dir_is_empty (const vfs_path_t * vpath)
1144 DIR *dir;
1145 struct dirent *d;
1146 int i = 1;
1148 dir = mc_opendir (vpath);
1149 if (dir == NULL)
1150 return -1;
1152 for (d = mc_readdir (dir); d != NULL; d = mc_readdir (dir))
1153 if (!DIR_IS_DOT (d->d_name) && !DIR_IS_DOTDOT (d->d_name))
1155 i = 0;
1156 break;
1159 mc_closedir (dir);
1160 return i;
1163 /* --------------------------------------------------------------------------------------------- */
1165 static FileProgressStatus
1166 erase_dir_iff_empty (file_op_context_t * ctx, const vfs_path_t * vpath, size_t count)
1168 FileProgressStatus error = FILE_CONT;
1169 const char *s;
1171 s = vfs_path_as_str (vpath);
1173 file_progress_show_deleting (ctx, s, NULL);
1174 file_progress_show_count (ctx, count, ctx->progress_count);
1175 if (check_progress_buttons (ctx) == FILE_ABORT)
1176 return FILE_ABORT;
1178 mc_refresh ();
1180 if (check_dir_is_empty (vpath) == 1) /* not empty or error */
1182 while (my_rmdir (s) != 0 && !ctx->skip_all)
1184 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1185 if (error == FILE_SKIPALL)
1186 ctx->skip_all = TRUE;
1187 if (error != FILE_RETRY)
1188 break;
1192 return error;
1195 /* }}} */
1197 /* --------------------------------------------------------------------------------------------- */
1198 /* {{{ Panel operate routines */
1201 * Return currently selected entry name or the name of the first marked
1202 * entry if there is one.
1205 static char *
1206 panel_get_file (WPanel * panel)
1208 if (get_current_type () == view_tree)
1210 WTree *tree;
1211 vfs_path_t *selected_name;
1213 tree = (WTree *) get_panel_widget (get_current_index ());
1214 selected_name = tree_selected_name (tree);
1215 return g_strdup (vfs_path_as_str (selected_name));
1218 if (panel->marked != 0)
1220 int i;
1222 for (i = 0; i < panel->dir.len; i++)
1223 if (panel->dir.list[i].f.marked)
1224 return g_strdup (panel->dir.list[i].fname);
1226 return g_strdup (panel->dir.list[panel->selected].fname);
1229 /* --------------------------------------------------------------------------------------------- */
1231 * panel_compute_totals:
1233 * compute the number of files and the number of bytes
1234 * used up by the whole selection, recursing directories
1235 * as required. In addition, it checks to see if it will
1236 * overwrite any files by doing the copy.
1239 static FileProgressStatus
1240 panel_compute_totals (const WPanel * panel, void *ui, compute_dir_size_callback cback,
1241 size_t * ret_count, uintmax_t * ret_total, gboolean compute_symlinks)
1243 int i;
1244 size_t dir_count = 0;
1246 for (i = 0; i < panel->dir.len; i++)
1248 struct stat *s;
1250 if (!panel->dir.list[i].f.marked)
1251 continue;
1253 s = &panel->dir.list[i].st;
1255 if (S_ISDIR (s->st_mode))
1257 vfs_path_t *p;
1258 FileProgressStatus status;
1260 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, NULL);
1261 status = compute_dir_size (p, ui, cback, &dir_count, ret_count, ret_total,
1262 compute_symlinks);
1263 vfs_path_free (p);
1265 if (status != FILE_CONT)
1266 return status;
1268 else
1270 (*ret_count)++;
1271 *ret_total += (uintmax_t) s->st_size;
1275 return FILE_CONT;
1278 /* --------------------------------------------------------------------------------------------- */
1280 /** Initialize variables for progress bars */
1281 static FileProgressStatus
1282 panel_operate_init_totals (const WPanel * panel, const char *source, file_op_context_t * ctx,
1283 filegui_dialog_type_t dialog_type)
1285 FileProgressStatus status;
1287 #ifdef ENABLE_BACKGROUND
1288 if (mc_global.we_are_background)
1289 return FILE_CONT;
1290 #endif
1292 if (verbose && file_op_compute_totals)
1294 ComputeDirSizeUI *ui;
1296 ui = compute_dir_size_create_ui (TRUE);
1298 ctx->progress_count = 0;
1299 ctx->progress_bytes = 0;
1301 if (source == NULL)
1302 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1303 &ctx->progress_count, &ctx->progress_bytes,
1304 ctx->follow_links);
1305 else
1307 vfs_path_t *p;
1308 size_t dir_count = 0;
1310 p = vfs_path_from_str (source);
1311 status = compute_dir_size (p, ui, compute_dir_size_update_ui, &dir_count,
1312 &ctx->progress_count, &ctx->progress_bytes,
1313 ctx->follow_links);
1314 vfs_path_free (p);
1317 compute_dir_size_destroy_ui (ui);
1319 ctx->progress_totals_computed = (status == FILE_CONT);
1321 if (status == FILE_SKIP)
1322 status = FILE_CONT;
1324 else
1326 status = FILE_CONT;
1327 ctx->progress_count = panel->marked;
1328 ctx->progress_bytes = panel->total;
1329 ctx->progress_totals_computed = FALSE;
1332 file_op_context_create_ui (ctx, TRUE, dialog_type);
1334 return status;
1337 /* --------------------------------------------------------------------------------------------- */
1339 * Generate user prompt for panel operation.
1340 * single_source is the name if the source entry or NULL for multiple
1341 * entries.
1342 * src_stat is only used when single_source is not NULL.
1345 static char *
1346 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1347 gboolean single_source, const struct stat *src_stat)
1349 char *sp;
1350 char *format_string;
1351 const char *cp;
1353 static gboolean i18n_flag = FALSE;
1354 if (!i18n_flag)
1356 size_t i;
1358 for (i = G_N_ELEMENTS (op_names1); i-- != 0;)
1359 op_names1[i] = Q_ (op_names1[i]);
1361 #ifdef ENABLE_NLS
1362 for (i = G_N_ELEMENTS (prompt_parts); i-- != 0;)
1363 prompt_parts[i] = _(prompt_parts[i]);
1365 one_format = _(one_format);
1366 many_format = _(many_format);
1367 #endif /* ENABLE_NLS */
1368 i18n_flag = TRUE;
1371 /* Possible prompts:
1372 * OP_COPY:
1373 * "Copy file \"%s\" with source mask:"
1374 * "Copy %d files with source mask:"
1375 * "Copy directory \"%s\" with source mask:"
1376 * "Copy %d directories with source mask:"
1377 * "Copy %d files/directories with source mask:"
1378 * OP_MOVE:
1379 * "Move file \"%s\" with source mask:"
1380 * "Move %d files with source mask:"
1381 * "Move directory \"%s\" with source mask:"
1382 * "Move %d directories with source mask:"
1383 * "Move %d files/directories with source mask:"
1384 * OP_DELETE:
1385 * "Delete file \"%s\"?"
1386 * "Delete %d files?"
1387 * "Delete directory \"%s\"?"
1388 * "Delete %d directories?"
1389 * "Delete %d files/directories?"
1392 sp = (char *) (single_source ? one_format : many_format);
1394 /* 1. Substitute %o */
1395 format_string = str_replace_all (sp, "%o", op_names1[(int) operation]);
1397 /* 2. Substitute %n */
1398 cp = operation == OP_DELETE ? "\n" : " ";
1399 sp = format_string;
1400 format_string = str_replace_all (sp, "%n", cp);
1401 g_free (sp);
1403 /* 3. Substitute %f */
1404 if (single_source)
1405 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1406 else if (panel->marked == panel->dirs_marked)
1407 cp = prompt_parts[3];
1408 else
1409 cp = panel->dirs_marked != 0 ? prompt_parts[4] : prompt_parts[1];
1411 sp = format_string;
1412 format_string = str_replace_all (sp, "%f", cp);
1413 g_free (sp);
1415 /* 4. Substitute %m */
1416 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1417 sp = format_string;
1418 format_string = str_replace_all (sp, "%m", cp);
1419 g_free (sp);
1421 return format_string;
1424 /* --------------------------------------------------------------------------------------------- */
1426 #ifdef ENABLE_BACKGROUND
1427 static int
1428 end_bg_process (file_op_context_t * ctx, enum OperationMode mode)
1430 int pid = ctx->pid;
1432 (void) mode;
1433 ctx->pid = 0;
1435 unregister_task_with_pid (pid);
1436 /* file_op_context_destroy(ctx); */
1437 return 1;
1439 #endif
1440 /* }}} */
1442 /* --------------------------------------------------------------------------------------------- */
1443 /*** public functions ****************************************************************************/
1444 /* --------------------------------------------------------------------------------------------- */
1446 FileProgressStatus
1447 copy_file_file (FileOpTotalContext * tctx, file_op_context_t * ctx,
1448 const char *src_path, const char *dst_path)
1450 uid_t src_uid = (uid_t) (-1);
1451 gid_t src_gid = (gid_t) (-1);
1453 int src_desc, dest_desc = -1;
1454 int n_read, n_written;
1455 mode_t src_mode = 0; /* The mode of the source file */
1456 struct stat sb, sb2;
1457 struct utimbuf utb;
1458 gboolean dst_exists = FALSE, appending = FALSE;
1459 off_t file_size = -1;
1460 FileProgressStatus return_status, temp_status;
1461 struct timeval tv_transfer_start;
1462 dest_status_t dst_status = DEST_NONE;
1463 int open_flags;
1464 gboolean is_first_time = TRUE;
1465 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1466 gboolean write_errno_nospace = FALSE;
1468 /* FIXME: We should not be using global variables! */
1469 ctx->do_reget = 0;
1470 return_status = FILE_RETRY;
1472 dst_vpath = vfs_path_from_str (dst_path);
1473 src_vpath = vfs_path_from_str (src_path);
1475 file_progress_show_source (ctx, src_vpath);
1476 file_progress_show_target (ctx, dst_vpath);
1478 if (check_progress_buttons (ctx) == FILE_ABORT)
1480 return_status = FILE_ABORT;
1481 goto ret_fast;
1484 mc_refresh ();
1486 while (mc_stat (dst_vpath, &sb2) == 0)
1488 if (S_ISDIR (sb2.st_mode))
1490 if (ctx->skip_all)
1491 return_status = FILE_SKIPALL;
1492 else
1494 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1495 if (return_status == FILE_SKIPALL)
1496 ctx->skip_all = TRUE;
1497 if (return_status == FILE_RETRY)
1498 continue;
1500 goto ret_fast;
1503 dst_exists = TRUE;
1504 break;
1507 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1509 if (ctx->skip_all)
1510 return_status = FILE_SKIPALL;
1511 else
1513 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1514 if (return_status == FILE_SKIPALL)
1515 ctx->skip_all = TRUE;
1518 if (return_status != FILE_RETRY)
1519 goto ret_fast;
1522 if (dst_exists)
1524 /* Destination already exists */
1525 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1527 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1528 src_path, dst_path);
1529 goto ret_fast;
1532 /* Should we replace destination? */
1533 if (tctx->ask_overwrite)
1535 ctx->do_reget = 0;
1536 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1537 if (return_status != FILE_CONT)
1538 goto ret_fast;
1542 if (!ctx->do_append)
1544 /* Check the hardlinks */
1545 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &sb))
1547 /* We have made a hardlink - no more processing is necessary */
1548 return_status = FILE_CONT;
1549 goto ret_fast;
1552 if (S_ISLNK (sb.st_mode))
1554 return_status = make_symlink (ctx, src_path, dst_path);
1555 goto ret_fast;
1558 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1559 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1561 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1562 && !ctx->skip_all)
1564 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1565 if (return_status == FILE_RETRY)
1566 continue;
1567 if (return_status == FILE_SKIPALL)
1568 ctx->skip_all = TRUE;
1569 goto ret_fast;
1571 /* Success */
1573 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1574 && !ctx->skip_all)
1576 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1577 if (temp_status == FILE_SKIP)
1578 break;
1579 if (temp_status == FILE_SKIPALL)
1580 ctx->skip_all = TRUE;
1581 if (temp_status != FILE_RETRY)
1583 return_status = temp_status;
1584 goto ret_fast;
1588 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1589 && !ctx->skip_all)
1591 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1592 if (temp_status == FILE_SKIP)
1593 break;
1594 if (temp_status == FILE_SKIPALL)
1595 ctx->skip_all = TRUE;
1596 if (temp_status != FILE_RETRY)
1598 return_status = temp_status;
1599 goto ret_fast;
1603 return_status = FILE_CONT;
1604 goto ret_fast;
1608 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1610 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1612 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1613 if (return_status == FILE_RETRY)
1614 continue;
1615 if (return_status == FILE_SKIPALL)
1616 ctx->skip_all = TRUE;
1617 if (return_status == FILE_SKIP)
1618 break;
1619 ctx->do_append = 0;
1620 goto ret_fast;
1623 if (ctx->do_reget != 0)
1625 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1627 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1628 ctx->do_reget = 0;
1629 ctx->do_append = FALSE;
1633 while (mc_fstat (src_desc, &sb) != 0)
1635 if (ctx->skip_all)
1636 return_status = FILE_SKIPALL;
1637 else
1639 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1640 if (return_status == FILE_RETRY)
1641 continue;
1642 if (return_status == FILE_SKIPALL)
1643 ctx->skip_all = TRUE;
1644 ctx->do_append = FALSE;
1646 goto ret;
1649 src_mode = sb.st_mode;
1650 src_uid = sb.st_uid;
1651 src_gid = sb.st_gid;
1652 utb.actime = sb.st_atime;
1653 utb.modtime = sb.st_mtime;
1654 file_size = sb.st_size;
1656 open_flags = O_WRONLY;
1657 if (dst_exists)
1659 if (ctx->do_append != 0)
1660 open_flags |= O_APPEND;
1661 else
1662 open_flags |= O_CREAT | O_TRUNC;
1664 else
1666 open_flags |= O_CREAT | O_EXCL;
1669 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
1671 if (errno != EEXIST)
1673 if (ctx->skip_all)
1674 return_status = FILE_SKIPALL;
1675 else
1677 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1678 if (return_status == FILE_RETRY)
1679 continue;
1680 if (return_status == FILE_SKIPALL)
1681 ctx->skip_all = TRUE;
1682 ctx->do_append = FALSE;
1685 goto ret;
1687 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1689 appending = ctx->do_append;
1690 ctx->do_append = FALSE;
1692 /* Find out the optimal buffer size. */
1693 while (mc_fstat (dest_desc, &sb) != 0)
1695 if (ctx->skip_all)
1696 return_status = FILE_SKIPALL;
1697 else
1699 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1700 if (return_status == FILE_RETRY)
1701 continue;
1702 if (return_status == FILE_SKIPALL)
1703 ctx->skip_all = TRUE;
1705 goto ret;
1708 while (TRUE)
1710 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1711 if (errno == 0)
1712 break;
1714 if (ctx->skip_all)
1715 return_status = FILE_SKIPALL;
1716 else
1718 return_status =
1719 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1720 if (return_status == FILE_RETRY)
1721 continue;
1722 if (return_status == FILE_SKIPALL)
1723 ctx->skip_all = TRUE;
1725 mc_close (dest_desc);
1726 dest_desc = -1;
1727 mc_unlink (dst_vpath);
1728 dst_status = DEST_NONE;
1729 goto ret;
1732 ctx->eta_secs = 0.0;
1733 ctx->bps = 0;
1735 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1736 file_progress_show (ctx, 0, file_size, "", TRUE);
1737 else
1738 file_progress_show (ctx, 1, 1, "", TRUE);
1739 return_status = check_progress_buttons (ctx);
1740 mc_refresh ();
1742 if (return_status != FILE_CONT)
1743 goto ret;
1746 off_t n_read_total = 0;
1747 struct timeval tv_current, tv_last_update, tv_last_input;
1748 int secs, update_secs;
1749 const char *stalled_msg = "";
1751 tv_last_update = tv_transfer_start;
1753 while (TRUE)
1755 char buf[BUF_8K];
1757 /* src_read */
1758 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1759 n_read = -1;
1760 else
1761 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1763 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1764 if (return_status == FILE_RETRY)
1765 continue;
1766 if (return_status == FILE_SKIPALL)
1767 ctx->skip_all = TRUE;
1768 goto ret;
1770 if (n_read == 0)
1771 break;
1773 gettimeofday (&tv_current, NULL);
1775 if (n_read > 0)
1777 char *t = buf;
1778 n_read_total += n_read;
1780 /* Windows NT ftp servers report that files have no
1781 * permissions: -------, so if we happen to have actually
1782 * read something, we should fix the permissions.
1784 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1785 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1786 gettimeofday (&tv_last_input, NULL);
1788 /* dst_write */
1789 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read)
1791 if (n_written > 0)
1793 n_read -= n_written;
1794 t += n_written;
1795 continue;
1798 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
1800 if (ctx->skip_all)
1801 return_status = FILE_SKIPALL;
1802 else
1803 return_status =
1804 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1806 if (return_status == FILE_SKIP)
1808 if (write_errno_nospace)
1809 goto ret;
1810 break;
1812 if (return_status == FILE_SKIPALL)
1814 ctx->skip_all = TRUE;
1815 if (write_errno_nospace)
1816 goto ret;
1818 if (return_status != FILE_RETRY)
1819 goto ret;
1821 /* User pressed "Retry". Will the next mc_write() call be successful?
1822 * Reset error flag to be ready for that. */
1823 write_errno_nospace = FALSE;
1827 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1829 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1830 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1832 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1834 copy_file_file_display_progress (tctx, ctx,
1835 tv_current,
1836 tv_transfer_start, file_size, n_read_total);
1837 tv_last_update = tv_current;
1839 is_first_time = FALSE;
1841 if (update_secs > FILEOP_STALLING_INTERVAL)
1843 stalled_msg = _("(stalled)");
1847 gboolean force_update;
1849 force_update =
1850 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1852 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1854 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1855 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1858 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1859 force_update);
1861 mc_refresh ();
1863 return_status = check_progress_buttons (ctx);
1865 if (return_status != FILE_CONT)
1867 mc_refresh ();
1868 goto ret;
1873 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1875 ret:
1876 rotate_dash (FALSE);
1877 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1879 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1880 if (temp_status == FILE_RETRY)
1881 continue;
1882 if (temp_status == FILE_ABORT)
1883 return_status = temp_status;
1884 if (temp_status == FILE_SKIPALL)
1885 ctx->skip_all = TRUE;
1886 break;
1889 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1891 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1892 if (temp_status == FILE_RETRY)
1893 continue;
1894 if (temp_status == FILE_SKIPALL)
1895 ctx->skip_all = TRUE;
1896 return_status = temp_status;
1897 break;
1900 if (dst_status == DEST_SHORT)
1902 /* Remove short file */
1903 int result = 0;
1905 /* In case of copy/move to full partition, keep source file
1906 * and remove incomplete destination one */
1907 if (!write_errno_nospace)
1908 result = query_dialog (Q_ ("DialogTitle|Copy"),
1909 _("Incomplete file was retrieved. Keep it?"),
1910 D_ERROR, 2, _("&Delete"), _("&Keep"));
1911 if (result == 0)
1912 mc_unlink (dst_vpath);
1914 else if (dst_status == DEST_FULL)
1916 /* Copy has succeeded */
1917 if (!appending && ctx->preserve_uidgid)
1919 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1921 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1922 if (temp_status == FILE_RETRY)
1923 continue;
1924 if (temp_status == FILE_SKIPALL)
1926 ctx->skip_all = TRUE;
1927 return_status = FILE_CONT;
1929 if (temp_status == FILE_SKIP)
1930 return_status = FILE_CONT;
1931 break;
1935 if (!appending)
1937 if (ctx->preserve)
1939 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1941 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1942 if (temp_status == FILE_RETRY)
1943 continue;
1944 if (temp_status == FILE_SKIPALL)
1946 ctx->skip_all = TRUE;
1947 return_status = FILE_CONT;
1949 if (temp_status == FILE_SKIP)
1950 return_status = FILE_CONT;
1951 break;
1954 else if (!dst_exists)
1956 src_mode = umask (-1);
1957 umask (src_mode);
1958 src_mode = 0100666 & ~src_mode;
1959 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
1961 mc_utime (dst_vpath, &utb);
1965 if (return_status == FILE_CONT)
1966 return_status = progress_update_one (tctx, ctx, file_size);
1968 ret_fast:
1969 vfs_path_free (src_vpath);
1970 vfs_path_free (dst_vpath);
1971 return return_status;
1974 /* --------------------------------------------------------------------------------------------- */
1976 * I think these copy_*_* functions should have a return type.
1977 * anyway, this function *must* have two directories as arguments.
1979 /* FIXME: This function needs to check the return values of the
1980 function calls */
1982 FileProgressStatus
1983 copy_dir_dir (FileOpTotalContext * tctx, file_op_context_t * ctx, const char *s, const char *d,
1984 gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs)
1986 struct dirent *next;
1987 struct stat buf, cbuf;
1988 DIR *reading;
1989 FileProgressStatus return_status = FILE_CONT;
1990 struct link *lp;
1991 vfs_path_t *src_vpath, *dst_vpath;
1992 gboolean do_mkdir = TRUE;
1994 src_vpath = vfs_path_from_str (s);
1995 dst_vpath = vfs_path_from_str (d);
1997 /* First get the mode of the source dir */
1999 retry_src_stat:
2000 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
2002 if (ctx->skip_all)
2003 return_status = FILE_SKIPALL;
2004 else
2006 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
2007 if (return_status == FILE_RETRY)
2008 goto retry_src_stat;
2009 if (return_status == FILE_SKIPALL)
2010 ctx->skip_all = TRUE;
2012 goto ret_fast;
2015 if (is_in_linklist (dest_dirs, src_vpath, &cbuf))
2017 /* Don't copy a directory we created before (we don't want to copy
2018 infinitely if a directory is copied into itself) */
2019 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2020 return_status = FILE_CONT;
2021 goto ret_fast;
2024 /* Hmm, hardlink to directory??? - Norbert */
2025 /* FIXME: In this step we should do something
2026 in case the destination already exist */
2027 /* Check the hardlinks */
2028 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &cbuf))
2030 /* We have made a hardlink - no more processing is necessary */
2031 goto ret_fast;
2034 if (!S_ISDIR (cbuf.st_mode))
2036 if (ctx->skip_all)
2037 return_status = FILE_SKIPALL;
2038 else
2040 return_status = file_error (_("Source \"%s\" is not a directory\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 (parent_dirs, src_vpath, &cbuf))
2051 /* we found a cyclic symbolic link */
2052 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
2053 return_status = FILE_SKIP;
2054 goto ret_fast;
2057 lp = g_new0 (struct link, 1);
2058 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
2059 lp->ino = cbuf.st_ino;
2060 lp->dev = cbuf.st_dev;
2061 parent_dirs = g_slist_prepend (parent_dirs, lp);
2063 retry_dst_stat:
2064 /* Now, check if the dest dir exists, if not, create it. */
2065 if (mc_stat (dst_vpath, &buf) != 0)
2067 /* Here the dir doesn't exist : make it ! */
2068 if (move_over && mc_rename (src_vpath, dst_vpath) == 0)
2070 return_status = FILE_CONT;
2071 goto ret;
2074 else
2077 * If the destination directory exists, we want to copy the whole
2078 * directory, but we only want this to happen once.
2080 * Escape sequences added to the * to compiler warnings.
2081 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2082 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2084 if (!S_ISDIR (buf.st_mode))
2086 if (ctx->skip_all)
2087 return_status = FILE_SKIPALL;
2088 else
2090 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2091 if (return_status == FILE_SKIPALL)
2092 ctx->skip_all = TRUE;
2093 if (return_status == FILE_RETRY)
2094 goto retry_dst_stat;
2096 goto ret;
2098 /* Dive into subdir if exists */
2099 if (toplevel && ctx->dive_into_subdirs)
2101 vfs_path_t *tmp;
2103 tmp = dst_vpath;
2104 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), NULL);
2105 vfs_path_free (tmp);
2108 else
2109 do_mkdir = FALSE;
2112 d = vfs_path_as_str (dst_vpath);
2114 if (do_mkdir)
2116 while (my_mkdir (dst_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2118 if (ctx->skip_all)
2119 return_status = FILE_SKIPALL;
2120 else
2122 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), d);
2123 if (return_status == FILE_SKIPALL)
2124 ctx->skip_all = TRUE;
2126 if (return_status != FILE_RETRY)
2127 goto ret;
2130 lp = g_new0 (struct link, 1);
2131 mc_stat (dst_vpath, &buf);
2132 lp->vfs = vfs_path_get_by_index (dst_vpath, -1)->class;
2133 lp->ino = buf.st_ino;
2134 lp->dev = buf.st_dev;
2135 dest_dirs = g_slist_prepend (dest_dirs, lp);
2138 if (ctx->preserve_uidgid)
2140 while (mc_chown (dst_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2142 if (ctx->skip_all)
2143 return_status = FILE_SKIPALL;
2144 else
2146 return_status = file_error (_("Cannot chown target directory \"%s\"\n%s"), d);
2147 if (return_status == FILE_SKIPALL)
2148 ctx->skip_all = TRUE;
2150 if (return_status != FILE_RETRY)
2151 goto ret;
2155 /* open the source dir for reading */
2156 reading = mc_opendir (src_vpath);
2157 if (reading == NULL)
2158 goto ret;
2160 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2162 char *path;
2163 vfs_path_t *tmp_vpath;
2166 * Now, we don't want '.' and '..' to be created / copied at any time
2168 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
2169 continue;
2171 /* get the filename and add it to the src directory */
2172 path = mc_build_filename (s, next->d_name, NULL);
2173 tmp_vpath = vfs_path_from_str (path);
2175 (*ctx->stat_func) (tmp_vpath, &buf);
2176 if (S_ISDIR (buf.st_mode))
2178 char *mdpath;
2180 mdpath = mc_build_filename (d, next->d_name, NULL);
2182 * From here, we just intend to recursively copy subdirs, not
2183 * the double functionality of copying different when the target
2184 * dir already exists. So, we give the recursive call the flag 0
2185 * meaning no toplevel.
2187 return_status =
2188 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2189 g_free (mdpath);
2191 else
2193 char *dest_file;
2195 dest_file = mc_build_filename (d, x_basename (path), NULL);
2196 return_status = copy_file_file (tctx, ctx, path, dest_file);
2197 g_free (dest_file);
2200 g_free (path);
2202 if (do_delete && return_status == FILE_CONT)
2204 if (ctx->erase_at_end)
2206 lp = g_new0 (struct link, 1);
2207 lp->src_vpath = tmp_vpath;
2208 lp->st_mode = buf.st_mode;
2209 erase_list = g_slist_append (erase_list, lp);
2210 tmp_vpath = NULL;
2212 else if (S_ISDIR (buf.st_mode))
2213 return_status = erase_dir_iff_empty (ctx, tmp_vpath, tctx->progress_count);
2214 else
2215 return_status = erase_file (tctx, ctx, tmp_vpath);
2217 vfs_path_free (tmp_vpath);
2219 mc_closedir (reading);
2221 if (ctx->preserve)
2223 struct utimbuf utb;
2225 mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill);
2226 utb.actime = cbuf.st_atime;
2227 utb.modtime = cbuf.st_mtime;
2228 mc_utime (dst_vpath, &utb);
2230 else
2232 cbuf.st_mode = umask (-1);
2233 umask (cbuf.st_mode);
2234 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2235 mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill);
2238 ret:
2239 free_link (parent_dirs->data);
2240 g_slist_free_1 (parent_dirs);
2241 ret_fast:
2242 vfs_path_free (src_vpath);
2243 vfs_path_free (dst_vpath);
2244 return return_status;
2247 /* }}} */
2249 /* --------------------------------------------------------------------------------------------- */
2250 /* {{{ Move routines */
2252 FileProgressStatus
2253 move_dir_dir (FileOpTotalContext * tctx, file_op_context_t * ctx, const char *s, const char *d)
2255 struct stat sbuf, dbuf;
2256 FileProgressStatus return_status;
2257 gboolean move_over = FALSE;
2258 gboolean dstat_ok;
2259 vfs_path_t *src_vpath, *dst_vpath;
2261 src_vpath = vfs_path_from_str (s);
2262 dst_vpath = vfs_path_from_str (d);
2264 file_progress_show_source (ctx, src_vpath);
2265 file_progress_show_target (ctx, dst_vpath);
2267 if (check_progress_buttons (ctx) == FILE_ABORT)
2269 return_status = FILE_ABORT;
2270 goto ret_fast;
2273 mc_refresh ();
2275 mc_stat (src_vpath, &sbuf);
2277 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2278 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2280 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2281 goto ret_fast;
2284 if (!dstat_ok)
2285 ; /* destination doesn't exist */
2286 else if (!ctx->dive_into_subdirs)
2287 move_over = TRUE;
2288 else
2290 vfs_path_t *tmp;
2292 tmp = dst_vpath;
2293 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), NULL);
2294 vfs_path_free (tmp);
2297 d = vfs_path_as_str (dst_vpath);
2299 /* Check if the user inputted an existing dir */
2300 retry_dst_stat:
2301 if (mc_stat (dst_vpath, &dbuf) == 0)
2303 if (move_over)
2305 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, TRUE, TRUE, NULL);
2307 if (return_status != FILE_CONT)
2308 goto ret;
2309 goto oktoret;
2311 else if (ctx->skip_all)
2312 return_status = FILE_SKIPALL;
2313 else
2315 if (S_ISDIR (dbuf.st_mode))
2316 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), d);
2317 else
2318 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), d);
2319 if (return_status == FILE_SKIPALL)
2320 ctx->skip_all = TRUE;
2321 if (return_status == FILE_RETRY)
2322 goto retry_dst_stat;
2325 goto ret_fast;
2328 retry_rename:
2329 if (mc_rename (src_vpath, dst_vpath) == 0)
2331 return_status = FILE_CONT;
2332 goto ret;
2335 if (errno != EXDEV)
2337 if (!ctx->skip_all)
2339 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2340 if (return_status == FILE_SKIPALL)
2341 ctx->skip_all = TRUE;
2342 if (return_status == FILE_RETRY)
2343 goto retry_rename;
2345 goto ret;
2347 /* Failed because of filesystem boundary -> copy dir instead */
2348 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, FALSE, TRUE, NULL);
2350 if (return_status != FILE_CONT)
2351 goto ret;
2352 oktoret:
2353 file_progress_show_source (ctx, NULL);
2354 file_progress_show (ctx, 0, 0, "", FALSE);
2356 return_status = check_progress_buttons (ctx);
2357 if (return_status != FILE_CONT)
2358 goto ret;
2360 mc_refresh ();
2361 if (ctx->erase_at_end)
2363 while (erase_list != NULL && return_status != FILE_ABORT)
2365 struct link *lp = (struct link *) erase_list->data;
2367 if (S_ISDIR (lp->st_mode))
2368 return_status = erase_dir_iff_empty (ctx, lp->src_vpath, tctx->progress_count);
2369 else
2370 return_status = erase_file (tctx, ctx, lp->src_vpath);
2372 erase_list = g_slist_remove (erase_list, lp);
2373 free_link (lp);
2376 erase_dir_iff_empty (ctx, src_vpath, tctx->progress_count);
2378 ret:
2379 erase_list = free_linklist (erase_list);
2380 ret_fast:
2381 vfs_path_free (src_vpath);
2382 vfs_path_free (dst_vpath);
2383 return return_status;
2386 /* }}} */
2388 /* --------------------------------------------------------------------------------------------- */
2389 /* {{{ Erase routines */
2391 FileProgressStatus
2392 erase_dir (FileOpTotalContext * tctx, file_op_context_t * ctx, const vfs_path_t * s_vpath)
2394 FileProgressStatus error;
2396 file_progress_show_deleting (ctx, vfs_path_as_str (s_vpath), NULL);
2397 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2398 if (check_progress_buttons (ctx) == FILE_ABORT)
2399 return FILE_ABORT;
2401 mc_refresh ();
2403 /* The old way to detect a non empty directory was:
2404 error = my_rmdir (s);
2405 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2406 For the linux user space nfs server (nfs-server-2.2beta29-2)
2407 we would have to check also for EIO. I hope the new way is
2408 fool proof. (Norbert)
2410 error = check_dir_is_empty (s_vpath);
2411 if (error == 0)
2412 { /* not empty */
2413 error = query_recursive (ctx, vfs_path_as_str (s_vpath));
2414 if (error == FILE_CONT)
2415 error = recursive_erase (tctx, ctx, s_vpath);
2416 return error;
2419 while (my_rmdir (vfs_path_as_str (s_vpath)) == -1 && !ctx->skip_all)
2421 error = file_error (_("Cannot remove directory \"%s\"\n%s"), vfs_path_as_str (s_vpath));
2422 if (error != FILE_RETRY)
2423 return error;
2426 return FILE_CONT;
2429 /* }}} */
2431 /* --------------------------------------------------------------------------------------------- */
2432 /* {{{ Panel operate routines */
2434 ComputeDirSizeUI *
2435 compute_dir_size_create_ui (gboolean allow_skip)
2437 ComputeDirSizeUI *ui;
2439 const char *b1_name = N_("&Abort");
2440 const char *b2_name = N_("&Skip");
2441 int b1_width, b2_width = 0, b_width = 0;
2442 int b1_x;
2443 int ui_width;
2444 Widget *b;
2446 #ifdef ENABLE_NLS
2447 b1_name = _(b1_name);
2448 b2_name = _(b2_name);
2449 #endif
2451 b1_width = str_term_width1 (b1_name) + 4;
2452 if (allow_skip)
2453 b2_width = str_term_width1 (b2_name) + 4 + 1;
2454 b_width = b1_width + b2_width;
2456 ui = g_new (ComputeDirSizeUI, 1);
2458 ui_width = max (COLS / 2, b_width + 6);
2459 ui->dlg = dlg_create (TRUE, 0, 0, 8, ui_width, dialog_colors, NULL, NULL, NULL,
2460 _("Directory scanning"), DLG_CENTER);
2462 ui->dirname = label_new (2, 3, "");
2463 add_widget (ui->dlg, ui->dirname);
2464 add_widget (ui->dlg, hline_new (4, -1, -1));
2465 b1_x = (ui_width - b_width) / 2;
2466 b = WIDGET (button_new (5, b1_x, FILE_ABORT, NORMAL_BUTTON, b1_name, NULL));
2467 add_widget (ui->dlg, b);
2468 if (allow_skip)
2470 add_widget (ui->dlg,
2471 button_new (5, b1_x + 1 + b1_width, FILE_SKIP, NORMAL_BUTTON, b2_name, NULL));
2472 dlg_select_widget (b);
2475 /* We will manage the dialog without any help,
2476 that's why we have to call dlg_init */
2477 dlg_init (ui->dlg);
2479 return ui;
2482 /* --------------------------------------------------------------------------------------------- */
2484 void
2485 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2487 if (ui != NULL)
2489 /* schedule to update passive panel */
2490 other_panel->dirty = 1;
2492 /* close and destroy dialog */
2493 dlg_run_done (ui->dlg);
2494 dlg_destroy (ui->dlg);
2495 g_free (ui);
2499 /* --------------------------------------------------------------------------------------------- */
2501 FileProgressStatus
2502 compute_dir_size_update_ui (void *ui, const vfs_path_t * dirname_vpath, size_t dir_count,
2503 uintmax_t total_size)
2505 ComputeDirSizeUI *this = (ComputeDirSizeUI *) ui;
2506 int c;
2507 Gpm_Event event;
2508 char buffer[BUF_1K];
2510 if (ui == NULL)
2511 return FILE_CONT;
2513 g_snprintf (buffer, sizeof (buffer), _("%s\nDirectories: %zd, total size: %s"),
2514 str_trunc (vfs_path_as_str (dirname_vpath), WIDGET (this->dlg)->cols - 6),
2515 dir_count, size_trunc_sep (total_size, panels_options.kilobyte_si));
2516 label_set_text (this->dirname, buffer);
2518 event.x = -1; /* Don't show the GPM cursor */
2519 c = tty_get_event (&event, FALSE, FALSE);
2520 if (c == EV_NONE)
2521 return FILE_CONT;
2523 /* Reinitialize to avoid old values after events other than
2524 selecting a button */
2525 this->dlg->ret_value = FILE_CONT;
2527 dlg_process_event (this->dlg, c, &event);
2529 switch (this->dlg->ret_value)
2531 case B_CANCEL:
2532 case FILE_ABORT:
2533 return FILE_ABORT;
2534 case FILE_SKIP:
2535 return FILE_SKIP;
2536 default:
2537 return FILE_CONT;
2541 /* --------------------------------------------------------------------------------------------- */
2543 * compute_dir_size:
2545 * Computes the number of bytes used by the files in a directory
2548 FileProgressStatus
2549 compute_dir_size (const vfs_path_t * dirname_vpath, void *ui, compute_dir_size_callback cback,
2550 size_t * ret_dir_count, size_t * ret_marked_count, uintmax_t * ret_total,
2551 gboolean compute_symlinks)
2553 return do_compute_dir_size (dirname_vpath, ui, cback, ret_dir_count, ret_marked_count,
2554 ret_total, compute_symlinks);
2557 /* --------------------------------------------------------------------------------------------- */
2559 * panel_operate:
2561 * Performs one of the operations on the selection on the source_panel
2562 * (copy, delete, move).
2564 * Returns TRUE if did change the directory
2565 * structure, Returns FALSE if user aborted
2567 * force_single forces operation on the current entry and affects
2568 * default destination. Current filename is used as default.
2571 gboolean
2572 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2574 WPanel *panel = PANEL (source_panel);
2575 const gboolean single_entry = force_single || (panel->marked <= 1)
2576 || (get_current_type () == view_tree);
2578 char *source = NULL;
2579 #ifdef WITH_FULL_PATHS
2580 vfs_path_t *source_with_vpath = NULL;
2581 #else
2582 #define source_with_path source
2583 #endif /* !WITH_FULL_PATHS */
2584 char *dest = NULL;
2585 vfs_path_t *dest_vpath = NULL;
2586 char *temp = NULL;
2587 char *save_cwd = NULL, *save_dest = NULL;
2588 struct stat src_stat;
2589 gboolean ret_val = TRUE;
2590 int i;
2591 FileProgressStatus value;
2592 file_op_context_t *ctx;
2593 FileOpTotalContext *tctx;
2594 vfs_path_t *tmp_vpath;
2595 filegui_dialog_type_t dialog_type = FILEGUI_DIALOG_ONE_ITEM;
2597 gboolean do_bg = FALSE; /* do background operation? */
2599 static gboolean i18n_flag = FALSE;
2600 if (!i18n_flag)
2602 for (i = G_N_ELEMENTS (op_names); i-- != 0;)
2603 op_names[i] = Q_ (op_names[i]);
2604 i18n_flag = TRUE;
2607 linklist = free_linklist (linklist);
2608 dest_dirs = free_linklist (dest_dirs);
2610 if (single_entry)
2612 vfs_path_t *source_vpath;
2614 if (force_single)
2615 source = g_strdup (selection (panel)->fname);
2616 else
2617 source = panel_get_file (panel);
2619 if (DIR_IS_DOTDOT (source))
2621 g_free (source);
2622 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2623 return FALSE;
2626 source_vpath = vfs_path_from_str (source);
2627 /* Update stat to get actual info */
2628 if (mc_lstat (source_vpath, &src_stat) != 0)
2630 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2631 path_trunc (source, 30), unix_error_string (errno));
2633 /* Directory was changed outside MC. Reload it forced */
2634 if (!panel->is_panelized)
2636 panel_update_flags_t flags = UP_RELOAD;
2638 /* don't update panelized panel */
2639 if (get_other_type () == view_listing && other_panel->is_panelized)
2640 flags |= UP_ONLY_CURRENT;
2642 update_panels (flags, UP_KEEPSEL);
2644 vfs_path_free (source_vpath);
2645 return FALSE;
2647 vfs_path_free (source_vpath);
2650 ctx = file_op_context_new (operation);
2652 /* Show confirmation dialog */
2653 if (operation != OP_DELETE)
2655 char *tmp_dest_dir, *dest_dir;
2656 char *format;
2658 /* Forced single operations default to the original name */
2659 if (force_single)
2660 tmp_dest_dir = g_strdup (source);
2661 else if (get_other_type () == view_listing)
2662 tmp_dest_dir = g_strdup (vfs_path_as_str (other_panel->cwd_vpath));
2663 else
2664 tmp_dest_dir = g_strdup (vfs_path_as_str (panel->cwd_vpath));
2666 * Add trailing backslash only when do non-local ops.
2667 * It saves user from occasional file renames (when destination
2668 * dir is deleted)
2670 if (!force_single && tmp_dest_dir[0] != '\0'
2671 && tmp_dest_dir[strlen (tmp_dest_dir) - 1] != PATH_SEP)
2673 /* add trailing separator */
2674 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
2675 g_free (tmp_dest_dir);
2677 else
2679 /* just copy */
2680 dest_dir = tmp_dest_dir;
2682 if (dest_dir == NULL)
2684 ret_val = FALSE;
2685 goto ret_fast;
2688 /* Generate confirmation prompt */
2689 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2691 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2692 source != NULL ? (void *) source
2693 : (void *) &panel->marked, dest_dir, &do_bg);
2695 g_free (format);
2696 g_free (dest_dir);
2698 if (dest == NULL || dest[0] == '\0')
2700 g_free (dest);
2701 ret_val = FALSE;
2702 goto ret_fast;
2704 dest_vpath = vfs_path_from_str (dest);
2706 else if (confirm_delete)
2708 char *format;
2709 char fmd_buf[BUF_MEDIUM];
2711 /* Generate confirmation prompt */
2712 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2714 if (source == NULL)
2715 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2716 else
2718 const int fmd_xlen = 64;
2719 i = fmd_xlen - str_term_width1 (format) - 4;
2720 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2723 g_free (format);
2725 if (safe_delete)
2726 query_set_sel (1);
2728 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2730 if (i != 0)
2732 ret_val = FALSE;
2733 goto ret_fast;
2737 tctx = file_op_total_context_new ();
2738 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2740 #ifdef ENABLE_BACKGROUND
2741 /* Did the user select to do a background operation? */
2742 if (do_bg)
2744 int v;
2746 v = do_background (ctx,
2747 g_strconcat (op_names[operation], ": ",
2748 vfs_path_as_str (panel->cwd_vpath), (char *) NULL));
2749 if (v == -1)
2750 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2752 /* If we are the parent */
2753 if (v == 1)
2755 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
2757 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
2758 vfs_path_free (dest_vpath);
2759 g_free (dest);
2760 /* file_op_context_destroy (ctx); */
2761 return FALSE;
2764 else
2765 #endif /* ENABLE_BACKGROUND */
2767 if (operation == OP_DELETE)
2768 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2769 else if (single_entry && S_ISDIR (selection (panel)->st.st_mode))
2770 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2771 else if (single_entry || force_single)
2772 dialog_type = FILEGUI_DIALOG_ONE_ITEM;
2773 else
2774 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2777 /* Initialize things */
2778 /* We do not want to trash cache every time file is
2779 created/touched. However, this will make our cache contain
2780 invalid data. */
2781 if ((dest != NULL)
2782 && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
2783 save_dest = g_strdup (dest);
2785 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
2786 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
2787 save_cwd = g_strdup (vfs_path_as_str (panel->cwd_vpath));
2789 /* Now, let's do the job */
2791 /* This code is only called by the tree and panel code */
2792 if (single_entry)
2794 /* We now have ETA in all cases */
2796 /* One file: FIXME mc_chdir will take user out of any vfs */
2797 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2799 vfs_path_t *vpath;
2800 int chdir_retcode;
2802 vpath = vfs_path_from_str (PATH_SEP_STR);
2803 chdir_retcode = mc_chdir (vpath);
2804 vfs_path_free (vpath);
2805 if (chdir_retcode < 0)
2807 ret_val = FALSE;
2808 goto clean_up;
2812 /* The source and src_stat variables have been initialized before */
2813 #ifdef WITH_FULL_PATHS
2814 if (g_path_is_absolute (source))
2815 source_with_vpath = vfs_path_from_str (source);
2816 else
2817 source_with_vpath = vfs_path_append_new (panel->cwd_vpath, source, (char *) NULL);
2818 #endif /* WITH_FULL_PATHS */
2819 if (panel_operate_init_totals (panel, vfs_path_as_str (source_with_vpath), ctx, dialog_type)
2820 == FILE_CONT)
2822 if (operation == OP_DELETE)
2824 if (S_ISDIR (src_stat.st_mode))
2825 value = erase_dir (tctx, ctx, source_with_vpath);
2826 else
2827 value = erase_file (tctx, ctx, source_with_vpath);
2829 else
2831 temp = transform_source (ctx, source_with_vpath);
2832 if (temp == NULL)
2833 value = transform_error;
2834 else
2836 char *repl_dest, *temp2;
2838 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2839 if (ctx->search_handle->error != MC_SEARCH_E_OK)
2841 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
2842 g_free (repl_dest);
2843 goto clean_up;
2846 temp2 = mc_build_filename (repl_dest, temp, NULL);
2847 g_free (temp);
2848 g_free (repl_dest);
2849 g_free (dest);
2850 vfs_path_free (dest_vpath);
2851 dest = temp2;
2852 dest_vpath = vfs_path_from_str (dest);
2854 switch (operation)
2856 case OP_COPY:
2857 /* we use file_mask_op_follow_links only with OP_COPY */
2858 ctx->stat_func (source_with_vpath, &src_stat);
2860 if (S_ISDIR (src_stat.st_mode))
2861 value =
2862 copy_dir_dir (tctx, ctx, vfs_path_as_str (source_with_vpath),
2863 dest, TRUE, FALSE, FALSE, NULL);
2864 else
2865 value =
2866 copy_file_file (tctx, ctx, vfs_path_as_str (source_with_vpath),
2867 dest);
2868 break;
2870 case OP_MOVE:
2871 if (S_ISDIR (src_stat.st_mode))
2872 value =
2873 move_dir_dir (tctx, ctx, vfs_path_as_str (source_with_vpath), dest);
2874 else
2875 value =
2876 move_file_file (tctx, ctx, vfs_path_as_str (source_with_vpath),
2877 dest);
2878 break;
2880 default:
2881 /* Unknown file operation */
2882 abort ();
2885 } /* Copy or move operation */
2887 if ((value == FILE_CONT) && !force_single)
2888 unmark_files (panel);
2891 else
2893 /* Many files */
2895 /* Check destination for copy or move operation */
2896 while (operation != OP_DELETE)
2898 int dst_result;
2899 struct stat dst_stat;
2901 dst_result = mc_stat (dest_vpath, &dst_stat);
2903 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2904 break;
2906 if (ctx->skip_all
2907 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2908 goto clean_up;
2911 if (panel_operate_init_totals (panel, NULL, ctx, dialog_type) == FILE_CONT)
2913 /* Loop for every file, perform the actual copy operation */
2914 for (i = 0; i < panel->dir.len; i++)
2916 const char *source2;
2918 if (!panel->dir.list[i].f.marked)
2919 continue; /* Skip the unmarked ones */
2921 source2 = panel->dir.list[i].fname;
2922 src_stat = panel->dir.list[i].st;
2924 #ifdef WITH_FULL_PATHS
2925 vfs_path_free (source_with_vpath);
2926 if (g_path_is_absolute (source2))
2927 source_with_vpath = vfs_path_from_str (source2);
2928 else
2929 source_with_vpath =
2930 vfs_path_append_new (panel->cwd_vpath, source2, (char *) NULL);
2931 #endif /* WITH_FULL_PATHS */
2933 if (operation == OP_DELETE)
2935 if (S_ISDIR (src_stat.st_mode))
2936 value = erase_dir (tctx, ctx, source_with_vpath);
2937 else
2938 value = erase_file (tctx, ctx, source_with_vpath);
2940 else
2942 temp = transform_source (ctx, source_with_vpath);
2943 if (temp == NULL)
2944 value = transform_error;
2945 else
2947 char *temp2, *repl_dest, *source_with_path_str;
2949 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2950 if (ctx->search_handle->error != MC_SEARCH_E_OK)
2952 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
2953 g_free (repl_dest);
2954 goto clean_up;
2957 temp2 = mc_build_filename (repl_dest, temp, NULL);
2958 g_free (temp);
2959 g_free (repl_dest);
2960 source_with_path_str =
2961 strutils_shell_unescape (vfs_path_as_str (source_with_vpath));
2962 temp = strutils_shell_unescape (temp2);
2963 g_free (temp2);
2965 switch (operation)
2967 case OP_COPY:
2968 /* we use file_mask_op_follow_links only with OP_COPY */
2970 vfs_path_t *vpath;
2972 vpath = vfs_path_from_str (source_with_path_str);
2973 ctx->stat_func (vpath, &src_stat);
2974 vfs_path_free (vpath);
2976 if (S_ISDIR (src_stat.st_mode))
2977 value = copy_dir_dir (tctx, ctx, source_with_path_str, temp,
2978 TRUE, FALSE, FALSE, NULL);
2979 else
2980 value = copy_file_file (tctx, ctx, source_with_path_str, temp);
2981 dest_dirs = free_linklist (dest_dirs);
2982 break;
2984 case OP_MOVE:
2985 if (S_ISDIR (src_stat.st_mode))
2986 value = move_dir_dir (tctx, ctx, source_with_path_str, temp);
2987 else
2988 value = move_file_file (tctx, ctx, source_with_path_str, temp);
2989 break;
2991 default:
2992 /* Unknown file operation */
2993 abort ();
2996 g_free (temp);
2998 } /* Copy or move operation */
3000 if (value == FILE_ABORT)
3001 break;
3003 if (value == FILE_CONT)
3004 do_file_mark (panel, i, 0);
3006 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3008 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3009 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3012 if (operation != OP_DELETE)
3013 file_progress_show (ctx, 0, 0, "", FALSE);
3015 if (check_progress_buttons (ctx) == FILE_ABORT)
3016 break;
3018 mc_refresh ();
3019 } /* Loop for every file */
3021 } /* Many entries */
3023 clean_up:
3024 /* Clean up */
3025 if (save_cwd != NULL)
3027 tmp_vpath = vfs_path_from_str (save_cwd);
3028 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3029 vfs_path_free (tmp_vpath);
3030 g_free (save_cwd);
3033 if (save_dest != NULL)
3035 tmp_vpath = vfs_path_from_str (save_dest);
3036 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3037 vfs_path_free (tmp_vpath);
3038 g_free (save_dest);
3041 linklist = free_linklist (linklist);
3042 dest_dirs = free_linklist (dest_dirs);
3043 #ifdef WITH_FULL_PATHS
3044 vfs_path_free (source_with_vpath);
3045 #endif /* WITH_FULL_PATHS */
3046 g_free (dest);
3047 vfs_path_free (dest_vpath);
3048 g_free (ctx->dest_mask);
3049 ctx->dest_mask = NULL;
3051 #ifdef ENABLE_BACKGROUND
3052 /* Let our parent know we are saying bye bye */
3053 if (mc_global.we_are_background)
3055 int cur_pid = getpid ();
3056 /* Send pid to parent with child context, it is fork and
3057 don't modify real parent ctx */
3058 ctx->pid = cur_pid;
3059 parent_call ((void *) end_bg_process, ctx, 0);
3061 vfs_shut ();
3062 my_exit (EXIT_SUCCESS);
3064 #endif /* ENABLE_BACKGROUND */
3066 file_op_total_context_destroy (tctx);
3067 ret_fast:
3068 file_op_context_destroy (ctx);
3069 g_free (source);
3071 return ret_val;
3074 /* }}} */
3076 /* --------------------------------------------------------------------------------------------- */
3077 /* {{{ Query/status report routines */
3078 /** Report error with one file */
3079 FileProgressStatus
3080 file_error (const char *format, const char *file)
3082 char buf[BUF_MEDIUM];
3084 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3086 return do_file_error (buf);
3089 /* --------------------------------------------------------------------------------------------- */
3092 Cause emacs to enter folding mode for this file:
3093 Local variables:
3094 end: