Code refactoring in tests.
[midnight-commander.git] / src / filemanager / file.c
bloba7a6c7d052fb31a6dbe2f8d2f1e2418eb1e676db
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 #include "layout.h" /* rotate_dash() */
82 /* Needed for current_panel, other_panel and WTree */
83 #include "dir.h"
84 #include "filegui.h"
85 #include "filenot.h"
86 #include "tree.h"
87 #include "midnight.h" /* current_panel */
89 #include "file.h"
91 /* }}} */
93 /*** global variables ****************************************************************************/
95 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
96 const char *op_names[3] = {
97 N_("DialogTitle|Copy"),
98 N_("DialogTitle|Move"),
99 N_("DialogTitle|Delete")
102 /*** file scope macro definitions ****************************************************************/
104 /* Hack: the vfs code should not rely on this */
105 #define WITH_FULL_PATHS 1
107 #define FILEOP_UPDATE_INTERVAL 2
108 #define FILEOP_STALLING_INTERVAL 4
110 /*** file scope type declarations ****************************************************************/
112 /* This is a hard link cache */
113 struct link
115 const struct vfs_class *vfs;
116 dev_t dev;
117 ino_t ino;
118 short linkcount;
119 mode_t st_mode;
120 vfs_path_t *src_vpath;
121 vfs_path_t *dst_vpath;
124 /* Status of the destination file */
125 typedef enum
127 DEST_NONE = 0, /* Not created */
128 DEST_SHORT = 1, /* Created, not fully copied */
129 DEST_FULL = 2 /* Created, fully copied */
130 } dest_status_t;
133 * This array introduced to avoid translation problems. The former (op_names)
134 * is assumed to be nouns, suitable in dialog box titles; this one should
135 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
136 * (I don't use spaces around the words, because someday they could be
137 * dropped, when widgets get smarter)
140 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
141 static const char *op_names1[] = {
142 N_("FileOperation|Copy"),
143 N_("FileOperation|Move"),
144 N_("FileOperation|Delete")
148 * These are formats for building a prompt. Parts encoded as follows:
149 * %o - operation from op_names1
150 * %f - file/files or files/directories, as appropriate
151 * %m - "with source mask" or question mark for delete
152 * %s - source name (truncated)
153 * %d - number of marked files
154 * %e - "to:" or question mark for delete
156 * xgettext:no-c-format */
157 static const char *one_format = N_("%o %f \"%s\"%m");
158 /* xgettext:no-c-format */
159 static const char *many_format = N_("%o %d %f%m");
161 static const char *prompt_parts[] = {
162 N_("file"),
163 N_("files"),
164 N_("directory"),
165 N_("directories"),
166 N_("files/directories"),
167 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
168 N_(" with source mask:"),
169 N_("to:")
172 static const char *question_format = N_("%s?");
174 /*** file scope variables ************************************************************************/
176 /* the hard link cache */
177 static GSList *linklist = NULL;
179 /* the files-to-be-erased list */
180 static GSList *erase_list = NULL;
183 * In copy_dir_dir we use two additional single linked lists: The first -
184 * variable name `parent_dirs' - holds information about already copied
185 * directories and is used to detect cyclic symbolic links.
186 * The second (`dest_dirs' below) holds information about just created
187 * target directories and is used to detect when an directory is copied
188 * into itself (we don't want to copy infinitly).
189 * Both lists don't use the linkcount and name structure members of struct
190 * link.
192 static GSList *dest_dirs = NULL;
194 static FileProgressStatus transform_error = FILE_CONT;
196 /*** file scope functions ************************************************************************/
197 /* --------------------------------------------------------------------------------------------- */
199 static char *
200 transform_source (FileOpContext * ctx, const char *source)
202 char *s, *q;
203 char *fnsource;
205 s = g_strdup (source);
207 /* We remove \n from the filename since regex routines would use \n as an anchor */
208 /* this is just to be allowed to maniupulate file names with \n on it */
209 for (q = s; *q != '\0'; q++)
210 if (*q == '\n')
211 *q = ' ';
213 fnsource = (char *) x_basename (s);
215 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
217 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
218 if (ctx->search_handle->error != MC_SEARCH_E_OK)
220 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
221 q = NULL;
222 transform_error = FILE_ABORT;
225 else
227 q = NULL;
228 transform_error = FILE_SKIP;
231 g_free (s);
232 return q;
235 /* --------------------------------------------------------------------------------------------- */
237 static void
238 free_link (void *data)
240 struct link *lp = (struct link *) data;
242 vfs_path_free (lp->src_vpath);
243 vfs_path_free (lp->dst_vpath);
244 g_free (lp);
247 /* --------------------------------------------------------------------------------------------- */
249 static void *
250 free_linklist (GSList * lp)
252 g_slist_foreach (lp, (GFunc) free_link, NULL);
253 g_slist_free (lp);
255 return NULL;
258 /* --------------------------------------------------------------------------------------------- */
260 static gboolean
261 is_in_linklist (const GSList * lp, const vfs_path_t * vpath, const struct stat *sb)
263 const struct vfs_class *class;
264 ino_t ino = sb->st_ino;
265 dev_t dev = sb->st_dev;
267 class = vfs_path_get_last_path_vfs (vpath);
269 for (; lp != NULL; lp = g_slist_next (lp))
271 const struct link *lnk = (const struct link *) lp->data;
273 if (lnk->vfs == class && lnk->ino == ino && lnk->dev == dev)
274 return TRUE;
276 return FALSE;
279 /* --------------------------------------------------------------------------------------------- */
281 * Check and made hardlink
283 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
284 * and a hardlink was succesfully made
287 static gboolean
288 check_hardlinks (const vfs_path_t * src_vpath, const vfs_path_t * dst_vpath, struct stat *pstat)
290 GSList *lp;
291 struct link *lnk;
293 const struct vfs_class *my_vfs;
294 ino_t ino = pstat->st_ino;
295 dev_t dev = pstat->st_dev;
296 struct stat link_stat;
298 if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
299 return FALSE;
301 my_vfs = vfs_path_get_by_index (src_vpath, -1)->class;
303 for (lp = linklist; lp != NULL; lp = g_slist_next (lp))
305 lnk = (struct link *) lp->data;
307 if (lnk->vfs == my_vfs && lnk->ino == ino && lnk->dev == dev)
309 const struct vfs_class *lp_name_class;
310 int stat_result;
312 lp_name_class = vfs_path_get_last_path_vfs (lnk->src_vpath);
313 stat_result = mc_stat (lnk->src_vpath, &link_stat);
315 if (stat_result == 0 && link_stat.st_ino == ino
316 && link_stat.st_dev == dev && lp_name_class == my_vfs)
318 const struct vfs_class *p_class, *dst_name_class;
320 dst_name_class = vfs_path_get_last_path_vfs (dst_vpath);
321 p_class = vfs_path_get_last_path_vfs (lnk->dst_vpath);
323 if (dst_name_class == p_class &&
324 mc_stat (lnk->dst_vpath, &link_stat) == 0 &&
325 mc_link (lnk->dst_vpath, dst_vpath) == 0)
326 return TRUE;
329 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
330 return FALSE;
334 lnk = g_new0 (struct link, 1);
335 if (lnk != NULL)
337 lnk->vfs = my_vfs;
338 lnk->ino = ino;
339 lnk->dev = dev;
340 lnk->src_vpath = vfs_path_clone (src_vpath);
341 lnk->dst_vpath = vfs_path_clone (dst_vpath);
342 linklist = g_slist_prepend (linklist, lnk);
345 return FALSE;
348 /* --------------------------------------------------------------------------------------------- */
350 * Duplicate the contents of the symbolic link src_path in dst_path.
351 * Try to make a stable symlink if the option "stable symlink" was
352 * set in the file mask dialog.
353 * If dst_path is an existing symlink it will be deleted silently
354 * (upper levels take already care of existing files at dst_path).
357 static FileProgressStatus
358 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
360 char link_target[MC_MAXPATHLEN];
361 int len;
362 FileProgressStatus return_status;
363 struct stat sb;
364 vfs_path_t *src_vpath;
365 vfs_path_t *dst_vpath;
366 gboolean dst_is_symlink;
367 vfs_path_t *link_target_vpath = NULL;
369 src_vpath = vfs_path_from_str (src_path);
370 dst_vpath = vfs_path_from_str (dst_path);
371 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
373 retry_src_readlink:
374 len = mc_readlink (src_vpath, link_target, MC_MAXPATHLEN - 1);
375 if (len < 0)
377 if (ctx->skip_all)
378 return_status = FILE_SKIPALL;
379 else
381 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
382 if (return_status == FILE_SKIPALL)
383 ctx->skip_all = TRUE;
384 if (return_status == FILE_RETRY)
385 goto retry_src_readlink;
387 goto ret;
389 link_target[len] = 0;
391 if (ctx->stable_symlinks)
394 if (!vfs_file_is_local (src_vpath) || !vfs_file_is_local (dst_vpath))
396 message (D_ERROR, MSG_ERROR,
397 _("Cannot make stable symlinks across"
398 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
399 ctx->stable_symlinks = FALSE;
403 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
405 char *p, *s;
406 vfs_path_t *q;
408 const char *r = strrchr (src_path, PATH_SEP);
410 if (r)
412 p = g_strndup (src_path, r - src_path + 1);
413 if (g_path_is_absolute (dst_path))
414 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
415 else
416 q = vfs_path_build_filename (p, dst_path, (char *) NULL);
418 if (vfs_path_tokens_count (q) > 1)
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 (strcmp (dirent->d_name, ".") == 0)
529 continue;
530 if (strcmp (dirent->d_name, "..") == 0)
531 continue;
533 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, NULL);
535 res = mc_lstat (tmp_vpath, &s);
536 if (res == 0)
538 if (S_ISDIR (s.st_mode))
540 ret =
541 do_compute_dir_size (tmp_vpath, ui, cback, dir_count, ret_marked, ret_total,
542 compute_symlinks);
543 if (ret == FILE_CONT)
544 ret =
545 (cback == NULL) ? FILE_CONT : cback (ui, tmp_vpath, *dir_count, *ret_total);
547 else
549 (*ret_marked)++;
550 *ret_total += (uintmax_t) s.st_size;
552 update_ui_count++;
553 if ((update_ui_count & 31) == 0)
554 ret =
555 (cback == NULL) ? FILE_CONT : cback (ui, tmp_vpath, *dir_count, *ret_total);
559 vfs_path_free (tmp_vpath);
562 mc_closedir (dir);
563 return ret;
566 /* --------------------------------------------------------------------------------------------- */
568 static FileProgressStatus
569 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add)
571 struct timeval tv_current;
572 static struct timeval tv_start = { };
574 tctx->progress_count++;
575 tctx->progress_bytes += (uintmax_t) add;
577 if (tv_start.tv_sec == 0)
579 gettimeofday (&tv_start, (struct timezone *) NULL);
581 gettimeofday (&tv_current, (struct timezone *) NULL);
582 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
584 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
586 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
587 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
589 tv_start.tv_sec = tv_current.tv_sec;
592 return check_progress_buttons (ctx);
595 /* --------------------------------------------------------------------------------------------- */
597 static FileProgressStatus
598 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
600 char *msg;
601 int result = 0;
602 const char *head_msg;
604 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
606 msg = g_strdup_printf (fmt, a, b);
607 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
608 g_free (msg);
609 do_refresh ();
611 return (result == 1) ? FILE_ABORT : FILE_SKIP;
614 /* --------------------------------------------------------------------------------------------- */
616 static FileProgressStatus
617 warn_same_file (const char *fmt, const char *a, const char *b)
619 #ifdef ENABLE_BACKGROUND
620 /* *INDENT-OFF* */
621 union
623 void *p;
624 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
625 const char *a, const char *b);
626 } pntr;
627 /* *INDENT-ON* */
629 pntr.f = real_warn_same_file;
631 if (mc_global.we_are_background)
632 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
633 #endif
634 return real_warn_same_file (Foreground, fmt, a, b);
637 /* --------------------------------------------------------------------------------------------- */
638 /* {{{ Query/status report routines */
640 static FileProgressStatus
641 real_do_file_error (enum OperationMode mode, const char *error)
643 int result;
644 const char *msg;
646 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
647 result =
648 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
650 switch (result)
652 case 0:
653 do_refresh ();
654 return FILE_SKIP;
656 case 1:
657 do_refresh ();
658 return FILE_SKIPALL;
660 case 2:
661 do_refresh ();
662 return FILE_RETRY;
664 case 3:
665 default:
666 return FILE_ABORT;
670 /* --------------------------------------------------------------------------------------------- */
672 static FileProgressStatus
673 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
676 if (ctx->recursive_result < RECURSIVE_ALWAYS)
679 const char *msg;
680 char *text;
682 msg = mode == Foreground
683 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
684 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
685 text = g_strdup_printf (msg, path_trunc (s, 30));
687 if (safe_delete)
688 query_set_sel (1);
690 ctx->recursive_result =
691 (FileCopyMode) query_dialog (op_names[OP_DELETE], text, D_ERROR, 5,
692 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
693 g_free (text);
695 if (ctx->recursive_result != RECURSIVE_ABORT)
696 do_refresh ();
699 switch (ctx->recursive_result)
701 case RECURSIVE_YES:
702 case RECURSIVE_ALWAYS:
703 return FILE_CONT;
705 case RECURSIVE_NO:
706 case RECURSIVE_NEVER:
707 return FILE_SKIP;
709 case RECURSIVE_ABORT:
710 default:
711 return FILE_ABORT;
715 /* --------------------------------------------------------------------------------------------- */
717 #ifdef ENABLE_BACKGROUND
718 static FileProgressStatus
719 do_file_error (const char *str)
721 union
723 void *p;
724 FileProgressStatus (*f) (enum OperationMode, const char *);
725 } pntr;
726 pntr.f = real_do_file_error;
728 if (mc_global.we_are_background)
729 return parent_call (pntr.p, NULL, 1, strlen (str), str);
730 else
731 return real_do_file_error (Foreground, str);
734 /* --------------------------------------------------------------------------------------------- */
736 static FileProgressStatus
737 query_recursive (FileOpContext * ctx, const char *s)
739 union
741 void *p;
742 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
743 } pntr;
744 pntr.f = real_query_recursive;
746 if (mc_global.we_are_background)
747 return parent_call (pntr.p, ctx, 1, strlen (s), s);
748 else
749 return real_query_recursive (ctx, Foreground, s);
752 /* --------------------------------------------------------------------------------------------- */
754 static FileProgressStatus
755 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
756 struct stat *_d_stat)
758 union
760 void *p;
761 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
762 struct stat *, struct stat *);
763 } pntr;
764 pntr.f = file_progress_real_query_replace;
766 if (mc_global.we_are_background)
767 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
768 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
769 else
770 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
773 #else
774 /* --------------------------------------------------------------------------------------------- */
776 static FileProgressStatus
777 do_file_error (const char *str)
779 return real_do_file_error (Foreground, str);
782 /* --------------------------------------------------------------------------------------------- */
784 static FileProgressStatus
785 query_recursive (FileOpContext * ctx, const char *s)
787 return real_query_recursive (ctx, Foreground, s);
790 /* --------------------------------------------------------------------------------------------- */
792 static FileProgressStatus
793 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
794 struct stat *_d_stat)
796 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
799 #endif /* !ENABLE_BACKGROUND */
801 /* --------------------------------------------------------------------------------------------- */
802 /** Report error with two files */
804 static FileProgressStatus
805 files_error (const char *format, const char *file1, const char *file2)
807 char buf[BUF_MEDIUM];
808 char *nfile1 = g_strdup (path_trunc (file1, 15));
809 char *nfile2 = g_strdup (path_trunc (file2, 15));
811 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
813 g_free (nfile1);
814 g_free (nfile2);
816 return do_file_error (buf);
819 /* }}} */
821 /* --------------------------------------------------------------------------------------------- */
823 static void
824 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
825 struct timeval tv_current, struct timeval tv_transfer_start,
826 off_t file_size, off_t n_read_total)
828 long dt;
830 /* 1. Update rotating dash after some time */
831 rotate_dash ();
833 /* 3. Compute ETA */
834 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
836 if (n_read_total == 0)
837 ctx->eta_secs = 0.0;
838 else
840 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
841 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
844 /* 4. Compute BPS rate */
845 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
846 if (ctx->bps_time < 1)
847 ctx->bps_time = 1;
848 ctx->bps = n_read_total / ctx->bps_time;
850 /* 5. Compute total ETA and BPS */
851 if (ctx->progress_bytes != 0)
853 uintmax_t remain_bytes;
855 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
856 #if 1
858 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
860 if (total_secs < 1)
861 total_secs = 1;
863 tctx->bps = tctx->copied_bytes / total_secs;
864 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
866 #else
867 /* broken on lot of little files */
868 tctx->bps_count++;
869 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
870 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
871 #endif
875 /* --------------------------------------------------------------------------------------------- */
877 /* {{{ Move routines */
878 static FileProgressStatus
879 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
881 struct stat src_stats, dst_stats;
882 FileProgressStatus return_status = FILE_CONT;
883 gboolean copy_done = FALSE;
884 gboolean old_ask_overwrite;
885 vfs_path_t *src_vpath, *dst_vpath;
887 src_vpath = vfs_path_from_str (s);
888 dst_vpath = vfs_path_from_str (d);
890 file_progress_show_source (ctx, src_vpath);
891 file_progress_show_target (ctx, dst_vpath);
893 if (check_progress_buttons (ctx) == FILE_ABORT)
895 return_status = FILE_ABORT;
896 goto ret;
899 mc_refresh ();
901 while (mc_lstat (src_vpath, &src_stats) != 0)
903 /* Source doesn't exist */
904 if (ctx->skip_all)
905 return_status = FILE_SKIPALL;
906 else
908 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
909 if (return_status == FILE_SKIPALL)
910 ctx->skip_all = TRUE;
913 if (return_status != FILE_RETRY)
914 goto ret;
917 if (mc_lstat (dst_vpath, &dst_stats) == 0)
919 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
921 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
922 goto ret;
925 if (S_ISDIR (dst_stats.st_mode))
927 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
928 do_refresh ();
929 return_status = FILE_SKIP;
930 goto ret;
933 if (confirm_overwrite)
935 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
936 if (return_status != FILE_CONT)
937 goto ret;
939 /* Ok to overwrite */
942 if (!ctx->do_append)
944 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
946 return_status = make_symlink (ctx, s, d);
947 if (return_status == FILE_CONT)
948 goto retry_src_remove;
949 goto ret;
952 if (mc_rename (src_vpath, dst_vpath) == 0)
954 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
955 goto ret;
958 #if 0
959 /* Comparison to EXDEV seems not to work in nfs if you're moving from
960 one nfs to the same, but on the server it is on two different
961 filesystems. Then nfs returns EIO instead of EXDEV.
962 Hope it will not hurt if we always in case of error try to copy/delete. */
963 else
964 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
966 if (errno != EXDEV)
968 if (ctx->skip_all)
969 return_status = FILE_SKIPALL;
970 else
972 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
973 if (return_status == FILE_SKIPALL)
974 ctx->skip_all = TRUE;
975 if (return_status == FILE_RETRY)
976 goto retry_rename;
979 goto ret;
981 #endif
983 /* Failed because filesystem boundary -> copy the file instead */
984 old_ask_overwrite = tctx->ask_overwrite;
985 tctx->ask_overwrite = FALSE;
986 return_status = copy_file_file (tctx, ctx, s, d);
987 tctx->ask_overwrite = old_ask_overwrite;
988 if (return_status != FILE_CONT)
989 goto ret;
991 copy_done = TRUE;
993 file_progress_show_source (ctx, NULL);
994 file_progress_show (ctx, 0, 0, "", FALSE);
996 return_status = check_progress_buttons (ctx);
997 if (return_status != FILE_CONT)
998 goto ret;
999 mc_refresh ();
1001 retry_src_remove:
1002 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
1004 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
1005 if (return_status == FILE_RETRY)
1006 goto retry_src_remove;
1007 if (return_status == FILE_SKIPALL)
1008 ctx->skip_all = TRUE;
1009 goto ret;
1012 if (!copy_done)
1013 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
1015 ret:
1016 vfs_path_free (src_vpath);
1017 vfs_path_free (dst_vpath);
1019 return return_status;
1022 /* }}} */
1024 /* --------------------------------------------------------------------------------------------- */
1025 /* {{{ Erase routines */
1026 /** Don't update progress status if progress_count==NULL */
1028 static FileProgressStatus
1029 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * vpath)
1031 int return_status;
1032 struct stat buf;
1033 char *s;
1035 s = vfs_path_to_str (vpath);
1036 file_progress_show_deleting (ctx, s);
1037 if (check_progress_buttons (ctx) == FILE_ABORT)
1039 g_free (s);
1040 return FILE_ABORT;
1042 mc_refresh ();
1044 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
1046 /* ignore, most likely the mc_unlink fails, too */
1047 buf.st_size = 0;
1050 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
1052 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
1053 if (return_status == FILE_ABORT)
1055 g_free (s);
1056 return return_status;
1058 if (return_status == FILE_RETRY)
1059 continue;
1060 if (return_status == FILE_SKIPALL)
1061 ctx->skip_all = TRUE;
1062 break;
1064 g_free (s);
1065 if (tctx->progress_count == 0)
1066 return FILE_CONT;
1067 return progress_update_one (tctx, ctx, buf.st_size);
1070 /* --------------------------------------------------------------------------------------------- */
1073 Recursive remove of files
1074 abort->cancel stack
1075 skip ->warn every level, gets default
1076 skipall->remove as much as possible
1078 static FileProgressStatus
1079 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
1081 struct dirent *next;
1082 struct stat buf;
1083 DIR *reading;
1084 char *path;
1085 FileProgressStatus return_status = FILE_CONT;
1086 vfs_path_t *vpath;
1088 if (strcmp (s, "..") == 0)
1089 return FILE_RETRY;
1091 vpath = vfs_path_from_str (s);
1092 reading = mc_opendir (vpath);
1094 if (reading == NULL)
1096 return_status = FILE_RETRY;
1097 goto ret;
1100 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1102 vfs_path_t *tmp_vpath;
1104 if (!strcmp (next->d_name, "."))
1105 continue;
1106 if (!strcmp (next->d_name, ".."))
1107 continue;
1108 path = mc_build_filename (s, next->d_name, NULL);
1109 tmp_vpath = vfs_path_from_str (path);
1110 if (mc_lstat (tmp_vpath, &buf) != 0)
1112 g_free (path);
1113 mc_closedir (reading);
1114 vfs_path_free (tmp_vpath);
1115 return_status = FILE_RETRY;
1116 goto ret;
1118 if (S_ISDIR (buf.st_mode))
1119 return_status = recursive_erase (tctx, ctx, path);
1120 else
1121 return_status = erase_file (tctx, ctx, tmp_vpath);
1122 vfs_path_free (tmp_vpath);
1123 g_free (path);
1125 mc_closedir (reading);
1126 if (return_status == FILE_ABORT)
1127 goto ret;
1129 file_progress_show_deleting (ctx, s);
1130 if (check_progress_buttons (ctx) == FILE_ABORT)
1132 return_status = FILE_ABORT;
1133 goto ret;
1135 mc_refresh ();
1137 while (my_rmdir (s) != 0 && !ctx->skip_all)
1139 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1140 if (return_status == FILE_RETRY)
1141 continue;
1142 if (return_status == FILE_ABORT)
1143 goto ret;
1144 if (return_status == FILE_SKIPALL)
1145 ctx->skip_all = TRUE;
1146 break;
1149 ret:
1150 vfs_path_free (vpath);
1151 return return_status;
1154 /* --------------------------------------------------------------------------------------------- */
1155 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1156 in the directory path points to, 0 else. */
1158 static int
1159 check_dir_is_empty (const vfs_path_t * vpath)
1161 DIR *dir;
1162 struct dirent *d;
1163 int i;
1165 dir = mc_opendir (vpath);
1166 if (!dir)
1167 return -1;
1169 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1171 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1172 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1173 continue; /* "." or ".." */
1174 i = 0;
1175 break;
1178 mc_closedir (dir);
1179 return i;
1182 /* --------------------------------------------------------------------------------------------- */
1184 static FileProgressStatus
1185 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1187 FileProgressStatus error;
1188 vfs_path_t *s_vpath;
1190 if (strcmp (s, "..") == 0)
1191 return FILE_SKIP;
1193 if (strcmp (s, ".") == 0)
1194 return FILE_SKIP;
1196 file_progress_show_deleting (ctx, s);
1197 if (check_progress_buttons (ctx) == FILE_ABORT)
1198 return FILE_ABORT;
1200 mc_refresh ();
1202 s_vpath = vfs_path_from_str (s);
1204 if (check_dir_is_empty (s_vpath) == 1) /* not empty or error */
1206 while (my_rmdir (s) != 0 && !ctx->skip_all)
1208 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1209 if (error == FILE_SKIPALL)
1210 ctx->skip_all = TRUE;
1211 if (error != FILE_RETRY)
1213 vfs_path_free (s_vpath);
1214 return error;
1219 vfs_path_free (s_vpath);
1220 return FILE_CONT;
1223 /* }}} */
1225 /* --------------------------------------------------------------------------------------------- */
1226 /* {{{ Panel operate routines */
1229 * Return currently selected entry name or the name of the first marked
1230 * entry if there is one.
1233 static char *
1234 panel_get_file (WPanel * panel)
1236 if (get_current_type () == view_tree)
1238 WTree *tree;
1240 tree = (WTree *) get_panel_widget (get_current_index ());
1241 return vfs_path_to_str (tree_selected_name (tree));
1244 if (panel->marked != 0)
1246 int i;
1248 for (i = 0; i < panel->count; i++)
1249 if (panel->dir.list[i].f.marked)
1250 return g_strdup (panel->dir.list[i].fname);
1252 return g_strdup (panel->dir.list[panel->selected].fname);
1255 /* --------------------------------------------------------------------------------------------- */
1257 * panel_compute_totals:
1259 * compute the number of files and the number of bytes
1260 * used up by the whole selection, recursing directories
1261 * as required. In addition, it checks to see if it will
1262 * overwrite any files by doing the copy.
1265 static FileProgressStatus
1266 panel_compute_totals (const WPanel * panel, void *ui, compute_dir_size_callback cback,
1267 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1269 int i;
1271 for (i = 0; i < panel->count; i++)
1273 struct stat *s;
1275 if (!panel->dir.list[i].f.marked)
1276 continue;
1278 s = &panel->dir.list[i].st;
1280 if (S_ISDIR (s->st_mode))
1282 vfs_path_t *p;
1283 FileProgressStatus status;
1285 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, NULL);
1286 status = compute_dir_size (p, ui, cback, ret_marked, ret_total, compute_symlinks);
1287 vfs_path_free (p);
1289 if (status != FILE_CONT)
1290 return status;
1292 else
1294 (*ret_marked)++;
1295 *ret_total += (uintmax_t) s->st_size;
1299 return FILE_CONT;
1302 /* --------------------------------------------------------------------------------------------- */
1304 /** Initialize variables for progress bars */
1305 static FileProgressStatus
1306 panel_operate_init_totals (FileOperation operation, const WPanel * panel, const char *source,
1307 FileOpContext * ctx, filegui_dialog_type_t dialog_type)
1309 FileProgressStatus status;
1311 #ifdef ENABLE_BACKGROUND
1312 if (mc_global.we_are_background)
1313 return FILE_CONT;
1314 #endif
1316 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1318 ComputeDirSizeUI *ui;
1320 ui = compute_dir_size_create_ui (TRUE);
1322 ctx->progress_count = 0;
1323 ctx->progress_bytes = 0;
1325 if (source == NULL)
1326 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1327 &ctx->progress_count, &ctx->progress_bytes,
1328 ctx->follow_links);
1329 else
1331 vfs_path_t *p;
1333 p = vfs_path_from_str (source);
1334 status = compute_dir_size (p, ui, compute_dir_size_update_ui,
1335 &ctx->progress_count, &ctx->progress_bytes,
1336 ctx->follow_links);
1337 vfs_path_free (p);
1340 compute_dir_size_destroy_ui (ui);
1342 ctx->progress_totals_computed = (status == FILE_CONT);
1344 if (status == FILE_SKIP)
1345 status = FILE_CONT;
1347 else
1349 status = FILE_CONT;
1350 ctx->progress_count = panel->marked;
1351 ctx->progress_bytes = panel->total;
1352 ctx->progress_totals_computed = FALSE;
1355 file_op_context_create_ui (ctx, TRUE, dialog_type);
1357 return status;
1360 /* --------------------------------------------------------------------------------------------- */
1362 * Generate user prompt for panel operation.
1363 * single_source is the name if the source entry or NULL for multiple
1364 * entries.
1365 * src_stat is only used when single_source is not NULL.
1368 static char *
1369 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1370 gboolean single_source, const struct stat *src_stat)
1372 const char *sp, *cp;
1373 char format_string[BUF_MEDIUM];
1374 char *dp = format_string;
1375 gboolean build_question = FALSE;
1377 static gboolean i18n_flag = FALSE;
1378 if (!i18n_flag)
1380 size_t i;
1382 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1383 op_names1[i] = Q_ (op_names1[i]);
1385 #ifdef ENABLE_NLS
1386 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1387 prompt_parts[i] = _(prompt_parts[i]);
1389 one_format = _(one_format);
1390 many_format = _(many_format);
1391 question_format = _(question_format);
1392 #endif /* ENABLE_NLS */
1393 i18n_flag = TRUE;
1396 sp = single_source ? one_format : many_format;
1398 while (*sp != '\0')
1400 switch (*sp)
1402 case '%':
1403 cp = NULL;
1404 switch (sp[1])
1406 case 'o':
1407 cp = op_names1[operation];
1408 break;
1409 case 'm':
1410 if (operation == OP_DELETE)
1412 cp = "";
1413 build_question = TRUE;
1415 else
1416 cp = prompt_parts[5];
1417 break;
1418 case 'e':
1419 if (operation == OP_DELETE)
1421 cp = "";
1422 build_question = TRUE;
1424 else
1425 cp = prompt_parts[6];
1426 break;
1427 case 'f':
1428 if (single_source)
1429 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1430 else
1431 cp = (panel->marked == panel->dirs_marked)
1432 ? prompt_parts[3]
1433 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1434 break;
1435 default:
1436 *dp++ = *sp++;
1439 if (cp != NULL)
1441 sp += 2;
1443 while (*cp != '\0')
1444 *dp++ = *cp++;
1446 /* form two-lines query prompt for file deletion */
1447 if (operation == OP_DELETE && sp[-1] == 'f')
1449 *dp++ = '\n';
1451 while (isblank (*sp) != 0)
1452 sp++;
1455 break;
1456 default:
1457 *dp++ = *sp++;
1460 *dp = '\0';
1462 if (build_question)
1464 char tmp[BUF_MEDIUM];
1466 memmove (tmp, format_string, sizeof (tmp));
1467 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1470 return g_strdup (format_string);
1473 /* --------------------------------------------------------------------------------------------- */
1475 #ifdef ENABLE_BACKGROUND
1476 static int
1477 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1479 int pid = ctx->pid;
1481 (void) mode;
1482 ctx->pid = 0;
1484 unregister_task_with_pid (pid);
1485 /* file_op_context_destroy(ctx); */
1486 return 1;
1488 #endif
1489 /* }}} */
1491 /* --------------------------------------------------------------------------------------------- */
1492 /*** public functions ****************************************************************************/
1493 /* --------------------------------------------------------------------------------------------- */
1495 FileProgressStatus
1496 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1497 const char *src_path, const char *dst_path)
1499 uid_t src_uid = (uid_t) (-1);
1500 gid_t src_gid = (gid_t) (-1);
1502 int src_desc, dest_desc = -1;
1503 int n_read, n_written;
1504 mode_t src_mode = 0; /* The mode of the source file */
1505 struct stat sb, sb2;
1506 struct utimbuf utb;
1507 gboolean dst_exists = FALSE, appending = FALSE;
1508 off_t file_size = -1;
1509 FileProgressStatus return_status, temp_status;
1510 struct timeval tv_transfer_start;
1511 dest_status_t dst_status = DEST_NONE;
1512 int open_flags;
1513 gboolean is_first_time = TRUE;
1514 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1515 gboolean write_errno_nospace = FALSE;
1517 /* FIXME: We should not be using global variables! */
1518 ctx->do_reget = 0;
1519 return_status = FILE_RETRY;
1521 dst_vpath = vfs_path_from_str (dst_path);
1522 src_vpath = vfs_path_from_str (src_path);
1524 file_progress_show_source (ctx, src_vpath);
1525 file_progress_show_target (ctx, dst_vpath);
1527 if (check_progress_buttons (ctx) == FILE_ABORT)
1529 return_status = FILE_ABORT;
1530 goto ret_fast;
1533 mc_refresh ();
1535 while (mc_stat (dst_vpath, &sb2) == 0)
1537 if (S_ISDIR (sb2.st_mode))
1539 if (ctx->skip_all)
1540 return_status = FILE_SKIPALL;
1541 else
1543 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1544 if (return_status == FILE_SKIPALL)
1545 ctx->skip_all = TRUE;
1546 if (return_status == FILE_RETRY)
1547 continue;
1549 goto ret_fast;
1552 dst_exists = TRUE;
1553 break;
1556 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1558 if (ctx->skip_all)
1559 return_status = FILE_SKIPALL;
1560 else
1562 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1563 if (return_status == FILE_SKIPALL)
1564 ctx->skip_all = TRUE;
1567 if (return_status != FILE_RETRY)
1568 goto ret_fast;
1571 if (dst_exists)
1573 /* Destination already exists */
1574 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1576 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1577 src_path, dst_path);
1578 goto ret_fast;
1581 /* Should we replace destination? */
1582 if (tctx->ask_overwrite)
1584 ctx->do_reget = 0;
1585 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1586 if (return_status != FILE_CONT)
1587 goto ret_fast;
1591 if (!ctx->do_append)
1593 /* Check the hardlinks */
1594 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &sb))
1596 /* We have made a hardlink - no more processing is necessary */
1597 return_status = FILE_CONT;
1598 goto ret_fast;
1601 if (S_ISLNK (sb.st_mode))
1603 return_status = make_symlink (ctx, src_path, dst_path);
1604 goto ret_fast;
1607 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1608 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1610 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1611 && !ctx->skip_all)
1613 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1614 if (return_status == FILE_RETRY)
1615 continue;
1616 if (return_status == FILE_SKIPALL)
1617 ctx->skip_all = TRUE;
1618 goto ret_fast;
1620 /* Success */
1622 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1623 && !ctx->skip_all)
1625 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1626 if (temp_status == FILE_SKIP)
1627 break;
1628 if (temp_status == FILE_SKIPALL)
1629 ctx->skip_all = TRUE;
1630 if (temp_status != FILE_RETRY)
1632 return_status = temp_status;
1633 goto ret_fast;
1637 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1638 && !ctx->skip_all)
1640 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1641 if (temp_status == FILE_SKIP)
1642 break;
1643 if (temp_status == FILE_SKIPALL)
1644 ctx->skip_all = TRUE;
1645 if (temp_status != FILE_RETRY)
1647 return_status = temp_status;
1648 goto ret_fast;
1652 return_status = FILE_CONT;
1653 goto ret_fast;
1657 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1659 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1661 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1662 if (return_status == FILE_RETRY)
1663 continue;
1664 if (return_status == FILE_SKIPALL)
1665 ctx->skip_all = TRUE;
1666 if (return_status == FILE_SKIP)
1667 break;
1668 ctx->do_append = 0;
1669 goto ret_fast;
1672 if (ctx->do_reget != 0)
1674 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1676 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1677 ctx->do_reget = 0;
1678 ctx->do_append = FALSE;
1682 while (mc_fstat (src_desc, &sb) != 0)
1684 if (ctx->skip_all)
1685 return_status = FILE_SKIPALL;
1686 else
1688 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1689 if (return_status == FILE_RETRY)
1690 continue;
1691 if (return_status == FILE_SKIPALL)
1692 ctx->skip_all = TRUE;
1693 ctx->do_append = FALSE;
1695 goto ret;
1698 src_mode = sb.st_mode;
1699 src_uid = sb.st_uid;
1700 src_gid = sb.st_gid;
1701 utb.actime = sb.st_atime;
1702 utb.modtime = sb.st_mtime;
1703 file_size = sb.st_size;
1705 open_flags = O_WRONLY;
1706 if (dst_exists)
1708 if (ctx->do_append != 0)
1709 open_flags |= O_APPEND;
1710 else
1711 open_flags |= O_CREAT | O_TRUNC;
1713 else
1715 open_flags |= O_CREAT | O_EXCL;
1718 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
1720 if (errno != EEXIST)
1722 if (ctx->skip_all)
1723 return_status = FILE_SKIPALL;
1724 else
1726 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1727 if (return_status == FILE_RETRY)
1728 continue;
1729 if (return_status == FILE_SKIPALL)
1730 ctx->skip_all = TRUE;
1731 ctx->do_append = FALSE;
1734 goto ret;
1736 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1738 appending = ctx->do_append;
1739 ctx->do_append = FALSE;
1741 /* Find out the optimal buffer size. */
1742 while (mc_fstat (dest_desc, &sb) != 0)
1744 if (ctx->skip_all)
1745 return_status = FILE_SKIPALL;
1746 else
1748 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1749 if (return_status == FILE_RETRY)
1750 continue;
1751 if (return_status == FILE_SKIPALL)
1752 ctx->skip_all = TRUE;
1754 goto ret;
1757 while (TRUE)
1759 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1760 if (errno == 0)
1761 break;
1763 if (ctx->skip_all)
1764 return_status = FILE_SKIPALL;
1765 else
1767 return_status =
1768 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1769 if (return_status == FILE_RETRY)
1770 continue;
1771 if (return_status == FILE_SKIPALL)
1772 ctx->skip_all = TRUE;
1774 mc_close (dest_desc);
1775 dest_desc = -1;
1776 mc_unlink (dst_vpath);
1777 dst_status = DEST_NONE;
1778 goto ret;
1781 ctx->eta_secs = 0.0;
1782 ctx->bps = 0;
1784 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1785 file_progress_show (ctx, 0, file_size, "", TRUE);
1786 else
1787 file_progress_show (ctx, 1, 1, "", TRUE);
1788 return_status = check_progress_buttons (ctx);
1789 mc_refresh ();
1791 if (return_status != FILE_CONT)
1792 goto ret;
1795 off_t n_read_total = 0;
1796 struct timeval tv_current, tv_last_update, tv_last_input;
1797 int secs, update_secs;
1798 const char *stalled_msg = "";
1800 tv_last_update = tv_transfer_start;
1802 while (TRUE)
1804 char buf[BUF_8K];
1806 /* src_read */
1807 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1808 n_read = -1;
1809 else
1810 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1812 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1813 if (return_status == FILE_RETRY)
1814 continue;
1815 if (return_status == FILE_SKIPALL)
1816 ctx->skip_all = TRUE;
1817 goto ret;
1819 if (n_read == 0)
1820 break;
1822 gettimeofday (&tv_current, NULL);
1824 if (n_read > 0)
1826 char *t = buf;
1827 n_read_total += n_read;
1829 /* Windows NT ftp servers report that files have no
1830 * permissions: -------, so if we happen to have actually
1831 * read something, we should fix the permissions.
1833 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1834 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1835 gettimeofday (&tv_last_input, NULL);
1837 /* dst_write */
1838 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read)
1840 if (n_written > 0)
1842 n_read -= n_written;
1843 t += n_written;
1844 continue;
1847 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
1849 if (ctx->skip_all)
1850 return_status = FILE_SKIPALL;
1851 else
1852 return_status =
1853 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1855 if (return_status == FILE_SKIP)
1857 if (write_errno_nospace)
1858 goto ret;
1859 break;
1861 if (return_status == FILE_SKIPALL)
1863 ctx->skip_all = TRUE;
1864 if (write_errno_nospace)
1865 goto ret;
1867 if (return_status != FILE_RETRY)
1868 goto ret;
1870 /* User pressed "Retry". Will the next mc_write() call be succesful?
1871 * Reset error flag to be ready for that. */
1872 write_errno_nospace = FALSE;
1876 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1878 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1879 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1881 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1883 copy_file_file_display_progress (tctx, ctx,
1884 tv_current,
1885 tv_transfer_start, file_size, n_read_total);
1886 tv_last_update = tv_current;
1888 is_first_time = FALSE;
1890 if (update_secs > FILEOP_STALLING_INTERVAL)
1892 stalled_msg = _("(stalled)");
1896 gboolean force_update;
1898 force_update =
1899 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1901 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1903 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1904 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1907 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1908 force_update);
1910 mc_refresh ();
1912 return_status = check_progress_buttons (ctx);
1914 if (return_status != FILE_CONT)
1916 mc_refresh ();
1917 goto ret;
1922 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1924 ret:
1925 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1927 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1928 if (temp_status == FILE_RETRY)
1929 continue;
1930 if (temp_status == FILE_ABORT)
1931 return_status = temp_status;
1932 if (temp_status == FILE_SKIPALL)
1933 ctx->skip_all = TRUE;
1934 break;
1937 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1939 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1940 if (temp_status == FILE_RETRY)
1941 continue;
1942 if (temp_status == FILE_SKIPALL)
1943 ctx->skip_all = TRUE;
1944 return_status = temp_status;
1945 break;
1948 if (dst_status == DEST_SHORT)
1950 /* Remove short file */
1951 int result = 0;
1953 /* In case of copy/move to full partition, keep source file
1954 * and remove incomplete destination one */
1955 if (!write_errno_nospace)
1956 result = query_dialog (Q_ ("DialogTitle|Copy"),
1957 _("Incomplete file was retrieved. Keep it?"),
1958 D_ERROR, 2, _("&Delete"), _("&Keep"));
1959 if (result == 0)
1960 mc_unlink (dst_vpath);
1962 else if (dst_status == DEST_FULL)
1964 /* Copy has succeeded */
1965 if (!appending && ctx->preserve_uidgid)
1967 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1969 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1970 if (temp_status == FILE_RETRY)
1971 continue;
1972 if (temp_status == FILE_SKIPALL)
1974 ctx->skip_all = TRUE;
1975 return_status = FILE_CONT;
1977 if (temp_status == FILE_SKIP)
1978 return_status = FILE_CONT;
1979 break;
1983 if (!appending)
1985 if (ctx->preserve)
1987 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1989 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1990 if (temp_status == FILE_RETRY)
1991 continue;
1992 if (temp_status == FILE_SKIPALL)
1994 ctx->skip_all = TRUE;
1995 return_status = FILE_CONT;
1997 if (temp_status == FILE_SKIP)
1998 return_status = FILE_CONT;
1999 break;
2002 else if (!dst_exists)
2004 src_mode = umask (-1);
2005 umask (src_mode);
2006 src_mode = 0100666 & ~src_mode;
2007 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
2009 mc_utime (dst_vpath, &utb);
2013 if (return_status == FILE_CONT)
2014 return_status = progress_update_one (tctx, ctx, file_size);
2016 ret_fast:
2017 vfs_path_free (src_vpath);
2018 vfs_path_free (dst_vpath);
2019 return return_status;
2022 /* --------------------------------------------------------------------------------------------- */
2024 * I think these copy_*_* functions should have a return type.
2025 * anyway, this function *must* have two directories as arguments.
2027 /* FIXME: This function needs to check the return values of the
2028 function calls */
2030 FileProgressStatus
2031 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d,
2032 gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs)
2034 struct dirent *next;
2035 struct stat buf, cbuf;
2036 DIR *reading;
2037 char *dest_dir = NULL;
2038 FileProgressStatus return_status = FILE_CONT;
2039 struct utimbuf utb;
2040 struct link *lp;
2041 vfs_path_t *src_vpath, *dst_vpath, *dest_dir_vpath = NULL;
2042 gboolean do_mkdir = TRUE;
2044 src_vpath = vfs_path_from_str (s);
2045 dst_vpath = vfs_path_from_str (d);
2047 /* First get the mode of the source dir */
2049 retry_src_stat:
2050 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
2052 if (ctx->skip_all)
2053 return_status = FILE_SKIPALL;
2054 else
2056 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
2057 if (return_status == FILE_RETRY)
2058 goto retry_src_stat;
2059 if (return_status == FILE_SKIPALL)
2060 ctx->skip_all = TRUE;
2062 goto ret_fast;
2065 if (is_in_linklist (dest_dirs, src_vpath, &cbuf))
2067 /* Don't copy a directory we created before (we don't want to copy
2068 infinitely if a directory is copied into itself) */
2069 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2070 return_status = FILE_CONT;
2071 goto ret_fast;
2074 /* Hmm, hardlink to directory??? - Norbert */
2075 /* FIXME: In this step we should do something
2076 in case the destination already exist */
2077 /* Check the hardlinks */
2078 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &cbuf))
2080 /* We have made a hardlink - no more processing is necessary */
2081 goto ret_fast;
2084 if (!S_ISDIR (cbuf.st_mode))
2086 if (ctx->skip_all)
2087 return_status = FILE_SKIPALL;
2088 else
2090 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
2091 if (return_status == FILE_RETRY)
2092 goto retry_src_stat;
2093 if (return_status == FILE_SKIPALL)
2094 ctx->skip_all = TRUE;
2096 goto ret_fast;
2099 if (is_in_linklist (parent_dirs, src_vpath, &cbuf))
2101 /* we found a cyclic symbolic link */
2102 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
2103 return_status = FILE_SKIP;
2104 goto ret_fast;
2107 lp = g_new0 (struct link, 1);
2108 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
2109 lp->ino = cbuf.st_ino;
2110 lp->dev = cbuf.st_dev;
2111 parent_dirs = g_slist_prepend (parent_dirs, lp);
2113 retry_dst_stat:
2114 /* Now, check if the dest dir exists, if not, create it. */
2115 if (mc_stat (dst_vpath, &buf) != 0)
2117 /* Here the dir doesn't exist : make it ! */
2118 if (move_over)
2120 if (mc_rename (src_vpath, dst_vpath) == 0)
2122 return_status = FILE_CONT;
2123 goto ret;
2126 dest_dir = g_strdup (d);
2128 else
2131 * If the destination directory exists, we want to copy the whole
2132 * directory, but we only want this to happen once.
2134 * Escape sequences added to the * to compiler warnings.
2135 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2136 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2138 if (!S_ISDIR (buf.st_mode))
2140 if (ctx->skip_all)
2141 return_status = FILE_SKIPALL;
2142 else
2144 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2145 if (return_status == FILE_SKIPALL)
2146 ctx->skip_all = TRUE;
2147 if (return_status == FILE_RETRY)
2148 goto retry_dst_stat;
2150 goto ret;
2152 /* Dive into subdir if exists */
2153 if (toplevel && ctx->dive_into_subdirs)
2154 dest_dir = mc_build_filename (d, x_basename (s), NULL);
2155 else
2157 dest_dir = g_strdup (d);
2158 do_mkdir = FALSE;
2162 dest_dir_vpath = vfs_path_from_str (dest_dir);
2164 if (do_mkdir)
2166 while (my_mkdir (dest_dir_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2168 if (ctx->skip_all)
2169 return_status = FILE_SKIPALL;
2170 else
2172 return_status =
2173 file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
2174 if (return_status == FILE_SKIPALL)
2175 ctx->skip_all = TRUE;
2177 if (return_status != FILE_RETRY)
2178 goto ret;
2181 lp = g_new0 (struct link, 1);
2182 mc_stat (dest_dir_vpath, &buf);
2183 lp->vfs = vfs_path_get_by_index (dest_dir_vpath, -1)->class;
2184 lp->ino = buf.st_ino;
2185 lp->dev = buf.st_dev;
2186 dest_dirs = g_slist_prepend (dest_dirs, lp);
2189 if (ctx->preserve_uidgid)
2191 while (mc_chown (dest_dir_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2193 if (ctx->skip_all)
2194 return_status = FILE_SKIPALL;
2195 else
2197 return_status =
2198 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
2199 if (return_status == FILE_SKIPALL)
2200 ctx->skip_all = TRUE;
2202 if (return_status != FILE_RETRY)
2203 goto ret;
2207 /* open the source dir for reading */
2208 reading = mc_opendir (src_vpath);
2209 if (reading == NULL)
2210 goto ret;
2212 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2214 char *path;
2215 vfs_path_t *tmp_vpath;
2217 * Now, we don't want '.' and '..' to be created / copied at any time
2219 if (!strcmp (next->d_name, "."))
2220 continue;
2221 if (!strcmp (next->d_name, ".."))
2222 continue;
2224 /* get the filename and add it to the src directory */
2225 path = mc_build_filename (s, next->d_name, NULL);
2226 tmp_vpath = vfs_path_from_str (path);
2228 (*ctx->stat_func) (tmp_vpath, &buf);
2229 if (S_ISDIR (buf.st_mode))
2231 char *mdpath;
2233 mdpath = mc_build_filename (dest_dir, next->d_name, NULL);
2235 * From here, we just intend to recursively copy subdirs, not
2236 * the double functionality of copying different when the target
2237 * dir already exists. So, we give the recursive call the flag 0
2238 * meaning no toplevel.
2240 return_status =
2241 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2242 g_free (mdpath);
2244 else
2246 char *dest_file;
2248 dest_file = mc_build_filename (dest_dir, x_basename (path), NULL);
2249 return_status = copy_file_file (tctx, ctx, path, dest_file);
2250 g_free (dest_file);
2252 if (do_delete && return_status == FILE_CONT)
2254 if (ctx->erase_at_end)
2256 lp = g_new0 (struct link, 1);
2257 lp->src_vpath = vfs_path_clone (tmp_vpath);
2258 lp->st_mode = buf.st_mode;
2259 erase_list = g_slist_append (erase_list, lp);
2261 else if (S_ISDIR (buf.st_mode))
2262 return_status = erase_dir_iff_empty (ctx, path);
2263 else
2264 return_status = erase_file (tctx, ctx, tmp_vpath);
2266 g_free (path);
2267 vfs_path_free (tmp_vpath);
2269 mc_closedir (reading);
2271 if (ctx->preserve)
2273 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2274 utb.actime = cbuf.st_atime;
2275 utb.modtime = cbuf.st_mtime;
2276 mc_utime (dest_dir_vpath, &utb);
2278 else
2280 cbuf.st_mode = umask (-1);
2281 umask (cbuf.st_mode);
2282 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2283 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2286 ret:
2287 g_free (dest_dir);
2288 vfs_path_free (dest_dir_vpath);
2289 free_link (parent_dirs->data);
2290 g_slist_free_1 (parent_dirs);
2291 ret_fast:
2292 vfs_path_free (src_vpath);
2293 vfs_path_free (dst_vpath);
2294 return return_status;
2297 /* }}} */
2299 /* --------------------------------------------------------------------------------------------- */
2300 /* {{{ Move routines */
2302 FileProgressStatus
2303 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2305 struct stat sbuf, dbuf, destbuf;
2306 struct link *lp;
2307 char *destdir;
2308 FileProgressStatus return_status;
2309 gboolean move_over = FALSE;
2310 gboolean dstat_ok;
2311 vfs_path_t *src_vpath, *dst_vpath, *destdir_vpath;
2313 src_vpath = vfs_path_from_str (s);
2314 dst_vpath = vfs_path_from_str (d);
2316 file_progress_show_source (ctx, src_vpath);
2317 file_progress_show_target (ctx, dst_vpath);
2319 if (check_progress_buttons (ctx) == FILE_ABORT)
2321 return_status = FILE_ABORT;
2322 goto ret_fast;
2325 mc_refresh ();
2327 mc_stat (src_vpath, &sbuf);
2329 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2330 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2332 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2333 goto ret_fast;
2336 if (!dstat_ok)
2337 destdir = g_strdup (d); /* destination doesn't exist */
2338 else if (!ctx->dive_into_subdirs)
2340 destdir = g_strdup (d);
2341 move_over = TRUE;
2343 else
2344 destdir = mc_build_filename (d, x_basename (s), NULL);
2346 destdir_vpath = vfs_path_from_str (destdir);
2348 /* Check if the user inputted an existing dir */
2349 retry_dst_stat:
2350 if (mc_stat (destdir_vpath, &destbuf) == 0)
2352 if (move_over)
2354 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2356 if (return_status != FILE_CONT)
2357 goto ret;
2358 goto oktoret;
2360 else if (ctx->skip_all)
2361 return_status = FILE_SKIPALL;
2362 else
2364 if (S_ISDIR (destbuf.st_mode))
2365 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2366 else
2367 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2368 if (return_status == FILE_SKIPALL)
2369 ctx->skip_all = TRUE;
2370 if (return_status == FILE_RETRY)
2371 goto retry_dst_stat;
2374 g_free (destdir);
2375 vfs_path_free (destdir_vpath);
2376 goto ret_fast;
2379 retry_rename:
2380 if (mc_rename (src_vpath, destdir_vpath) == 0)
2382 return_status = FILE_CONT;
2383 goto ret;
2386 if (errno != EXDEV)
2388 if (!ctx->skip_all)
2390 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2391 if (return_status == FILE_SKIPALL)
2392 ctx->skip_all = TRUE;
2393 if (return_status == FILE_RETRY)
2394 goto retry_rename;
2396 goto ret;
2398 /* Failed because of filesystem boundary -> copy dir instead */
2399 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2401 if (return_status != FILE_CONT)
2402 goto ret;
2403 oktoret:
2404 file_progress_show_source (ctx, NULL);
2405 file_progress_show (ctx, 0, 0, "", FALSE);
2407 return_status = check_progress_buttons (ctx);
2408 if (return_status != FILE_CONT)
2409 goto ret;
2411 mc_refresh ();
2412 if (ctx->erase_at_end)
2414 for (; erase_list != NULL && return_status != FILE_ABORT;)
2416 lp = (struct link *) erase_list->data;
2418 if (S_ISDIR (lp->st_mode))
2420 char *src_path;
2422 src_path = vfs_path_to_str (lp->src_vpath);
2423 return_status = erase_dir_iff_empty (ctx, src_path);
2424 g_free (src_path);
2426 else
2427 return_status = erase_file (tctx, ctx, lp->src_vpath);
2429 erase_list = g_slist_remove (erase_list, lp);
2430 free_link (lp);
2433 erase_dir_iff_empty (ctx, s);
2435 ret:
2436 g_free (destdir);
2437 vfs_path_free (destdir_vpath);
2438 erase_list = free_linklist (erase_list);
2439 ret_fast:
2440 vfs_path_free (src_vpath);
2441 vfs_path_free (dst_vpath);
2442 return return_status;
2445 /* }}} */
2447 /* --------------------------------------------------------------------------------------------- */
2448 /* {{{ Erase routines */
2450 FileProgressStatus
2451 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * s_vpath)
2453 FileProgressStatus error;
2454 char *s;
2456 s = vfs_path_to_str (s_vpath);
2459 if (strcmp (s, "..") == 0)
2460 return FILE_SKIP;
2462 if (strcmp (s, ".") == 0)
2463 return FILE_SKIP;
2466 file_progress_show_deleting (ctx, s);
2467 if (check_progress_buttons (ctx) == FILE_ABORT)
2469 g_free (s);
2470 return FILE_ABORT;
2472 mc_refresh ();
2474 /* The old way to detect a non empty directory was:
2475 error = my_rmdir (s);
2476 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2477 For the linux user space nfs server (nfs-server-2.2beta29-2)
2478 we would have to check also for EIO. I hope the new way is
2479 fool proof. (Norbert)
2481 error = check_dir_is_empty (s_vpath);
2482 if (error == 0)
2483 { /* not empty */
2484 error = query_recursive (ctx, s);
2485 if (error == FILE_CONT)
2486 error = recursive_erase (tctx, ctx, s);
2487 g_free (s);
2488 return error;
2491 while (my_rmdir (s) == -1 && !ctx->skip_all)
2493 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2494 if (error != FILE_RETRY)
2496 g_free (s);
2497 return error;
2501 g_free (s);
2502 return FILE_CONT;
2505 /* }}} */
2507 /* --------------------------------------------------------------------------------------------- */
2508 /* {{{ Panel operate routines */
2510 ComputeDirSizeUI *
2511 compute_dir_size_create_ui (gboolean allow_skip)
2513 ComputeDirSizeUI *ui;
2515 const char *b1_name = N_("&Abort");
2516 const char *b2_name = N_("&Skip");
2517 int b1_width, b2_width = 0, b_width = 0;
2518 int b1_x;
2519 int ui_width;
2520 Widget *b;
2522 #ifdef ENABLE_NLS
2523 b1_name = _(b1_name);
2524 b2_name = _(b2_name);
2525 #endif
2527 b1_width = str_term_width1 (b1_name) + 4;
2528 if (allow_skip)
2529 b2_width = str_term_width1 (b2_name) + 4 + 1;
2530 b_width = b1_width + b2_width;
2532 ui = g_new (ComputeDirSizeUI, 1);
2534 ui_width = max (COLS / 2, b_width + 6);
2535 ui->dlg = create_dlg (TRUE, 0, 0, 8, ui_width, dialog_colors, NULL, NULL, NULL,
2536 _("Directory scanning"), DLG_CENTER);
2538 ui->dirname = label_new (2, 3, "");
2539 add_widget (ui->dlg, ui->dirname);
2540 add_widget (ui->dlg, hline_new (4, -1, -1));
2541 b1_x = (ui_width - b_width) / 2;
2542 b = WIDGET (button_new (5, b1_x, FILE_ABORT, NORMAL_BUTTON, b1_name, NULL));
2543 add_widget (ui->dlg, b);
2544 if (allow_skip)
2546 add_widget (ui->dlg,
2547 button_new (5, b1_x + 1 + b1_width, FILE_SKIP, NORMAL_BUTTON, b2_name, NULL));
2548 dlg_select_widget (b);
2551 /* We will manage the dialog without any help,
2552 that's why we have to call init_dlg */
2553 init_dlg (ui->dlg);
2555 return ui;
2558 /* --------------------------------------------------------------------------------------------- */
2560 void
2561 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2563 if (ui != NULL)
2565 /* schedule to update passive panel */
2566 other_panel->dirty = 1;
2568 /* close and destroy dialog */
2569 dlg_run_done (ui->dlg);
2570 destroy_dlg (ui->dlg);
2571 g_free (ui);
2575 /* --------------------------------------------------------------------------------------------- */
2577 FileProgressStatus
2578 compute_dir_size_update_ui (void *ui, const vfs_path_t * dirname_vpath, size_t dir_count,
2579 uintmax_t total_size)
2581 ComputeDirSizeUI *this = (ComputeDirSizeUI *) ui;
2582 int c;
2583 Gpm_Event event;
2584 char *dirname;
2585 char buffer[BUF_1K];
2587 if (ui == NULL)
2588 return FILE_CONT;
2590 dirname = vfs_path_to_str (dirname_vpath);
2591 g_snprintf (buffer, sizeof (buffer), _("%s\nDirectories: %zd, total size: %s"),
2592 str_trunc (dirname, WIDGET (this->dlg)->cols - 6), dir_count,
2593 size_trunc_sep (total_size, panels_options.kilobyte_si));
2594 g_free (dirname);
2595 label_set_text (this->dirname, buffer);
2597 event.x = -1; /* Don't show the GPM cursor */
2598 c = tty_get_event (&event, FALSE, FALSE);
2599 if (c == EV_NONE)
2600 return FILE_CONT;
2602 /* Reinitialize to avoid old values after events other than
2603 selecting a button */
2604 this->dlg->ret_value = FILE_CONT;
2606 dlg_process_event (this->dlg, c, &event);
2608 switch (this->dlg->ret_value)
2610 case B_CANCEL:
2611 case FILE_ABORT:
2612 return FILE_ABORT;
2613 case FILE_SKIP:
2614 return FILE_SKIP;
2615 default:
2616 return FILE_CONT;
2620 /* --------------------------------------------------------------------------------------------- */
2622 * compute_dir_size:
2624 * Computes the number of bytes used by the files in a directory
2627 FileProgressStatus
2628 compute_dir_size (const vfs_path_t * dirname_vpath, void *ui, compute_dir_size_callback cback,
2629 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2631 size_t dir_count = 0;
2633 return do_compute_dir_size (dirname_vpath, ui, cback, &dir_count, ret_marked, ret_total,
2634 compute_symlinks);
2637 /* --------------------------------------------------------------------------------------------- */
2639 * panel_operate:
2641 * Performs one of the operations on the selection on the source_panel
2642 * (copy, delete, move).
2644 * Returns TRUE if did change the directory
2645 * structure, Returns FALSE if user aborted
2647 * force_single forces operation on the current entry and affects
2648 * default destination. Current filename is used as default.
2651 gboolean
2652 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2654 WPanel *panel = (WPanel *) source_panel;
2655 const gboolean single_entry = force_single || (panel->marked <= 1)
2656 || (get_current_type () == view_tree);
2658 char *source = NULL;
2659 #ifdef WITH_FULL_PATHS
2660 vfs_path_t *source_with_vpath = NULL;
2661 char *source_with_path_str = NULL;
2662 #else
2663 #define source_with_path source
2664 #endif /* !WITH_FULL_PATHS */
2665 char *dest = NULL;
2666 vfs_path_t *dest_vpath = NULL;
2667 char *temp = NULL;
2668 char *save_cwd = NULL, *save_dest = NULL;
2669 struct stat src_stat;
2670 gboolean ret_val = TRUE;
2671 int i;
2672 FileProgressStatus value;
2673 FileOpContext *ctx;
2674 FileOpTotalContext *tctx;
2675 vfs_path_t *tmp_vpath;
2676 filegui_dialog_type_t dialog_type = FILEGUI_DIALOG_ONE_ITEM;
2678 gboolean do_bg = FALSE; /* do background operation? */
2680 static gboolean i18n_flag = FALSE;
2681 if (!i18n_flag)
2683 for (i = sizeof (op_names) / sizeof (op_names[0]); i--;)
2684 op_names[i] = Q_ (op_names[i]);
2685 i18n_flag = TRUE;
2688 linklist = free_linklist (linklist);
2689 dest_dirs = free_linklist (dest_dirs);
2691 if (single_entry)
2693 vfs_path_t *source_vpath;
2695 if (force_single)
2696 source = g_strdup (selection (panel)->fname);
2697 else
2698 source = panel_get_file (panel);
2700 if (strcmp (source, "..") == 0)
2702 g_free (source);
2703 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2704 return FALSE;
2707 source_vpath = vfs_path_from_str (source);
2708 /* Update stat to get actual info */
2709 if (mc_lstat (source_vpath, &src_stat) != 0)
2711 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2712 path_trunc (source, 30), unix_error_string (errno));
2714 /* Directory was changed outside MC. Reload it forced */
2715 if (!panel->is_panelized)
2717 panel_update_flags_t flags = UP_RELOAD;
2719 /* don't update panelized panel */
2720 if (get_other_type () == view_listing && other_panel->is_panelized)
2721 flags |= UP_ONLY_CURRENT;
2723 update_panels (flags, UP_KEEPSEL);
2725 vfs_path_free (source_vpath);
2726 return FALSE;
2728 vfs_path_free (source_vpath);
2731 ctx = file_op_context_new (operation);
2733 /* Show confirmation dialog */
2734 if (operation != OP_DELETE)
2736 char *tmp_dest_dir, *dest_dir;
2737 char *format;
2739 /* Forced single operations default to the original name */
2740 if (force_single)
2741 tmp_dest_dir = g_strdup (source);
2742 else if (get_other_type () == view_listing)
2743 tmp_dest_dir = vfs_path_to_str (other_panel->cwd_vpath);
2744 else
2745 tmp_dest_dir = vfs_path_to_str (panel->cwd_vpath);
2747 * Add trailing backslash only when do non-local ops.
2748 * It saves user from occasional file renames (when destination
2749 * dir is deleted)
2751 if (!force_single && tmp_dest_dir[0] != '\0'
2752 && tmp_dest_dir[strlen (tmp_dest_dir) - 1] != PATH_SEP)
2754 /* add trailing separator */
2755 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
2756 g_free (tmp_dest_dir);
2758 else
2760 /* just copy */
2761 dest_dir = tmp_dest_dir;
2763 if (dest_dir == NULL)
2765 ret_val = FALSE;
2766 goto ret_fast;
2769 /* Generate confirmation prompt */
2770 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2772 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2773 source != NULL ? (void *) source
2774 : (void *) &panel->marked, dest_dir, &do_bg);
2776 g_free (format);
2777 g_free (dest_dir);
2779 if (dest == NULL || dest[0] == '\0')
2781 g_free (dest);
2782 ret_val = FALSE;
2783 goto ret_fast;
2785 dest_vpath = vfs_path_from_str (dest);
2787 else if (confirm_delete)
2789 char *format;
2790 char fmd_buf[BUF_MEDIUM];
2792 /* Generate confirmation prompt */
2793 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2795 if (source == NULL)
2796 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2797 else
2799 const int fmd_xlen = 64;
2800 i = fmd_xlen - str_term_width1 (format) - 4;
2801 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2804 g_free (format);
2806 if (safe_delete)
2807 query_set_sel (1);
2809 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2811 if (i != 0)
2813 ret_val = FALSE;
2814 goto ret_fast;
2818 tctx = file_op_total_context_new ();
2819 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2821 #ifdef ENABLE_BACKGROUND
2822 /* Did the user select to do a background operation? */
2823 if (do_bg)
2825 int v;
2826 char *cwd_str;
2828 cwd_str = vfs_path_to_str (panel->cwd_vpath);
2829 v = do_background (ctx, g_strconcat (op_names[operation], ": ", cwd_str, (char *) NULL));
2830 g_free (cwd_str);
2831 if (v == -1)
2832 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2834 /* If we are the parent */
2835 if (v == 1)
2837 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
2839 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
2840 vfs_path_free (dest_vpath);
2841 g_free (dest);
2842 /* file_op_context_destroy (ctx); */
2843 return FALSE;
2846 else
2847 #endif /* ENABLE_BACKGROUND */
2849 if (operation == OP_DELETE)
2850 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2851 else
2853 dialog_type = ((operation != OP_COPY) || single_entry || force_single)
2854 ? FILEGUI_DIALOG_ONE_ITEM : FILEGUI_DIALOG_MULTI_ITEM;
2856 if (single_entry && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2857 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2861 /* Initialize things */
2862 /* We do not want to trash cache every time file is
2863 created/touched. However, this will make our cache contain
2864 invalid data. */
2865 if ((dest != NULL)
2866 && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
2867 save_dest = g_strdup (dest);
2869 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
2870 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
2871 save_cwd = vfs_path_to_str (panel->cwd_vpath);
2873 /* Now, let's do the job */
2875 /* This code is only called by the tree and panel code */
2876 if (single_entry)
2878 /* We now have ETA in all cases */
2880 /* One file: FIXME mc_chdir will take user out of any vfs */
2881 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2883 vfs_path_t *vpath;
2884 int chdir_retcode;
2886 vpath = vfs_path_from_str (PATH_SEP_STR);
2887 chdir_retcode = mc_chdir (vpath);
2888 vfs_path_free (vpath);
2889 if (chdir_retcode < 0)
2891 ret_val = FALSE;
2892 goto clean_up;
2896 /* The source and src_stat variables have been initialized before */
2897 #ifdef WITH_FULL_PATHS
2898 if (g_path_is_absolute (source))
2899 source_with_vpath = vfs_path_from_str (source);
2900 else
2901 source_with_vpath = vfs_path_append_new (panel->cwd_vpath, source, (char *) NULL);
2902 source_with_path_str = vfs_path_to_str (source_with_vpath);
2903 #endif /* WITH_FULL_PATHS */
2904 if (panel_operate_init_totals (operation, panel, source_with_path_str, ctx, dialog_type) ==
2905 FILE_CONT)
2907 if (operation == OP_DELETE)
2909 if (S_ISDIR (src_stat.st_mode))
2910 value = erase_dir (tctx, ctx, source_with_vpath);
2911 else
2912 value = erase_file (tctx, ctx, source_with_vpath);
2914 else
2916 temp = transform_source (ctx, source_with_path_str);
2917 if (temp == NULL)
2918 value = transform_error;
2919 else
2921 char *repl_dest, *temp2;
2923 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2924 if (ctx->search_handle->error != MC_SEARCH_E_OK)
2926 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
2927 g_free (repl_dest);
2928 goto clean_up;
2931 temp2 = mc_build_filename (repl_dest, temp, NULL);
2932 g_free (temp);
2933 g_free (repl_dest);
2934 g_free (dest);
2935 vfs_path_free (dest_vpath);
2936 dest = temp2;
2937 dest_vpath = vfs_path_from_str (dest);
2939 switch (operation)
2941 case OP_COPY:
2942 /* we use file_mask_op_follow_links only with OP_COPY */
2943 ctx->stat_func (source_with_vpath, &src_stat);
2945 if (S_ISDIR (src_stat.st_mode))
2946 value = copy_dir_dir (tctx, ctx, source_with_path_str, dest,
2947 TRUE, FALSE, FALSE, NULL);
2948 else
2949 value = copy_file_file (tctx, ctx, source_with_path_str, dest);
2950 break;
2952 case OP_MOVE:
2953 if (S_ISDIR (src_stat.st_mode))
2954 value = move_dir_dir (tctx, ctx, source_with_path_str, dest);
2955 else
2956 value = move_file_file (tctx, ctx, source_with_path_str, dest);
2957 break;
2959 default:
2960 /* Unknown file operation */
2961 abort ();
2964 } /* Copy or move operation */
2966 if ((value == FILE_CONT) && !force_single)
2967 unmark_files (panel);
2970 else
2972 /* Many files */
2974 /* Check destination for copy or move operation */
2975 while (operation != OP_DELETE)
2977 int dst_result;
2978 struct stat dst_stat;
2980 dst_result = mc_stat (dest_vpath, &dst_stat);
2982 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2983 break;
2985 if (ctx->skip_all
2986 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2987 goto clean_up;
2990 if (panel_operate_init_totals (operation, panel, NULL, ctx, dialog_type) == FILE_CONT)
2992 /* Loop for every file, perform the actual copy operation */
2993 for (i = 0; i < panel->count; i++)
2995 const char *source2;
2997 if (!panel->dir.list[i].f.marked)
2998 continue; /* Skip the unmarked ones */
3000 source2 = panel->dir.list[i].fname;
3001 src_stat = panel->dir.list[i].st;
3003 #ifdef WITH_FULL_PATHS
3004 g_free (source_with_path_str);
3005 vfs_path_free (source_with_vpath);
3006 if (g_path_is_absolute (source2))
3007 source_with_vpath = vfs_path_from_str (source2);
3008 else
3009 source_with_vpath =
3010 vfs_path_append_new (panel->cwd_vpath, source2, (char *) NULL);
3011 source_with_path_str = vfs_path_to_str (source_with_vpath);
3012 #endif /* WITH_FULL_PATHS */
3014 if (operation == OP_DELETE)
3016 if (S_ISDIR (src_stat.st_mode))
3017 value = erase_dir (tctx, ctx, source_with_vpath);
3018 else
3019 value = erase_file (tctx, ctx, source_with_vpath);
3021 else
3023 temp = transform_source (ctx, source_with_path_str);
3024 if (temp == NULL)
3025 value = transform_error;
3026 else
3028 char *temp2, *temp3, *repl_dest;
3030 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
3031 if (ctx->search_handle->error != MC_SEARCH_E_OK)
3033 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
3034 g_free (repl_dest);
3035 goto clean_up;
3038 temp2 = mc_build_filename (repl_dest, temp, NULL);
3039 g_free (temp);
3040 g_free (repl_dest);
3041 temp3 = source_with_path_str;
3042 source_with_path_str = strutils_shell_unescape (source_with_path_str);
3043 g_free (temp3);
3044 temp3 = temp2;
3045 temp2 = strutils_shell_unescape (temp2);
3046 g_free (temp3);
3048 switch (operation)
3050 case OP_COPY:
3051 /* we use file_mask_op_follow_links only with OP_COPY */
3053 vfs_path_t *vpath;
3055 vpath = vfs_path_from_str (source_with_path_str);
3056 ctx->stat_func (vpath, &src_stat);
3057 vfs_path_free (vpath);
3059 if (S_ISDIR (src_stat.st_mode))
3060 value = copy_dir_dir (tctx, ctx, source_with_path_str, temp2,
3061 TRUE, FALSE, FALSE, NULL);
3062 else
3063 value = copy_file_file (tctx, ctx, source_with_path_str, temp2);
3064 dest_dirs = free_linklist (dest_dirs);
3065 break;
3067 case OP_MOVE:
3068 if (S_ISDIR (src_stat.st_mode))
3069 value = move_dir_dir (tctx, ctx, source_with_path_str, temp2);
3070 else
3071 value = move_file_file (tctx, ctx, source_with_path_str, temp2);
3072 break;
3074 default:
3075 /* Unknown file operation */
3076 abort ();
3079 g_free (temp2);
3081 } /* Copy or move operation */
3083 if (value == FILE_ABORT)
3084 break;
3086 if (value == FILE_CONT)
3087 do_file_mark (panel, i, 0);
3089 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3091 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3092 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3095 if (operation != OP_DELETE)
3096 file_progress_show (ctx, 0, 0, "", FALSE);
3098 if (check_progress_buttons (ctx) == FILE_ABORT)
3099 break;
3101 mc_refresh ();
3102 } /* Loop for every file */
3104 } /* Many entries */
3106 clean_up:
3107 /* Clean up */
3108 if (save_cwd != NULL)
3110 tmp_vpath = vfs_path_from_str (save_cwd);
3111 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3112 vfs_path_free (tmp_vpath);
3113 g_free (save_cwd);
3116 if (save_dest != NULL)
3118 tmp_vpath = vfs_path_from_str (save_dest);
3119 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3120 vfs_path_free (tmp_vpath);
3121 g_free (save_dest);
3124 linklist = free_linklist (linklist);
3125 dest_dirs = free_linklist (dest_dirs);
3126 #ifdef WITH_FULL_PATHS
3127 g_free (source_with_path_str);
3128 vfs_path_free (source_with_vpath);
3129 #endif /* WITH_FULL_PATHS */
3130 g_free (dest);
3131 vfs_path_free (dest_vpath);
3132 g_free (ctx->dest_mask);
3133 ctx->dest_mask = NULL;
3135 #ifdef ENABLE_BACKGROUND
3136 /* Let our parent know we are saying bye bye */
3137 if (mc_global.we_are_background)
3139 int cur_pid = getpid ();
3140 /* Send pid to parent with child context, it is fork and
3141 don't modify real parent ctx */
3142 ctx->pid = cur_pid;
3143 parent_call ((void *) end_bg_process, ctx, 0);
3145 vfs_shut ();
3146 my_exit (EXIT_SUCCESS);
3148 #endif /* ENABLE_BACKGROUND */
3150 file_op_total_context_destroy (tctx);
3151 ret_fast:
3152 file_op_context_destroy (ctx);
3153 g_free (source);
3155 return ret_val;
3158 /* }}} */
3160 /* --------------------------------------------------------------------------------------------- */
3161 /* {{{ Query/status report routines */
3162 /** Report error with one file */
3163 FileProgressStatus
3164 file_error (const char *format, const char *file)
3166 char buf[BUF_MEDIUM];
3168 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3170 return do_file_error (buf);
3173 /* --------------------------------------------------------------------------------------------- */
3176 Cause emacs to enter folding mode for this file:
3177 Local variables:
3178 end: