cppcheck: reduce variable scope.
[midnight-commander.git] / src / filemanager / file.c
blobf2f3b631bbcaf3fdc1aa23ed237743766f07009f
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 * %e - "to:" or question mark for delete
155 * xgettext:no-c-format */
156 static const char *one_format = N_("%o %f \"%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:"),
168 N_("to:")
171 static const char *question_format = N_("%s?");
173 /*** file scope variables ************************************************************************/
175 /* the hard link cache */
176 static GSList *linklist = NULL;
178 /* the files-to-be-erased list */
179 static GSList *erase_list = NULL;
182 * In copy_dir_dir we use two additional single linked lists: The first -
183 * variable name 'parent_dirs' - holds information about already copied
184 * directories and is used to detect cyclic symbolic links.
185 * The second ('dest_dirs' below) holds information about just created
186 * target directories and is used to detect when an directory is copied
187 * into itself (we don't want to copy infinitly).
188 * Both lists don't use the linkcount and name structure members of struct
189 * link.
191 static GSList *dest_dirs = NULL;
193 static FileProgressStatus transform_error = FILE_CONT;
195 /*** file scope functions ************************************************************************/
196 /* --------------------------------------------------------------------------------------------- */
198 static char *
199 transform_source (FileOpContext * ctx, const vfs_path_t * source_vpath)
201 char *s, *q;
202 char *fnsource;
204 s = g_strdup (vfs_path_as_str (source_vpath));
206 /* We remove \n from the filename since regex routines would use \n as an anchor */
207 /* this is just to be allowed to maniupulate file names with \n on it */
208 for (q = s; *q != '\0'; q++)
209 if (*q == '\n')
210 *q = ' ';
212 fnsource = (char *) x_basename (s);
214 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
216 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
217 if (ctx->search_handle->error != MC_SEARCH_E_OK)
219 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
220 q = NULL;
221 transform_error = FILE_ABORT;
224 else
226 q = NULL;
227 transform_error = FILE_SKIP;
230 g_free (s);
231 return q;
234 /* --------------------------------------------------------------------------------------------- */
236 static void
237 free_link (void *data)
239 struct link *lp = (struct link *) data;
241 vfs_path_free (lp->src_vpath);
242 vfs_path_free (lp->dst_vpath);
243 g_free (lp);
246 /* --------------------------------------------------------------------------------------------- */
248 static void *
249 free_linklist (GSList * lp)
251 g_slist_foreach (lp, (GFunc) free_link, NULL);
252 g_slist_free (lp);
254 return NULL;
257 /* --------------------------------------------------------------------------------------------- */
259 static gboolean
260 is_in_linklist (const GSList * lp, const vfs_path_t * vpath, const struct stat *sb)
262 const struct vfs_class *class;
263 ino_t ino = sb->st_ino;
264 dev_t dev = sb->st_dev;
266 class = vfs_path_get_last_path_vfs (vpath);
268 for (; lp != NULL; lp = g_slist_next (lp))
270 const struct link *lnk = (const struct link *) lp->data;
272 if (lnk->vfs == class && lnk->ino == ino && lnk->dev == dev)
273 return TRUE;
275 return FALSE;
278 /* --------------------------------------------------------------------------------------------- */
280 * Check and made hardlink
282 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
283 * and a hardlink was successfully made
286 static gboolean
287 check_hardlinks (const vfs_path_t * src_vpath, const vfs_path_t * dst_vpath, struct stat *pstat)
289 GSList *lp;
290 struct link *lnk;
292 const struct vfs_class *my_vfs;
293 ino_t ino = pstat->st_ino;
294 dev_t dev = pstat->st_dev;
295 struct stat link_stat;
297 if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
298 return FALSE;
300 my_vfs = vfs_path_get_by_index (src_vpath, -1)->class;
302 for (lp = linklist; lp != NULL; lp = g_slist_next (lp))
304 lnk = (struct link *) lp->data;
306 if (lnk->vfs == my_vfs && lnk->ino == ino && lnk->dev == dev)
308 const struct vfs_class *lp_name_class;
309 int stat_result;
311 lp_name_class = vfs_path_get_last_path_vfs (lnk->src_vpath);
312 stat_result = mc_stat (lnk->src_vpath, &link_stat);
314 if (stat_result == 0 && link_stat.st_ino == ino
315 && link_stat.st_dev == dev && lp_name_class == my_vfs)
317 const struct vfs_class *p_class, *dst_name_class;
319 dst_name_class = vfs_path_get_last_path_vfs (dst_vpath);
320 p_class = vfs_path_get_last_path_vfs (lnk->dst_vpath);
322 if (dst_name_class == p_class &&
323 mc_stat (lnk->dst_vpath, &link_stat) == 0 &&
324 mc_link (lnk->dst_vpath, dst_vpath) == 0)
325 return TRUE;
328 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
329 return FALSE;
333 lnk = g_new0 (struct link, 1);
334 if (lnk != NULL)
336 lnk->vfs = my_vfs;
337 lnk->ino = ino;
338 lnk->dev = dev;
339 lnk->src_vpath = vfs_path_clone (src_vpath);
340 lnk->dst_vpath = vfs_path_clone (dst_vpath);
341 linklist = g_slist_prepend (linklist, lnk);
344 return FALSE;
347 /* --------------------------------------------------------------------------------------------- */
349 * Duplicate the contents of the symbolic link src_path in dst_path.
350 * Try to make a stable symlink if the option "stable symlink" was
351 * set in the file mask dialog.
352 * If dst_path is an existing symlink it will be deleted silently
353 * (upper levels take already care of existing files at dst_path).
356 static FileProgressStatus
357 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
359 char link_target[MC_MAXPATHLEN];
360 int len;
361 FileProgressStatus return_status;
362 struct stat sb;
363 vfs_path_t *src_vpath;
364 vfs_path_t *dst_vpath;
365 gboolean dst_is_symlink;
366 vfs_path_t *link_target_vpath = NULL;
368 src_vpath = vfs_path_from_str (src_path);
369 dst_vpath = vfs_path_from_str (dst_path);
370 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
372 retry_src_readlink:
373 len = mc_readlink (src_vpath, link_target, MC_MAXPATHLEN - 1);
374 if (len < 0)
376 if (ctx->skip_all)
377 return_status = FILE_SKIPALL;
378 else
380 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
381 if (return_status == FILE_SKIPALL)
382 ctx->skip_all = TRUE;
383 if (return_status == FILE_RETRY)
384 goto retry_src_readlink;
386 goto ret;
388 link_target[len] = 0;
390 if (ctx->stable_symlinks)
393 if (!vfs_file_is_local (src_vpath) || !vfs_file_is_local (dst_vpath))
395 message (D_ERROR, MSG_ERROR,
396 _("Cannot make stable symlinks across"
397 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
398 ctx->stable_symlinks = FALSE;
402 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
404 const char *r = strrchr (src_path, PATH_SEP);
406 if (r)
408 char *p;
409 vfs_path_t *q;
411 p = g_strndup (src_path, r - src_path + 1);
412 if (g_path_is_absolute (dst_path))
413 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
414 else
415 q = vfs_path_build_filename (p, dst_path, (char *) NULL);
417 if (vfs_path_tokens_count (q) > 1)
419 char *s;
420 vfs_path_t *tmp_vpath1, *tmp_vpath2;
422 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
423 s = g_strconcat (p, link_target, (char *) NULL);
424 g_free (p);
425 g_strlcpy (link_target, s, sizeof (link_target));
426 g_free (s);
427 tmp_vpath2 = vfs_path_from_str (link_target);
428 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
429 vfs_path_free (tmp_vpath1);
430 vfs_path_free (tmp_vpath2);
431 if (s)
433 g_strlcpy (link_target, s, sizeof (link_target));
434 g_free (s);
437 else
438 g_free (p);
439 vfs_path_free (q);
442 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
444 retry_dst_symlink:
445 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
447 /* Success */
448 return_status = FILE_CONT;
449 goto ret;
452 * if dst_exists, it is obvious that this had failed.
453 * We can delete the old symlink and try again...
455 if (dst_is_symlink)
457 if (mc_unlink (dst_vpath) == 0)
458 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
460 /* Success */
461 return_status = FILE_CONT;
462 goto ret;
465 if (ctx->skip_all)
466 return_status = FILE_SKIPALL;
467 else
469 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
470 if (return_status == FILE_SKIPALL)
471 ctx->skip_all = TRUE;
472 if (return_status == FILE_RETRY)
473 goto retry_dst_symlink;
476 ret:
477 vfs_path_free (src_vpath);
478 vfs_path_free (dst_vpath);
479 vfs_path_free (link_target_vpath);
480 return return_status;
483 /* --------------------------------------------------------------------------------------------- */
485 * do_compute_dir_size:
487 * Computes the number of bytes used by the files in a directory
490 static FileProgressStatus
491 do_compute_dir_size (const vfs_path_t * dirname_vpath, void *ui,
492 compute_dir_size_callback cback, size_t * dir_count, size_t * ret_marked,
493 uintmax_t * ret_total, gboolean compute_symlinks)
495 static unsigned short int update_ui_count = 0;
497 int res;
498 struct stat s;
499 DIR *dir;
500 struct dirent *dirent;
501 FileProgressStatus ret = FILE_CONT;
503 if (!compute_symlinks)
505 res = mc_lstat (dirname_vpath, &s);
506 if (res != 0)
507 return ret;
509 /* don't scan symlink to directory */
510 if (S_ISLNK (s.st_mode))
512 (*ret_marked)++;
513 *ret_total += (uintmax_t) s.st_size;
514 return ret;
518 (*dir_count)++;
520 dir = mc_opendir (dirname_vpath);
521 if (dir == NULL)
522 return ret;
524 while (ret == FILE_CONT && (dirent = mc_readdir (dir)) != NULL)
526 vfs_path_t *tmp_vpath;
528 if (DIR_IS_DOT (dirent->d_name) || DIR_IS_DOTDOT (dirent->d_name))
529 continue;
531 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, NULL);
533 res = mc_lstat (tmp_vpath, &s);
534 if (res == 0)
536 if (S_ISDIR (s.st_mode))
538 ret =
539 do_compute_dir_size (tmp_vpath, ui, cback, dir_count, ret_marked, ret_total,
540 compute_symlinks);
541 if (ret == FILE_CONT)
542 ret =
543 (cback == NULL) ? FILE_CONT : cback (ui, tmp_vpath, *dir_count, *ret_total);
545 else
547 (*ret_marked)++;
548 *ret_total += (uintmax_t) s.st_size;
550 update_ui_count++;
551 if ((update_ui_count & 31) == 0)
552 ret =
553 (cback == NULL) ? FILE_CONT : cback (ui, dirname_vpath, *dir_count,
554 *ret_total);
558 vfs_path_free (tmp_vpath);
561 mc_closedir (dir);
562 return ret;
565 /* --------------------------------------------------------------------------------------------- */
567 static FileProgressStatus
568 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add)
570 struct timeval tv_current;
571 static struct timeval tv_start = { };
573 tctx->progress_count++;
574 tctx->progress_bytes += (uintmax_t) add;
576 if (tv_start.tv_sec == 0)
578 gettimeofday (&tv_start, (struct timezone *) NULL);
580 gettimeofday (&tv_current, (struct timezone *) NULL);
581 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
583 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
585 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
586 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
588 tv_start.tv_sec = tv_current.tv_sec;
591 return check_progress_buttons (ctx);
594 /* --------------------------------------------------------------------------------------------- */
596 static FileProgressStatus
597 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
599 char *msg;
600 int result = 0;
601 const char *head_msg;
603 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
605 msg = g_strdup_printf (fmt, a, b);
606 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
607 g_free (msg);
608 do_refresh ();
610 return (result == 1) ? FILE_ABORT : FILE_SKIP;
613 /* --------------------------------------------------------------------------------------------- */
615 static FileProgressStatus
616 warn_same_file (const char *fmt, const char *a, const char *b)
618 #ifdef ENABLE_BACKGROUND
619 /* *INDENT-OFF* */
620 union
622 void *p;
623 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
624 const char *a, const char *b);
625 } pntr;
626 /* *INDENT-ON* */
628 pntr.f = real_warn_same_file;
630 if (mc_global.we_are_background)
631 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
632 #endif
633 return real_warn_same_file (Foreground, fmt, a, b);
636 /* --------------------------------------------------------------------------------------------- */
637 /* {{{ Query/status report routines */
639 static FileProgressStatus
640 real_do_file_error (enum OperationMode mode, const char *error)
642 int result;
643 const char *msg;
645 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
646 result =
647 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
649 switch (result)
651 case 0:
652 do_refresh ();
653 return FILE_SKIP;
655 case 1:
656 do_refresh ();
657 return FILE_SKIPALL;
659 case 2:
660 do_refresh ();
661 return FILE_RETRY;
663 case 3:
664 default:
665 return FILE_ABORT;
669 /* --------------------------------------------------------------------------------------------- */
671 static FileProgressStatus
672 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
675 if (ctx->recursive_result < RECURSIVE_ALWAYS)
678 const char *msg;
679 char *text;
681 msg = mode == Foreground
682 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
683 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
684 text = g_strdup_printf (msg, path_trunc (s, 30));
686 if (safe_delete)
687 query_set_sel (1);
689 ctx->recursive_result =
690 (FileCopyMode) query_dialog (op_names[OP_DELETE], text, D_ERROR, 5,
691 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
692 g_free (text);
694 if (ctx->recursive_result != RECURSIVE_ABORT)
695 do_refresh ();
698 switch (ctx->recursive_result)
700 case RECURSIVE_YES:
701 case RECURSIVE_ALWAYS:
702 return FILE_CONT;
704 case RECURSIVE_NO:
705 case RECURSIVE_NEVER:
706 return FILE_SKIP;
708 case RECURSIVE_ABORT:
709 default:
710 return FILE_ABORT;
714 /* --------------------------------------------------------------------------------------------- */
716 #ifdef ENABLE_BACKGROUND
717 static FileProgressStatus
718 do_file_error (const char *str)
720 union
722 void *p;
723 FileProgressStatus (*f) (enum OperationMode, const char *);
724 } pntr;
725 pntr.f = real_do_file_error;
727 if (mc_global.we_are_background)
728 return parent_call (pntr.p, NULL, 1, strlen (str), str);
729 else
730 return real_do_file_error (Foreground, str);
733 /* --------------------------------------------------------------------------------------------- */
735 static FileProgressStatus
736 query_recursive (FileOpContext * ctx, const char *s)
738 union
740 void *p;
741 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
742 } pntr;
743 pntr.f = real_query_recursive;
745 if (mc_global.we_are_background)
746 return parent_call (pntr.p, ctx, 1, strlen (s), s);
747 else
748 return real_query_recursive (ctx, Foreground, s);
751 /* --------------------------------------------------------------------------------------------- */
753 static FileProgressStatus
754 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
755 struct stat *_d_stat)
757 union
759 void *p;
760 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
761 struct stat *, struct stat *);
762 } pntr;
763 pntr.f = file_progress_real_query_replace;
765 if (mc_global.we_are_background)
766 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
767 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
768 else
769 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
772 #else
773 /* --------------------------------------------------------------------------------------------- */
775 static FileProgressStatus
776 do_file_error (const char *str)
778 return real_do_file_error (Foreground, str);
781 /* --------------------------------------------------------------------------------------------- */
783 static FileProgressStatus
784 query_recursive (FileOpContext * ctx, const char *s)
786 return real_query_recursive (ctx, Foreground, s);
789 /* --------------------------------------------------------------------------------------------- */
791 static FileProgressStatus
792 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
793 struct stat *_d_stat)
795 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
798 #endif /* !ENABLE_BACKGROUND */
800 /* --------------------------------------------------------------------------------------------- */
801 /** Report error with two files */
803 static FileProgressStatus
804 files_error (const char *format, const char *file1, const char *file2)
806 char buf[BUF_MEDIUM];
807 char *nfile1 = g_strdup (path_trunc (file1, 15));
808 char *nfile2 = g_strdup (path_trunc (file2, 15));
810 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
812 g_free (nfile1);
813 g_free (nfile2);
815 return do_file_error (buf);
818 /* }}} */
820 /* --------------------------------------------------------------------------------------------- */
822 static void
823 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
824 struct timeval tv_current, struct timeval tv_transfer_start,
825 off_t file_size, off_t n_read_total)
827 long dt;
829 /* 1. Update rotating dash after some time */
830 rotate_dash (TRUE);
832 /* 3. Compute ETA */
833 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
835 if (n_read_total == 0)
836 ctx->eta_secs = 0.0;
837 else
839 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
840 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
843 /* 4. Compute BPS rate */
844 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
845 if (ctx->bps_time < 1)
846 ctx->bps_time = 1;
847 ctx->bps = n_read_total / ctx->bps_time;
849 /* 5. Compute total ETA and BPS */
850 if (ctx->progress_bytes != 0)
852 uintmax_t remain_bytes;
854 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
855 #if 1
857 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
859 if (total_secs < 1)
860 total_secs = 1;
862 tctx->bps = tctx->copied_bytes / total_secs;
863 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
865 #else
866 /* broken on lot of little files */
867 tctx->bps_count++;
868 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
869 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
870 #endif
874 /* --------------------------------------------------------------------------------------------- */
876 /* {{{ Move routines */
877 static FileProgressStatus
878 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
880 struct stat src_stats, dst_stats;
881 FileProgressStatus return_status = FILE_CONT;
882 gboolean copy_done = FALSE;
883 gboolean old_ask_overwrite;
884 vfs_path_t *src_vpath, *dst_vpath;
886 src_vpath = vfs_path_from_str (s);
887 dst_vpath = vfs_path_from_str (d);
889 file_progress_show_source (ctx, src_vpath);
890 file_progress_show_target (ctx, dst_vpath);
892 if (check_progress_buttons (ctx) == FILE_ABORT)
894 return_status = FILE_ABORT;
895 goto ret;
898 mc_refresh ();
900 while (mc_lstat (src_vpath, &src_stats) != 0)
902 /* Source doesn't exist */
903 if (ctx->skip_all)
904 return_status = FILE_SKIPALL;
905 else
907 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
908 if (return_status == FILE_SKIPALL)
909 ctx->skip_all = TRUE;
912 if (return_status != FILE_RETRY)
913 goto ret;
916 if (mc_lstat (dst_vpath, &dst_stats) == 0)
918 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
920 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
921 goto ret;
924 if (S_ISDIR (dst_stats.st_mode))
926 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
927 do_refresh ();
928 return_status = FILE_SKIP;
929 goto ret;
932 if (confirm_overwrite)
934 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
935 if (return_status != FILE_CONT)
936 goto ret;
938 /* Ok to overwrite */
941 if (!ctx->do_append)
943 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
945 return_status = make_symlink (ctx, s, d);
946 if (return_status == FILE_CONT)
947 goto retry_src_remove;
948 goto ret;
951 if (mc_rename (src_vpath, dst_vpath) == 0)
953 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
954 goto ret;
957 #if 0
958 /* Comparison to EXDEV seems not to work in nfs if you're moving from
959 one nfs to the same, but on the server it is on two different
960 filesystems. Then nfs returns EIO instead of EXDEV.
961 Hope it will not hurt if we always in case of error try to copy/delete. */
962 else
963 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
965 if (errno != EXDEV)
967 if (ctx->skip_all)
968 return_status = FILE_SKIPALL;
969 else
971 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
972 if (return_status == FILE_SKIPALL)
973 ctx->skip_all = TRUE;
974 if (return_status == FILE_RETRY)
975 goto retry_rename;
978 goto ret;
980 #endif
982 /* Failed because filesystem boundary -> copy the file instead */
983 old_ask_overwrite = tctx->ask_overwrite;
984 tctx->ask_overwrite = FALSE;
985 return_status = copy_file_file (tctx, ctx, s, d);
986 tctx->ask_overwrite = old_ask_overwrite;
987 if (return_status != FILE_CONT)
988 goto ret;
990 copy_done = TRUE;
992 file_progress_show_source (ctx, NULL);
993 file_progress_show (ctx, 0, 0, "", FALSE);
995 return_status = check_progress_buttons (ctx);
996 if (return_status != FILE_CONT)
997 goto ret;
998 mc_refresh ();
1000 retry_src_remove:
1001 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
1003 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
1004 if (return_status == FILE_RETRY)
1005 goto retry_src_remove;
1006 if (return_status == FILE_SKIPALL)
1007 ctx->skip_all = TRUE;
1008 goto ret;
1011 if (!copy_done)
1012 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
1014 ret:
1015 vfs_path_free (src_vpath);
1016 vfs_path_free (dst_vpath);
1018 return return_status;
1021 /* }}} */
1023 /* --------------------------------------------------------------------------------------------- */
1024 /* {{{ Erase routines */
1025 /** Don't update progress status if progress_count==NULL */
1027 static FileProgressStatus
1028 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * vpath)
1030 struct stat buf;
1032 file_progress_show_deleting (ctx, vfs_path_as_str (vpath), &tctx->progress_count);
1033 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1034 if (check_progress_buttons (ctx) == FILE_ABORT)
1035 return FILE_ABORT;
1037 mc_refresh ();
1039 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
1041 /* ignore, most likely the mc_unlink fails, too */
1042 buf.st_size = 0;
1045 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
1047 int return_status;
1049 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), vfs_path_as_str (vpath));
1050 if (return_status == FILE_ABORT)
1051 return return_status;
1052 if (return_status == FILE_RETRY)
1053 continue;
1054 if (return_status == FILE_SKIPALL)
1055 ctx->skip_all = TRUE;
1056 break;
1059 if (tctx->progress_count == 0)
1060 return FILE_CONT;
1062 return check_progress_buttons (ctx);
1065 /* --------------------------------------------------------------------------------------------- */
1068 Recursive remove of files
1069 abort->cancel stack
1070 skip ->warn every level, gets default
1071 skipall->remove as much as possible
1073 static FileProgressStatus
1074 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * vpath)
1076 struct dirent *next;
1077 DIR *reading;
1078 const char *s;
1079 FileProgressStatus return_status = FILE_CONT;
1081 reading = mc_opendir (vpath);
1082 if (reading == NULL)
1083 return FILE_RETRY;
1085 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1087 vfs_path_t *tmp_vpath;
1088 struct stat buf;
1090 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
1091 continue;
1093 tmp_vpath = vfs_path_append_new (vpath, next->d_name, NULL);
1094 if (mc_lstat (tmp_vpath, &buf) != 0)
1096 mc_closedir (reading);
1097 vfs_path_free (tmp_vpath);
1098 return FILE_RETRY;
1100 if (S_ISDIR (buf.st_mode))
1101 return_status = recursive_erase (tctx, ctx, tmp_vpath);
1102 else
1103 return_status = erase_file (tctx, ctx, tmp_vpath);
1104 vfs_path_free (tmp_vpath);
1106 mc_closedir (reading);
1108 if (return_status == FILE_ABORT)
1109 return FILE_ABORT;
1111 s = vfs_path_as_str (vpath);
1113 file_progress_show_deleting (ctx, s, NULL);
1114 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1115 if (check_progress_buttons (ctx) == FILE_ABORT)
1116 return FILE_ABORT;
1118 mc_refresh ();
1120 while (my_rmdir (s) != 0 && !ctx->skip_all)
1122 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1123 if (return_status == FILE_RETRY)
1124 continue;
1125 if (return_status == FILE_ABORT)
1126 break;
1127 if (return_status == FILE_SKIPALL)
1128 ctx->skip_all = TRUE;
1129 break;
1132 return return_status;
1135 /* --------------------------------------------------------------------------------------------- */
1136 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1137 in the directory path points to, 0 else. */
1139 static int
1140 check_dir_is_empty (const vfs_path_t * vpath)
1142 DIR *dir;
1143 struct dirent *d;
1144 int i = 1;
1146 dir = mc_opendir (vpath);
1147 if (dir == NULL)
1148 return -1;
1150 for (d = mc_readdir (dir); d != NULL; d = mc_readdir (dir))
1151 if (!DIR_IS_DOT (d->d_name) && !DIR_IS_DOTDOT (d->d_name))
1153 i = 0;
1154 break;
1157 mc_closedir (dir);
1158 return i;
1161 /* --------------------------------------------------------------------------------------------- */
1163 static FileProgressStatus
1164 erase_dir_iff_empty (FileOpContext * ctx, const vfs_path_t * vpath, size_t count)
1166 FileProgressStatus error = FILE_CONT;
1167 const char *s;
1169 s = vfs_path_as_str (vpath);
1171 file_progress_show_deleting (ctx, s, NULL);
1172 file_progress_show_count (ctx, count, ctx->progress_count);
1173 if (check_progress_buttons (ctx) == FILE_ABORT)
1174 return FILE_ABORT;
1176 mc_refresh ();
1178 if (check_dir_is_empty (vpath) == 1) /* not empty or error */
1180 while (my_rmdir (s) != 0 && !ctx->skip_all)
1182 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1183 if (error == FILE_SKIPALL)
1184 ctx->skip_all = TRUE;
1185 if (error != FILE_RETRY)
1186 break;
1190 return error;
1193 /* }}} */
1195 /* --------------------------------------------------------------------------------------------- */
1196 /* {{{ Panel operate routines */
1199 * Return currently selected entry name or the name of the first marked
1200 * entry if there is one.
1203 static char *
1204 panel_get_file (WPanel * panel)
1206 if (get_current_type () == view_tree)
1208 WTree *tree;
1209 vfs_path_t *selected_name;
1211 tree = (WTree *) get_panel_widget (get_current_index ());
1212 selected_name = tree_selected_name (tree);
1213 return g_strdup (vfs_path_as_str (selected_name));
1216 if (panel->marked != 0)
1218 int i;
1220 for (i = 0; i < panel->dir.len; i++)
1221 if (panel->dir.list[i].f.marked)
1222 return g_strdup (panel->dir.list[i].fname);
1224 return g_strdup (panel->dir.list[panel->selected].fname);
1227 /* --------------------------------------------------------------------------------------------- */
1229 * panel_compute_totals:
1231 * compute the number of files and the number of bytes
1232 * used up by the whole selection, recursing directories
1233 * as required. In addition, it checks to see if it will
1234 * overwrite any files by doing the copy.
1237 static FileProgressStatus
1238 panel_compute_totals (const WPanel * panel, void *ui, compute_dir_size_callback cback,
1239 size_t * ret_count, uintmax_t * ret_total, gboolean compute_symlinks)
1241 int i;
1243 for (i = 0; i < panel->dir.len; i++)
1245 struct stat *s;
1247 if (!panel->dir.list[i].f.marked)
1248 continue;
1250 s = &panel->dir.list[i].st;
1252 if (S_ISDIR (s->st_mode))
1254 vfs_path_t *p;
1255 FileProgressStatus status;
1257 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, NULL);
1258 status = compute_dir_size (p, ui, cback, ret_count, ret_total, compute_symlinks);
1259 vfs_path_free (p);
1261 if (status != FILE_CONT)
1262 return status;
1264 else
1266 (*ret_count)++;
1267 *ret_total += (uintmax_t) s->st_size;
1271 return FILE_CONT;
1274 /* --------------------------------------------------------------------------------------------- */
1276 /** Initialize variables for progress bars */
1277 static FileProgressStatus
1278 panel_operate_init_totals (const WPanel * panel, const char *source, FileOpContext * ctx,
1279 filegui_dialog_type_t dialog_type)
1281 FileProgressStatus status;
1283 #ifdef ENABLE_BACKGROUND
1284 if (mc_global.we_are_background)
1285 return FILE_CONT;
1286 #endif
1288 if (verbose && file_op_compute_totals)
1290 ComputeDirSizeUI *ui;
1292 ui = compute_dir_size_create_ui (TRUE);
1294 ctx->progress_count = 0;
1295 ctx->progress_bytes = 0;
1297 if (source == NULL)
1298 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1299 &ctx->progress_count, &ctx->progress_bytes,
1300 ctx->follow_links);
1301 else
1303 vfs_path_t *p;
1305 p = vfs_path_from_str (source);
1306 status = compute_dir_size (p, ui, compute_dir_size_update_ui,
1307 &ctx->progress_count, &ctx->progress_bytes,
1308 ctx->follow_links);
1309 vfs_path_free (p);
1312 compute_dir_size_destroy_ui (ui);
1314 ctx->progress_totals_computed = (status == FILE_CONT);
1316 if (status == FILE_SKIP)
1317 status = FILE_CONT;
1319 else
1321 status = FILE_CONT;
1322 ctx->progress_count = panel->marked;
1323 ctx->progress_bytes = panel->total;
1324 ctx->progress_totals_computed = FALSE;
1327 file_op_context_create_ui (ctx, TRUE, dialog_type);
1329 return status;
1332 /* --------------------------------------------------------------------------------------------- */
1334 * Generate user prompt for panel operation.
1335 * single_source is the name if the source entry or NULL for multiple
1336 * entries.
1337 * src_stat is only used when single_source is not NULL.
1340 static char *
1341 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1342 gboolean single_source, const struct stat *src_stat)
1344 const char *sp, *cp;
1345 char format_string[BUF_MEDIUM];
1346 char *dp = format_string;
1347 gboolean build_question = FALSE;
1349 static gboolean i18n_flag = FALSE;
1350 if (!i18n_flag)
1352 size_t i;
1354 for (i = G_N_ELEMENTS (op_names1); i-- != 0;)
1355 op_names1[i] = Q_ (op_names1[i]);
1357 #ifdef ENABLE_NLS
1358 for (i = G_N_ELEMENTS (prompt_parts); i-- != 0;)
1359 prompt_parts[i] = _(prompt_parts[i]);
1361 one_format = _(one_format);
1362 many_format = _(many_format);
1363 question_format = _(question_format);
1364 #endif /* ENABLE_NLS */
1365 i18n_flag = TRUE;
1368 sp = single_source ? one_format : many_format;
1370 while (*sp != '\0')
1372 switch (*sp)
1374 case '%':
1375 cp = NULL;
1376 switch (sp[1])
1378 case 'o':
1379 cp = op_names1[operation];
1380 break;
1381 case 'm':
1382 if (operation == OP_DELETE)
1384 cp = "";
1385 build_question = TRUE;
1387 else
1388 cp = prompt_parts[5];
1389 break;
1390 case 'e':
1391 if (operation == OP_DELETE)
1393 cp = "";
1394 build_question = TRUE;
1396 else
1397 cp = prompt_parts[6];
1398 break;
1399 case 'f':
1400 if (single_source)
1401 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1402 else
1403 cp = (panel->marked == panel->dirs_marked)
1404 ? prompt_parts[3]
1405 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1406 break;
1407 default:
1408 *dp++ = *sp++;
1411 if (cp != NULL)
1413 sp += 2;
1415 while (*cp != '\0')
1416 *dp++ = *cp++;
1418 /* form two-lines query prompt for file deletion */
1419 if (operation == OP_DELETE && sp[-1] == 'f')
1421 *dp++ = '\n';
1423 while (isblank (*sp) != 0)
1424 sp++;
1427 break;
1428 default:
1429 *dp++ = *sp++;
1432 *dp = '\0';
1434 if (build_question)
1436 char tmp[BUF_MEDIUM];
1438 memmove (tmp, format_string, sizeof (tmp));
1439 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1442 return g_strdup (format_string);
1445 /* --------------------------------------------------------------------------------------------- */
1447 #ifdef ENABLE_BACKGROUND
1448 static int
1449 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1451 int pid = ctx->pid;
1453 (void) mode;
1454 ctx->pid = 0;
1456 unregister_task_with_pid (pid);
1457 /* file_op_context_destroy(ctx); */
1458 return 1;
1460 #endif
1461 /* }}} */
1463 /* --------------------------------------------------------------------------------------------- */
1464 /*** public functions ****************************************************************************/
1465 /* --------------------------------------------------------------------------------------------- */
1467 FileProgressStatus
1468 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1469 const char *src_path, const char *dst_path)
1471 uid_t src_uid = (uid_t) (-1);
1472 gid_t src_gid = (gid_t) (-1);
1474 int src_desc, dest_desc = -1;
1475 int n_read, n_written;
1476 mode_t src_mode = 0; /* The mode of the source file */
1477 struct stat sb, sb2;
1478 struct utimbuf utb;
1479 gboolean dst_exists = FALSE, appending = FALSE;
1480 off_t file_size = -1;
1481 FileProgressStatus return_status, temp_status;
1482 struct timeval tv_transfer_start;
1483 dest_status_t dst_status = DEST_NONE;
1484 int open_flags;
1485 gboolean is_first_time = TRUE;
1486 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1487 gboolean write_errno_nospace = FALSE;
1489 /* FIXME: We should not be using global variables! */
1490 ctx->do_reget = 0;
1491 return_status = FILE_RETRY;
1493 dst_vpath = vfs_path_from_str (dst_path);
1494 src_vpath = vfs_path_from_str (src_path);
1496 file_progress_show_source (ctx, src_vpath);
1497 file_progress_show_target (ctx, dst_vpath);
1499 if (check_progress_buttons (ctx) == FILE_ABORT)
1501 return_status = FILE_ABORT;
1502 goto ret_fast;
1505 mc_refresh ();
1507 while (mc_stat (dst_vpath, &sb2) == 0)
1509 if (S_ISDIR (sb2.st_mode))
1511 if (ctx->skip_all)
1512 return_status = FILE_SKIPALL;
1513 else
1515 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1516 if (return_status == FILE_SKIPALL)
1517 ctx->skip_all = TRUE;
1518 if (return_status == FILE_RETRY)
1519 continue;
1521 goto ret_fast;
1524 dst_exists = TRUE;
1525 break;
1528 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1530 if (ctx->skip_all)
1531 return_status = FILE_SKIPALL;
1532 else
1534 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1535 if (return_status == FILE_SKIPALL)
1536 ctx->skip_all = TRUE;
1539 if (return_status != FILE_RETRY)
1540 goto ret_fast;
1543 if (dst_exists)
1545 /* Destination already exists */
1546 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1548 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1549 src_path, dst_path);
1550 goto ret_fast;
1553 /* Should we replace destination? */
1554 if (tctx->ask_overwrite)
1556 ctx->do_reget = 0;
1557 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1558 if (return_status != FILE_CONT)
1559 goto ret_fast;
1563 if (!ctx->do_append)
1565 /* Check the hardlinks */
1566 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &sb))
1568 /* We have made a hardlink - no more processing is necessary */
1569 return_status = FILE_CONT;
1570 goto ret_fast;
1573 if (S_ISLNK (sb.st_mode))
1575 return_status = make_symlink (ctx, src_path, dst_path);
1576 goto ret_fast;
1579 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1580 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1582 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1583 && !ctx->skip_all)
1585 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1586 if (return_status == FILE_RETRY)
1587 continue;
1588 if (return_status == FILE_SKIPALL)
1589 ctx->skip_all = TRUE;
1590 goto ret_fast;
1592 /* Success */
1594 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1595 && !ctx->skip_all)
1597 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1598 if (temp_status == FILE_SKIP)
1599 break;
1600 if (temp_status == FILE_SKIPALL)
1601 ctx->skip_all = TRUE;
1602 if (temp_status != FILE_RETRY)
1604 return_status = temp_status;
1605 goto ret_fast;
1609 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1610 && !ctx->skip_all)
1612 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1613 if (temp_status == FILE_SKIP)
1614 break;
1615 if (temp_status == FILE_SKIPALL)
1616 ctx->skip_all = TRUE;
1617 if (temp_status != FILE_RETRY)
1619 return_status = temp_status;
1620 goto ret_fast;
1624 return_status = FILE_CONT;
1625 goto ret_fast;
1629 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1631 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1633 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1634 if (return_status == FILE_RETRY)
1635 continue;
1636 if (return_status == FILE_SKIPALL)
1637 ctx->skip_all = TRUE;
1638 if (return_status == FILE_SKIP)
1639 break;
1640 ctx->do_append = 0;
1641 goto ret_fast;
1644 if (ctx->do_reget != 0)
1646 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1648 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1649 ctx->do_reget = 0;
1650 ctx->do_append = FALSE;
1654 while (mc_fstat (src_desc, &sb) != 0)
1656 if (ctx->skip_all)
1657 return_status = FILE_SKIPALL;
1658 else
1660 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1661 if (return_status == FILE_RETRY)
1662 continue;
1663 if (return_status == FILE_SKIPALL)
1664 ctx->skip_all = TRUE;
1665 ctx->do_append = FALSE;
1667 goto ret;
1670 src_mode = sb.st_mode;
1671 src_uid = sb.st_uid;
1672 src_gid = sb.st_gid;
1673 utb.actime = sb.st_atime;
1674 utb.modtime = sb.st_mtime;
1675 file_size = sb.st_size;
1677 open_flags = O_WRONLY;
1678 if (dst_exists)
1680 if (ctx->do_append != 0)
1681 open_flags |= O_APPEND;
1682 else
1683 open_flags |= O_CREAT | O_TRUNC;
1685 else
1687 open_flags |= O_CREAT | O_EXCL;
1690 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
1692 if (errno != EEXIST)
1694 if (ctx->skip_all)
1695 return_status = FILE_SKIPALL;
1696 else
1698 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1699 if (return_status == FILE_RETRY)
1700 continue;
1701 if (return_status == FILE_SKIPALL)
1702 ctx->skip_all = TRUE;
1703 ctx->do_append = FALSE;
1706 goto ret;
1708 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1710 appending = ctx->do_append;
1711 ctx->do_append = FALSE;
1713 /* Find out the optimal buffer size. */
1714 while (mc_fstat (dest_desc, &sb) != 0)
1716 if (ctx->skip_all)
1717 return_status = FILE_SKIPALL;
1718 else
1720 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1721 if (return_status == FILE_RETRY)
1722 continue;
1723 if (return_status == FILE_SKIPALL)
1724 ctx->skip_all = TRUE;
1726 goto ret;
1729 while (TRUE)
1731 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1732 if (errno == 0)
1733 break;
1735 if (ctx->skip_all)
1736 return_status = FILE_SKIPALL;
1737 else
1739 return_status =
1740 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1741 if (return_status == FILE_RETRY)
1742 continue;
1743 if (return_status == FILE_SKIPALL)
1744 ctx->skip_all = TRUE;
1746 mc_close (dest_desc);
1747 dest_desc = -1;
1748 mc_unlink (dst_vpath);
1749 dst_status = DEST_NONE;
1750 goto ret;
1753 ctx->eta_secs = 0.0;
1754 ctx->bps = 0;
1756 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1757 file_progress_show (ctx, 0, file_size, "", TRUE);
1758 else
1759 file_progress_show (ctx, 1, 1, "", TRUE);
1760 return_status = check_progress_buttons (ctx);
1761 mc_refresh ();
1763 if (return_status != FILE_CONT)
1764 goto ret;
1767 off_t n_read_total = 0;
1768 struct timeval tv_current, tv_last_update, tv_last_input;
1769 int secs, update_secs;
1770 const char *stalled_msg = "";
1772 tv_last_update = tv_transfer_start;
1774 while (TRUE)
1776 char buf[BUF_8K];
1778 /* src_read */
1779 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1780 n_read = -1;
1781 else
1782 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1784 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1785 if (return_status == FILE_RETRY)
1786 continue;
1787 if (return_status == FILE_SKIPALL)
1788 ctx->skip_all = TRUE;
1789 goto ret;
1791 if (n_read == 0)
1792 break;
1794 gettimeofday (&tv_current, NULL);
1796 if (n_read > 0)
1798 char *t = buf;
1799 n_read_total += n_read;
1801 /* Windows NT ftp servers report that files have no
1802 * permissions: -------, so if we happen to have actually
1803 * read something, we should fix the permissions.
1805 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1806 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1807 gettimeofday (&tv_last_input, NULL);
1809 /* dst_write */
1810 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read)
1812 if (n_written > 0)
1814 n_read -= n_written;
1815 t += n_written;
1816 continue;
1819 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
1821 if (ctx->skip_all)
1822 return_status = FILE_SKIPALL;
1823 else
1824 return_status =
1825 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1827 if (return_status == FILE_SKIP)
1829 if (write_errno_nospace)
1830 goto ret;
1831 break;
1833 if (return_status == FILE_SKIPALL)
1835 ctx->skip_all = TRUE;
1836 if (write_errno_nospace)
1837 goto ret;
1839 if (return_status != FILE_RETRY)
1840 goto ret;
1842 /* User pressed "Retry". Will the next mc_write() call be successful?
1843 * Reset error flag to be ready for that. */
1844 write_errno_nospace = FALSE;
1848 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1850 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1851 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1853 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1855 copy_file_file_display_progress (tctx, ctx,
1856 tv_current,
1857 tv_transfer_start, file_size, n_read_total);
1858 tv_last_update = tv_current;
1860 is_first_time = FALSE;
1862 if (update_secs > FILEOP_STALLING_INTERVAL)
1864 stalled_msg = _("(stalled)");
1868 gboolean force_update;
1870 force_update =
1871 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1873 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1875 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1876 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1879 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1880 force_update);
1882 mc_refresh ();
1884 return_status = check_progress_buttons (ctx);
1886 if (return_status != FILE_CONT)
1888 mc_refresh ();
1889 goto ret;
1894 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1896 ret:
1897 rotate_dash (FALSE);
1898 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1900 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1901 if (temp_status == FILE_RETRY)
1902 continue;
1903 if (temp_status == FILE_ABORT)
1904 return_status = temp_status;
1905 if (temp_status == FILE_SKIPALL)
1906 ctx->skip_all = TRUE;
1907 break;
1910 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1912 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1913 if (temp_status == FILE_RETRY)
1914 continue;
1915 if (temp_status == FILE_SKIPALL)
1916 ctx->skip_all = TRUE;
1917 return_status = temp_status;
1918 break;
1921 if (dst_status == DEST_SHORT)
1923 /* Remove short file */
1924 int result = 0;
1926 /* In case of copy/move to full partition, keep source file
1927 * and remove incomplete destination one */
1928 if (!write_errno_nospace)
1929 result = query_dialog (Q_ ("DialogTitle|Copy"),
1930 _("Incomplete file was retrieved. Keep it?"),
1931 D_ERROR, 2, _("&Delete"), _("&Keep"));
1932 if (result == 0)
1933 mc_unlink (dst_vpath);
1935 else if (dst_status == DEST_FULL)
1937 /* Copy has succeeded */
1938 if (!appending && ctx->preserve_uidgid)
1940 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1942 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1943 if (temp_status == FILE_RETRY)
1944 continue;
1945 if (temp_status == FILE_SKIPALL)
1947 ctx->skip_all = TRUE;
1948 return_status = FILE_CONT;
1950 if (temp_status == FILE_SKIP)
1951 return_status = FILE_CONT;
1952 break;
1956 if (!appending)
1958 if (ctx->preserve)
1960 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1962 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1963 if (temp_status == FILE_RETRY)
1964 continue;
1965 if (temp_status == FILE_SKIPALL)
1967 ctx->skip_all = TRUE;
1968 return_status = FILE_CONT;
1970 if (temp_status == FILE_SKIP)
1971 return_status = FILE_CONT;
1972 break;
1975 else if (!dst_exists)
1977 src_mode = umask (-1);
1978 umask (src_mode);
1979 src_mode = 0100666 & ~src_mode;
1980 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
1982 mc_utime (dst_vpath, &utb);
1986 if (return_status == FILE_CONT)
1987 return_status = progress_update_one (tctx, ctx, file_size);
1989 ret_fast:
1990 vfs_path_free (src_vpath);
1991 vfs_path_free (dst_vpath);
1992 return return_status;
1995 /* --------------------------------------------------------------------------------------------- */
1997 * I think these copy_*_* functions should have a return type.
1998 * anyway, this function *must* have two directories as arguments.
2000 /* FIXME: This function needs to check the return values of the
2001 function calls */
2003 FileProgressStatus
2004 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d,
2005 gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs)
2007 struct dirent *next;
2008 struct stat buf, cbuf;
2009 DIR *reading;
2010 FileProgressStatus return_status = FILE_CONT;
2011 struct link *lp;
2012 vfs_path_t *src_vpath, *dst_vpath;
2013 gboolean do_mkdir = TRUE;
2015 src_vpath = vfs_path_from_str (s);
2016 dst_vpath = vfs_path_from_str (d);
2018 /* First get the mode of the source dir */
2020 retry_src_stat:
2021 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
2023 if (ctx->skip_all)
2024 return_status = FILE_SKIPALL;
2025 else
2027 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
2028 if (return_status == FILE_RETRY)
2029 goto retry_src_stat;
2030 if (return_status == FILE_SKIPALL)
2031 ctx->skip_all = TRUE;
2033 goto ret_fast;
2036 if (is_in_linklist (dest_dirs, src_vpath, &cbuf))
2038 /* Don't copy a directory we created before (we don't want to copy
2039 infinitely if a directory is copied into itself) */
2040 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2041 return_status = FILE_CONT;
2042 goto ret_fast;
2045 /* Hmm, hardlink to directory??? - Norbert */
2046 /* FIXME: In this step we should do something
2047 in case the destination already exist */
2048 /* Check the hardlinks */
2049 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &cbuf))
2051 /* We have made a hardlink - no more processing is necessary */
2052 goto ret_fast;
2055 if (!S_ISDIR (cbuf.st_mode))
2057 if (ctx->skip_all)
2058 return_status = FILE_SKIPALL;
2059 else
2061 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
2062 if (return_status == FILE_RETRY)
2063 goto retry_src_stat;
2064 if (return_status == FILE_SKIPALL)
2065 ctx->skip_all = TRUE;
2067 goto ret_fast;
2070 if (is_in_linklist (parent_dirs, src_vpath, &cbuf))
2072 /* we found a cyclic symbolic link */
2073 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
2074 return_status = FILE_SKIP;
2075 goto ret_fast;
2078 lp = g_new0 (struct link, 1);
2079 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
2080 lp->ino = cbuf.st_ino;
2081 lp->dev = cbuf.st_dev;
2082 parent_dirs = g_slist_prepend (parent_dirs, lp);
2084 retry_dst_stat:
2085 /* Now, check if the dest dir exists, if not, create it. */
2086 if (mc_stat (dst_vpath, &buf) != 0)
2088 /* Here the dir doesn't exist : make it ! */
2089 if (move_over && mc_rename (src_vpath, dst_vpath) == 0)
2091 return_status = FILE_CONT;
2092 goto ret;
2095 else
2098 * If the destination directory exists, we want to copy the whole
2099 * directory, but we only want this to happen once.
2101 * Escape sequences added to the * to compiler warnings.
2102 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2103 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2105 if (!S_ISDIR (buf.st_mode))
2107 if (ctx->skip_all)
2108 return_status = FILE_SKIPALL;
2109 else
2111 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2112 if (return_status == FILE_SKIPALL)
2113 ctx->skip_all = TRUE;
2114 if (return_status == FILE_RETRY)
2115 goto retry_dst_stat;
2117 goto ret;
2119 /* Dive into subdir if exists */
2120 if (toplevel && ctx->dive_into_subdirs)
2122 vfs_path_t *tmp;
2124 tmp = dst_vpath;
2125 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), NULL);
2126 vfs_path_free (tmp);
2129 else
2130 do_mkdir = FALSE;
2133 d = vfs_path_as_str (dst_vpath);
2135 if (do_mkdir)
2137 while (my_mkdir (dst_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2139 if (ctx->skip_all)
2140 return_status = FILE_SKIPALL;
2141 else
2143 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), d);
2144 if (return_status == FILE_SKIPALL)
2145 ctx->skip_all = TRUE;
2147 if (return_status != FILE_RETRY)
2148 goto ret;
2151 lp = g_new0 (struct link, 1);
2152 mc_stat (dst_vpath, &buf);
2153 lp->vfs = vfs_path_get_by_index (dst_vpath, -1)->class;
2154 lp->ino = buf.st_ino;
2155 lp->dev = buf.st_dev;
2156 dest_dirs = g_slist_prepend (dest_dirs, lp);
2159 if (ctx->preserve_uidgid)
2161 while (mc_chown (dst_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2163 if (ctx->skip_all)
2164 return_status = FILE_SKIPALL;
2165 else
2167 return_status = file_error (_("Cannot chown target directory \"%s\"\n%s"), d);
2168 if (return_status == FILE_SKIPALL)
2169 ctx->skip_all = TRUE;
2171 if (return_status != FILE_RETRY)
2172 goto ret;
2176 /* open the source dir for reading */
2177 reading = mc_opendir (src_vpath);
2178 if (reading == NULL)
2179 goto ret;
2181 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2183 char *path;
2184 vfs_path_t *tmp_vpath;
2187 * Now, we don't want '.' and '..' to be created / copied at any time
2189 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
2190 continue;
2192 /* get the filename and add it to the src directory */
2193 path = mc_build_filename (s, next->d_name, NULL);
2194 tmp_vpath = vfs_path_from_str (path);
2196 (*ctx->stat_func) (tmp_vpath, &buf);
2197 if (S_ISDIR (buf.st_mode))
2199 char *mdpath;
2201 mdpath = mc_build_filename (d, next->d_name, NULL);
2203 * From here, we just intend to recursively copy subdirs, not
2204 * the double functionality of copying different when the target
2205 * dir already exists. So, we give the recursive call the flag 0
2206 * meaning no toplevel.
2208 return_status =
2209 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2210 g_free (mdpath);
2212 else
2214 char *dest_file;
2216 dest_file = mc_build_filename (d, x_basename (path), NULL);
2217 return_status = copy_file_file (tctx, ctx, path, dest_file);
2218 g_free (dest_file);
2221 g_free (path);
2223 if (do_delete && return_status == FILE_CONT)
2225 if (ctx->erase_at_end)
2227 lp = g_new0 (struct link, 1);
2228 lp->src_vpath = tmp_vpath;
2229 lp->st_mode = buf.st_mode;
2230 erase_list = g_slist_append (erase_list, lp);
2231 tmp_vpath = NULL;
2233 else if (S_ISDIR (buf.st_mode))
2234 return_status = erase_dir_iff_empty (ctx, tmp_vpath, tctx->progress_count);
2235 else
2236 return_status = erase_file (tctx, ctx, tmp_vpath);
2238 vfs_path_free (tmp_vpath);
2240 mc_closedir (reading);
2242 if (ctx->preserve)
2244 struct utimbuf utb;
2246 mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill);
2247 utb.actime = cbuf.st_atime;
2248 utb.modtime = cbuf.st_mtime;
2249 mc_utime (dst_vpath, &utb);
2251 else
2253 cbuf.st_mode = umask (-1);
2254 umask (cbuf.st_mode);
2255 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2256 mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill);
2259 ret:
2260 free_link (parent_dirs->data);
2261 g_slist_free_1 (parent_dirs);
2262 ret_fast:
2263 vfs_path_free (src_vpath);
2264 vfs_path_free (dst_vpath);
2265 return return_status;
2268 /* }}} */
2270 /* --------------------------------------------------------------------------------------------- */
2271 /* {{{ Move routines */
2273 FileProgressStatus
2274 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2276 struct stat sbuf, dbuf, destbuf;
2277 FileProgressStatus return_status;
2278 gboolean move_over = FALSE;
2279 gboolean dstat_ok;
2280 vfs_path_t *src_vpath, *dst_vpath;
2282 src_vpath = vfs_path_from_str (s);
2283 dst_vpath = vfs_path_from_str (d);
2285 file_progress_show_source (ctx, src_vpath);
2286 file_progress_show_target (ctx, dst_vpath);
2288 if (check_progress_buttons (ctx) == FILE_ABORT)
2290 return_status = FILE_ABORT;
2291 goto ret_fast;
2294 mc_refresh ();
2296 mc_stat (src_vpath, &sbuf);
2298 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2299 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2301 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2302 goto ret_fast;
2305 if (!dstat_ok)
2306 ; /* destination doesn't exist */
2307 else if (!ctx->dive_into_subdirs)
2308 move_over = TRUE;
2309 else
2311 vfs_path_t *tmp;
2313 tmp = dst_vpath;
2314 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), NULL);
2315 vfs_path_free (tmp);
2318 d = vfs_path_as_str (dst_vpath);
2320 /* Check if the user inputted an existing dir */
2321 retry_dst_stat:
2322 if (mc_stat (dst_vpath, &destbuf) == 0)
2324 if (move_over)
2326 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, TRUE, TRUE, NULL);
2328 if (return_status != FILE_CONT)
2329 goto ret;
2330 goto oktoret;
2332 else if (ctx->skip_all)
2333 return_status = FILE_SKIPALL;
2334 else
2336 if (S_ISDIR (destbuf.st_mode))
2337 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), d);
2338 else
2339 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), d);
2340 if (return_status == FILE_SKIPALL)
2341 ctx->skip_all = TRUE;
2342 if (return_status == FILE_RETRY)
2343 goto retry_dst_stat;
2346 goto ret_fast;
2349 retry_rename:
2350 if (mc_rename (src_vpath, dst_vpath) == 0)
2352 return_status = FILE_CONT;
2353 goto ret;
2356 if (errno != EXDEV)
2358 if (!ctx->skip_all)
2360 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2361 if (return_status == FILE_SKIPALL)
2362 ctx->skip_all = TRUE;
2363 if (return_status == FILE_RETRY)
2364 goto retry_rename;
2366 goto ret;
2368 /* Failed because of filesystem boundary -> copy dir instead */
2369 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, FALSE, TRUE, NULL);
2371 if (return_status != FILE_CONT)
2372 goto ret;
2373 oktoret:
2374 file_progress_show_source (ctx, NULL);
2375 file_progress_show (ctx, 0, 0, "", FALSE);
2377 return_status = check_progress_buttons (ctx);
2378 if (return_status != FILE_CONT)
2379 goto ret;
2381 mc_refresh ();
2382 if (ctx->erase_at_end)
2384 while (erase_list != NULL && return_status != FILE_ABORT)
2386 struct link *lp = (struct link *) erase_list->data;
2388 if (S_ISDIR (lp->st_mode))
2389 return_status = erase_dir_iff_empty (ctx, lp->src_vpath, tctx->progress_count);
2390 else
2391 return_status = erase_file (tctx, ctx, lp->src_vpath);
2393 erase_list = g_slist_remove (erase_list, lp);
2394 free_link (lp);
2397 erase_dir_iff_empty (ctx, src_vpath, tctx->progress_count);
2399 ret:
2400 erase_list = free_linklist (erase_list);
2401 ret_fast:
2402 vfs_path_free (src_vpath);
2403 vfs_path_free (dst_vpath);
2404 return return_status;
2407 /* }}} */
2409 /* --------------------------------------------------------------------------------------------- */
2410 /* {{{ Erase routines */
2412 FileProgressStatus
2413 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * s_vpath)
2415 FileProgressStatus error;
2417 file_progress_show_deleting (ctx, vfs_path_as_str (s_vpath), NULL);
2418 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2419 if (check_progress_buttons (ctx) == FILE_ABORT)
2420 return FILE_ABORT;
2422 mc_refresh ();
2424 /* The old way to detect a non empty directory was:
2425 error = my_rmdir (s);
2426 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2427 For the linux user space nfs server (nfs-server-2.2beta29-2)
2428 we would have to check also for EIO. I hope the new way is
2429 fool proof. (Norbert)
2431 error = check_dir_is_empty (s_vpath);
2432 if (error == 0)
2433 { /* not empty */
2434 error = query_recursive (ctx, vfs_path_as_str (s_vpath));
2435 if (error == FILE_CONT)
2436 error = recursive_erase (tctx, ctx, s_vpath);
2437 return error;
2440 while (my_rmdir (vfs_path_as_str (s_vpath)) == -1 && !ctx->skip_all)
2442 error = file_error (_("Cannot remove directory \"%s\"\n%s"), vfs_path_as_str (s_vpath));
2443 if (error != FILE_RETRY)
2444 return error;
2447 return FILE_CONT;
2450 /* }}} */
2452 /* --------------------------------------------------------------------------------------------- */
2453 /* {{{ Panel operate routines */
2455 ComputeDirSizeUI *
2456 compute_dir_size_create_ui (gboolean allow_skip)
2458 ComputeDirSizeUI *ui;
2460 const char *b1_name = N_("&Abort");
2461 const char *b2_name = N_("&Skip");
2462 int b1_width, b2_width = 0, b_width = 0;
2463 int b1_x;
2464 int ui_width;
2465 Widget *b;
2467 #ifdef ENABLE_NLS
2468 b1_name = _(b1_name);
2469 b2_name = _(b2_name);
2470 #endif
2472 b1_width = str_term_width1 (b1_name) + 4;
2473 if (allow_skip)
2474 b2_width = str_term_width1 (b2_name) + 4 + 1;
2475 b_width = b1_width + b2_width;
2477 ui = g_new (ComputeDirSizeUI, 1);
2479 ui_width = max (COLS / 2, b_width + 6);
2480 ui->dlg = dlg_create (TRUE, 0, 0, 8, ui_width, dialog_colors, NULL, NULL, NULL,
2481 _("Directory scanning"), DLG_CENTER);
2483 ui->dirname = label_new (2, 3, "");
2484 add_widget (ui->dlg, ui->dirname);
2485 add_widget (ui->dlg, hline_new (4, -1, -1));
2486 b1_x = (ui_width - b_width) / 2;
2487 b = WIDGET (button_new (5, b1_x, FILE_ABORT, NORMAL_BUTTON, b1_name, NULL));
2488 add_widget (ui->dlg, b);
2489 if (allow_skip)
2491 add_widget (ui->dlg,
2492 button_new (5, b1_x + 1 + b1_width, FILE_SKIP, NORMAL_BUTTON, b2_name, NULL));
2493 dlg_select_widget (b);
2496 /* We will manage the dialog without any help,
2497 that's why we have to call dlg_init */
2498 dlg_init (ui->dlg);
2500 return ui;
2503 /* --------------------------------------------------------------------------------------------- */
2505 void
2506 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2508 if (ui != NULL)
2510 /* schedule to update passive panel */
2511 other_panel->dirty = 1;
2513 /* close and destroy dialog */
2514 dlg_run_done (ui->dlg);
2515 dlg_destroy (ui->dlg);
2516 g_free (ui);
2520 /* --------------------------------------------------------------------------------------------- */
2522 FileProgressStatus
2523 compute_dir_size_update_ui (void *ui, const vfs_path_t * dirname_vpath, size_t dir_count,
2524 uintmax_t total_size)
2526 ComputeDirSizeUI *this = (ComputeDirSizeUI *) ui;
2527 int c;
2528 Gpm_Event event;
2529 char buffer[BUF_1K];
2531 if (ui == NULL)
2532 return FILE_CONT;
2534 g_snprintf (buffer, sizeof (buffer), _("%s\nDirectories: %zd, total size: %s"),
2535 str_trunc (vfs_path_as_str (dirname_vpath), WIDGET (this->dlg)->cols - 6),
2536 dir_count, size_trunc_sep (total_size, panels_options.kilobyte_si));
2537 label_set_text (this->dirname, buffer);
2539 event.x = -1; /* Don't show the GPM cursor */
2540 c = tty_get_event (&event, FALSE, FALSE);
2541 if (c == EV_NONE)
2542 return FILE_CONT;
2544 /* Reinitialize to avoid old values after events other than
2545 selecting a button */
2546 this->dlg->ret_value = FILE_CONT;
2548 dlg_process_event (this->dlg, c, &event);
2550 switch (this->dlg->ret_value)
2552 case B_CANCEL:
2553 case FILE_ABORT:
2554 return FILE_ABORT;
2555 case FILE_SKIP:
2556 return FILE_SKIP;
2557 default:
2558 return FILE_CONT;
2562 /* --------------------------------------------------------------------------------------------- */
2564 * compute_dir_size:
2566 * Computes the number of bytes used by the files in a directory
2569 FileProgressStatus
2570 compute_dir_size (const vfs_path_t * dirname_vpath, void *ui, compute_dir_size_callback cback,
2571 size_t * ret_count, uintmax_t * ret_total, gboolean compute_symlinks)
2573 size_t marked = 0;
2575 return do_compute_dir_size (dirname_vpath, ui, cback, ret_count, &marked, ret_total,
2576 compute_symlinks);
2579 /* --------------------------------------------------------------------------------------------- */
2581 * panel_operate:
2583 * Performs one of the operations on the selection on the source_panel
2584 * (copy, delete, move).
2586 * Returns TRUE if did change the directory
2587 * structure, Returns FALSE if user aborted
2589 * force_single forces operation on the current entry and affects
2590 * default destination. Current filename is used as default.
2593 gboolean
2594 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2596 WPanel *panel = (WPanel *) source_panel;
2597 const gboolean single_entry = force_single || (panel->marked <= 1)
2598 || (get_current_type () == view_tree);
2600 char *source = NULL;
2601 #ifdef WITH_FULL_PATHS
2602 vfs_path_t *source_with_vpath = NULL;
2603 #else
2604 #define source_with_path source
2605 #endif /* !WITH_FULL_PATHS */
2606 char *dest = NULL;
2607 vfs_path_t *dest_vpath = NULL;
2608 char *temp = NULL;
2609 char *save_cwd = NULL, *save_dest = NULL;
2610 struct stat src_stat;
2611 gboolean ret_val = TRUE;
2612 int i;
2613 FileProgressStatus value;
2614 FileOpContext *ctx;
2615 FileOpTotalContext *tctx;
2616 vfs_path_t *tmp_vpath;
2617 filegui_dialog_type_t dialog_type = FILEGUI_DIALOG_ONE_ITEM;
2619 gboolean do_bg = FALSE; /* do background operation? */
2621 static gboolean i18n_flag = FALSE;
2622 if (!i18n_flag)
2624 for (i = G_N_ELEMENTS (op_names); i-- != 0;)
2625 op_names[i] = Q_ (op_names[i]);
2626 i18n_flag = TRUE;
2629 linklist = free_linklist (linklist);
2630 dest_dirs = free_linklist (dest_dirs);
2632 if (single_entry)
2634 vfs_path_t *source_vpath;
2636 if (force_single)
2637 source = g_strdup (selection (panel)->fname);
2638 else
2639 source = panel_get_file (panel);
2641 if (DIR_IS_DOTDOT (source))
2643 g_free (source);
2644 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2645 return FALSE;
2648 source_vpath = vfs_path_from_str (source);
2649 /* Update stat to get actual info */
2650 if (mc_lstat (source_vpath, &src_stat) != 0)
2652 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2653 path_trunc (source, 30), unix_error_string (errno));
2655 /* Directory was changed outside MC. Reload it forced */
2656 if (!panel->is_panelized)
2658 panel_update_flags_t flags = UP_RELOAD;
2660 /* don't update panelized panel */
2661 if (get_other_type () == view_listing && other_panel->is_panelized)
2662 flags |= UP_ONLY_CURRENT;
2664 update_panels (flags, UP_KEEPSEL);
2666 vfs_path_free (source_vpath);
2667 return FALSE;
2669 vfs_path_free (source_vpath);
2672 ctx = file_op_context_new (operation);
2674 /* Show confirmation dialog */
2675 if (operation != OP_DELETE)
2677 char *tmp_dest_dir, *dest_dir;
2678 char *format;
2680 /* Forced single operations default to the original name */
2681 if (force_single)
2682 tmp_dest_dir = g_strdup (source);
2683 else if (get_other_type () == view_listing)
2684 tmp_dest_dir = g_strdup (vfs_path_as_str (other_panel->cwd_vpath));
2685 else
2686 tmp_dest_dir = g_strdup (vfs_path_as_str (panel->cwd_vpath));
2688 * Add trailing backslash only when do non-local ops.
2689 * It saves user from occasional file renames (when destination
2690 * dir is deleted)
2692 if (!force_single && tmp_dest_dir[0] != '\0'
2693 && tmp_dest_dir[strlen (tmp_dest_dir) - 1] != PATH_SEP)
2695 /* add trailing separator */
2696 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
2697 g_free (tmp_dest_dir);
2699 else
2701 /* just copy */
2702 dest_dir = tmp_dest_dir;
2704 if (dest_dir == NULL)
2706 ret_val = FALSE;
2707 goto ret_fast;
2710 /* Generate confirmation prompt */
2711 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2713 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2714 source != NULL ? (void *) source
2715 : (void *) &panel->marked, dest_dir, &do_bg);
2717 g_free (format);
2718 g_free (dest_dir);
2720 if (dest == NULL || dest[0] == '\0')
2722 g_free (dest);
2723 ret_val = FALSE;
2724 goto ret_fast;
2726 dest_vpath = vfs_path_from_str (dest);
2728 else if (confirm_delete)
2730 char *format;
2731 char fmd_buf[BUF_MEDIUM];
2733 /* Generate confirmation prompt */
2734 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
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, *temp3, *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 temp3 = temp2;
2985 temp2 = strutils_shell_unescape (temp2);
2986 g_free (temp3);
2988 switch (operation)
2990 case OP_COPY:
2991 /* we use file_mask_op_follow_links only with OP_COPY */
2993 vfs_path_t *vpath;
2995 vpath = vfs_path_from_str (source_with_path_str);
2996 ctx->stat_func (vpath, &src_stat);
2997 vfs_path_free (vpath);
2999 if (S_ISDIR (src_stat.st_mode))
3000 value = copy_dir_dir (tctx, ctx, source_with_path_str, temp2,
3001 TRUE, FALSE, FALSE, NULL);
3002 else
3003 value = copy_file_file (tctx, ctx, source_with_path_str, temp2);
3004 dest_dirs = free_linklist (dest_dirs);
3005 break;
3007 case OP_MOVE:
3008 if (S_ISDIR (src_stat.st_mode))
3009 value = move_dir_dir (tctx, ctx, source_with_path_str, temp2);
3010 else
3011 value = move_file_file (tctx, ctx, source_with_path_str, temp2);
3012 break;
3014 default:
3015 /* Unknown file operation */
3016 abort ();
3019 g_free (temp2);
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 g_free (ctx->dest_mask);
3072 ctx->dest_mask = NULL;
3074 #ifdef ENABLE_BACKGROUND
3075 /* Let our parent know we are saying bye bye */
3076 if (mc_global.we_are_background)
3078 int cur_pid = getpid ();
3079 /* Send pid to parent with child context, it is fork and
3080 don't modify real parent ctx */
3081 ctx->pid = cur_pid;
3082 parent_call ((void *) end_bg_process, ctx, 0);
3084 vfs_shut ();
3085 my_exit (EXIT_SUCCESS);
3087 #endif /* ENABLE_BACKGROUND */
3089 file_op_total_context_destroy (tctx);
3090 ret_fast:
3091 file_op_context_destroy (ctx);
3092 g_free (source);
3094 return ret_val;
3097 /* }}} */
3099 /* --------------------------------------------------------------------------------------------- */
3100 /* {{{ Query/status report routines */
3101 /** Report error with one file */
3102 FileProgressStatus
3103 file_error (const char *format, const char *file)
3105 char buf[BUF_MEDIUM];
3107 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3109 return do_file_error (buf);
3112 /* --------------------------------------------------------------------------------------------- */
3115 Cause emacs to enter folding mode for this file:
3116 Local variables:
3117 end: