Tweak background operations support.
[midnight-commander.git] / src / filemanager / file.c
blob8663268ef1321c157c51536d553766bd76d37e87
1 /*
2 File management.
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2011
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
16 The copy code was based in GNU's cp, and was written by:
17 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
19 The move code was based in GNU's mv, and was written by:
20 Mike Parker and David MacKenzie.
22 Janne Kukonlehto added much error recovery to them for being used
23 in an interactive program.
25 This file is part of the Midnight Commander.
27 The Midnight Commander is free software: you can redistribute it
28 and/or modify it under the terms of the GNU General Public License as
29 published by the Free Software Foundation, either version 3 of the License,
30 or (at your option) any later version.
32 The Midnight Commander is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program. If not, see <http://www.gnu.org/licenses/>.
42 * Please note that all dialogs used here must be safe for background
43 * operations.
46 /** \file file.c
47 * \brief Source: file management
50 /* {{{ Include files */
52 #include <config.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
62 #include <fcntl.h>
64 #include "lib/global.h"
65 #include "lib/tty/tty.h"
66 #include "lib/tty/key.h"
67 #include "lib/search.h"
68 #include "lib/strescape.h"
69 #include "lib/strutil.h"
70 #include "lib/util.h"
71 #include "lib/vfs/vfs.h"
72 #include "lib/widget.h"
74 #include "src/setup.h"
75 #ifdef ENABLE_BACKGROUND
76 #include "src/background.h" /* do_background() */
77 #endif
79 #include "layout.h" /* rotate_dash() */
81 /* Needed for current_panel, other_panel and WTree */
82 #include "dir.h"
83 #include "filegui.h"
84 #include "filenot.h"
85 #include "tree.h"
86 #include "midnight.h" /* current_panel */
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 char *source)
201 char *s, *q;
202 char *fnsource;
204 s = g_strdup (source);
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))
215 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
216 else
218 q = NULL;
219 transform_error = FILE_SKIP;
222 g_free (s);
223 return q;
226 /* --------------------------------------------------------------------------------------------- */
228 static void
229 free_link (void *data)
231 struct link *lp = (struct link *) data;
233 vfs_path_free (lp->src_vpath);
234 vfs_path_free (lp->dst_vpath);
235 g_free (lp);
238 /* --------------------------------------------------------------------------------------------- */
240 static void *
241 free_linklist (GSList *lp)
243 g_slist_foreach (lp, (GFunc) free_link, NULL);
244 g_slist_free (lp);
246 return NULL;
249 /* --------------------------------------------------------------------------------------------- */
251 static gboolean
252 is_in_linklist (const GSList *lp, const vfs_path_t * vpath, const struct stat *sb)
254 const struct vfs_class *class;
255 ino_t ino = sb->st_ino;
256 dev_t dev = sb->st_dev;
258 class = vfs_path_get_last_path_vfs (vpath);
260 for (; lp != NULL; lp = g_slist_next (lp))
262 const struct link *lnk = (const struct link *) lp->data;
264 if (lnk->vfs == class && lnk->ino == ino && lnk->dev == dev)
265 return TRUE;
267 return FALSE;
270 /* --------------------------------------------------------------------------------------------- */
272 * Check and made hardlink
274 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
275 * and a hardlink was succesfully made
278 static gboolean
279 check_hardlinks (const vfs_path_t * src_vpath, const vfs_path_t * dst_vpath, struct stat *pstat)
281 GSList *lp;
282 struct link *lnk;
284 const struct vfs_class *my_vfs;
285 ino_t ino = pstat->st_ino;
286 dev_t dev = pstat->st_dev;
287 struct stat link_stat;
289 if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
290 return FALSE;
292 my_vfs = vfs_path_get_by_index (src_vpath, -1)->class;
294 for (lp = linklist; lp != NULL; lp = g_slist_next (lp))
296 lnk = (struct link *) lp->data;
298 if (lnk->vfs == my_vfs && lnk->ino == ino && lnk->dev == dev)
300 const struct vfs_class *lp_name_class;
301 int stat_result;
303 lp_name_class = vfs_path_get_last_path_vfs (lnk->src_vpath);
304 stat_result = mc_stat (lnk->src_vpath, &link_stat);
306 if (stat_result == 0 && link_stat.st_ino == ino
307 && link_stat.st_dev == dev && lp_name_class == my_vfs)
309 const struct vfs_class *p_class, *dst_name_class;
311 dst_name_class = vfs_path_get_last_path_vfs (dst_vpath);
312 p_class = vfs_path_get_last_path_vfs (lnk->dst_vpath);
314 if (dst_name_class == p_class &&
315 mc_stat (lnk->dst_vpath, &link_stat) == 0 &&
316 mc_link (lnk->dst_vpath, dst_vpath) == 0)
317 return TRUE;
320 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
321 return FALSE;
325 lnk = g_new0 (struct link, 1);
326 if (lnk != NULL)
328 lnk->vfs = my_vfs;
329 lnk->ino = ino;
330 lnk->dev = dev;
331 lnk->src_vpath = vfs_path_clone (src_vpath);
332 lnk->dst_vpath = vfs_path_clone (dst_vpath);
333 linklist = g_slist_prepend (linklist, lnk);
336 return FALSE;
339 /* --------------------------------------------------------------------------------------------- */
341 * Duplicate the contents of the symbolic link src_path in dst_path.
342 * Try to make a stable symlink if the option "stable symlink" was
343 * set in the file mask dialog.
344 * If dst_path is an existing symlink it will be deleted silently
345 * (upper levels take already care of existing files at dst_path).
348 static FileProgressStatus
349 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
351 char link_target[MC_MAXPATHLEN];
352 int len;
353 FileProgressStatus return_status;
354 struct stat sb;
355 vfs_path_t *src_vpath;
356 vfs_path_t *dst_vpath;
357 gboolean dst_is_symlink;
358 vfs_path_t *link_target_vpath = NULL;
360 src_vpath = vfs_path_from_str (src_path);
361 dst_vpath = vfs_path_from_str (dst_path);
362 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
364 retry_src_readlink:
365 len = mc_readlink (src_vpath, link_target, MC_MAXPATHLEN - 1);
366 if (len < 0)
368 if (ctx->skip_all)
369 return_status = FILE_SKIPALL;
370 else
372 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
373 if (return_status == FILE_SKIPALL)
374 ctx->skip_all = TRUE;
375 if (return_status == FILE_RETRY)
376 goto retry_src_readlink;
378 goto ret;
380 link_target[len] = 0;
382 if (ctx->stable_symlinks)
385 if (!vfs_file_is_local (src_vpath) || !vfs_file_is_local (dst_vpath))
387 message (D_ERROR, MSG_ERROR,
388 _("Cannot make stable symlinks across"
389 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
390 ctx->stable_symlinks = FALSE;
394 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
396 char *p, *s;
397 vfs_path_t *q;
399 const char *r = strrchr (src_path, PATH_SEP);
401 if (r)
403 p = g_strndup (src_path, r - src_path + 1);
404 if (g_path_is_absolute (dst_path))
405 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
406 else
407 q = vfs_path_build_filename (p, dst_path, (char *) NULL);
409 if (vfs_path_tokens_count (q) > 1)
411 vfs_path_t *tmp_vpath1, *tmp_vpath2;
413 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
414 s = g_strconcat (p, link_target, (char *) NULL);
415 g_free (p);
416 g_strlcpy (link_target, s, sizeof (link_target));
417 g_free (s);
418 tmp_vpath2 = vfs_path_from_str (link_target);
419 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
420 vfs_path_free (tmp_vpath1);
421 vfs_path_free (tmp_vpath2);
422 if (s)
424 g_strlcpy (link_target, s, sizeof (link_target));
425 g_free (s);
428 else
429 g_free (p);
430 vfs_path_free (q);
433 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
435 retry_dst_symlink:
436 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
438 /* Success */
439 return_status = FILE_CONT;
440 goto ret;
443 * if dst_exists, it is obvious that this had failed.
444 * We can delete the old symlink and try again...
446 if (dst_is_symlink)
448 if (mc_unlink (dst_vpath) == 0)
449 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
451 /* Success */
452 return_status = FILE_CONT;
453 goto ret;
456 if (ctx->skip_all)
457 return_status = FILE_SKIPALL;
458 else
460 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
461 if (return_status == FILE_SKIPALL)
462 ctx->skip_all = TRUE;
463 if (return_status == FILE_RETRY)
464 goto retry_dst_symlink;
467 ret:
468 vfs_path_free (src_vpath);
469 vfs_path_free (dst_vpath);
470 vfs_path_free (link_target_vpath);
471 return return_status;
474 /* --------------------------------------------------------------------------------------------- */
476 static FileProgressStatus
477 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add)
479 struct timeval tv_current;
480 static struct timeval tv_start = { };
482 tctx->progress_count++;
483 tctx->progress_bytes += (uintmax_t) add;
485 if (tv_start.tv_sec == 0)
487 gettimeofday (&tv_start, (struct timezone *) NULL);
489 gettimeofday (&tv_current, (struct timezone *) NULL);
490 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
492 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
494 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
495 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
497 tv_start.tv_sec = tv_current.tv_sec;
500 return check_progress_buttons (ctx);
503 /* --------------------------------------------------------------------------------------------- */
505 static FileProgressStatus
506 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
508 char *msg;
509 int result = 0;
510 const char *head_msg;
512 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
514 msg = g_strdup_printf (fmt, a, b);
515 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
516 g_free (msg);
517 do_refresh ();
519 return (result == 1) ? FILE_ABORT : FILE_SKIP;
522 /* --------------------------------------------------------------------------------------------- */
524 static FileProgressStatus
525 warn_same_file (const char *fmt, const char *a, const char *b)
527 #ifdef ENABLE_BACKGROUND
528 /* *INDENT-OFF* */
529 union
531 void *p;
532 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
533 const char *a, const char *b);
534 } pntr;
535 /* *INDENT-ON* */
537 pntr.f = real_warn_same_file;
539 if (mc_global.we_are_background)
540 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
541 #endif
542 return real_warn_same_file (Foreground, fmt, a, b);
545 /* --------------------------------------------------------------------------------------------- */
546 /* {{{ Query/status report routines */
548 static FileProgressStatus
549 real_do_file_error (enum OperationMode mode, const char *error)
551 int result;
552 const char *msg;
554 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
555 result =
556 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
558 switch (result)
560 case 0:
561 do_refresh ();
562 return FILE_SKIP;
564 case 1:
565 do_refresh ();
566 return FILE_SKIPALL;
568 case 2:
569 do_refresh ();
570 return FILE_RETRY;
572 case 3:
573 default:
574 return FILE_ABORT;
578 /* --------------------------------------------------------------------------------------------- */
580 static FileProgressStatus
581 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
583 gchar *text;
585 if (ctx->recursive_result < RECURSIVE_ALWAYS)
587 const char *msg = mode == Foreground
588 ? _("\nDirectory not empty.\nDelete it recursively?")
589 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
590 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
592 if (safe_delete)
593 query_set_sel (1);
595 ctx->recursive_result =
596 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
597 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
599 if (ctx->recursive_result != RECURSIVE_ABORT)
600 do_refresh ();
601 g_free (text);
604 switch (ctx->recursive_result)
606 case RECURSIVE_YES:
607 case RECURSIVE_ALWAYS:
608 return FILE_CONT;
610 case RECURSIVE_NO:
611 case RECURSIVE_NEVER:
612 return FILE_SKIP;
614 case RECURSIVE_ABORT:
615 default:
616 return FILE_ABORT;
620 /* --------------------------------------------------------------------------------------------- */
622 #ifdef ENABLE_BACKGROUND
623 static FileProgressStatus
624 do_file_error (const char *str)
626 union
628 void *p;
629 FileProgressStatus (*f) (enum OperationMode, const char *);
630 } pntr;
631 pntr.f = real_do_file_error;
633 if (mc_global.we_are_background)
634 return parent_call (pntr.p, NULL, 1, strlen (str), str);
635 else
636 return real_do_file_error (Foreground, str);
639 /* --------------------------------------------------------------------------------------------- */
641 static FileProgressStatus
642 query_recursive (FileOpContext * ctx, const char *s)
644 union
646 void *p;
647 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
648 } pntr;
649 pntr.f = real_query_recursive;
651 if (mc_global.we_are_background)
652 return parent_call (pntr.p, ctx, 1, strlen (s), s);
653 else
654 return real_query_recursive (ctx, Foreground, s);
657 /* --------------------------------------------------------------------------------------------- */
659 static FileProgressStatus
660 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
661 struct stat *_d_stat)
663 union
665 void *p;
666 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
667 struct stat *, struct stat *);
668 } pntr;
669 pntr.f = file_progress_real_query_replace;
671 if (mc_global.we_are_background)
672 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
673 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
674 else
675 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
678 #else
679 /* --------------------------------------------------------------------------------------------- */
681 static FileProgressStatus
682 do_file_error (const char *str)
684 return real_do_file_error (Foreground, str);
687 /* --------------------------------------------------------------------------------------------- */
689 static FileProgressStatus
690 query_recursive (FileOpContext * ctx, const char *s)
692 return real_query_recursive (ctx, Foreground, s);
695 /* --------------------------------------------------------------------------------------------- */
697 static FileProgressStatus
698 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
699 struct stat *_d_stat)
701 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
704 #endif /* !ENABLE_BACKGROUND */
706 /* --------------------------------------------------------------------------------------------- */
707 /** Report error with two files */
709 static FileProgressStatus
710 files_error (const char *format, const char *file1, const char *file2)
712 char buf[BUF_MEDIUM];
713 char *nfile1 = g_strdup (path_trunc (file1, 15));
714 char *nfile2 = g_strdup (path_trunc (file2, 15));
716 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
718 g_free (nfile1);
719 g_free (nfile2);
721 return do_file_error (buf);
724 /* }}} */
726 /* --------------------------------------------------------------------------------------------- */
728 static void
729 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
730 struct timeval tv_current, struct timeval tv_transfer_start,
731 off_t file_size, off_t n_read_total)
733 long dt;
735 /* 1. Update rotating dash after some time */
736 rotate_dash ();
738 /* 3. Compute ETA */
739 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
741 if (n_read_total == 0)
742 ctx->eta_secs = 0.0;
743 else
745 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
746 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
749 /* 4. Compute BPS rate */
750 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
751 if (ctx->bps_time < 1)
752 ctx->bps_time = 1;
753 ctx->bps = n_read_total / ctx->bps_time;
755 /* 5. Compute total ETA and BPS */
756 if (ctx->progress_bytes != 0)
758 uintmax_t remain_bytes;
760 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
761 #if 1
763 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
765 if (total_secs < 1)
766 total_secs = 1;
768 tctx->bps = tctx->copied_bytes / total_secs;
769 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
771 #else
772 /* broken on lot of little files */
773 tctx->bps_count++;
774 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
775 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
776 #endif
780 /* --------------------------------------------------------------------------------------------- */
782 /* {{{ Move routines */
783 static FileProgressStatus
784 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
786 struct stat src_stats, dst_stats;
787 FileProgressStatus return_status = FILE_CONT;
788 gboolean copy_done = FALSE;
789 gboolean old_ask_overwrite;
790 vfs_path_t *src_vpath, *dst_vpath;
792 src_vpath = vfs_path_from_str (s);
793 dst_vpath = vfs_path_from_str (d);
795 file_progress_show_source (ctx, src_vpath);
796 file_progress_show_target (ctx, dst_vpath);
798 if (check_progress_buttons (ctx) == FILE_ABORT)
800 return_status = FILE_ABORT;
801 goto ret;
804 mc_refresh ();
806 while (mc_lstat (src_vpath, &src_stats) != 0)
808 /* Source doesn't exist */
809 if (ctx->skip_all)
810 return_status = FILE_SKIPALL;
811 else
813 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
814 if (return_status == FILE_SKIPALL)
815 ctx->skip_all = TRUE;
818 if (return_status != FILE_RETRY)
819 goto ret;
822 if (mc_lstat (dst_vpath, &dst_stats) == 0)
824 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
826 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
827 goto ret;
830 if (S_ISDIR (dst_stats.st_mode))
832 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
833 do_refresh ();
834 return_status = FILE_SKIP;
835 goto ret;
838 if (confirm_overwrite)
840 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
841 if (return_status != FILE_CONT)
842 goto ret;
844 /* Ok to overwrite */
847 if (!ctx->do_append)
849 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
851 return_status = make_symlink (ctx, s, d);
852 if (return_status == FILE_CONT)
853 goto retry_src_remove;
854 goto ret;
857 if (mc_rename (src_vpath, dst_vpath) == 0)
859 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
860 goto ret;
863 #if 0
864 /* Comparison to EXDEV seems not to work in nfs if you're moving from
865 one nfs to the same, but on the server it is on two different
866 filesystems. Then nfs returns EIO instead of EXDEV.
867 Hope it will not hurt if we always in case of error try to copy/delete. */
868 else
869 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
871 if (errno != EXDEV)
873 if (ctx->skip_all)
874 return_status = FILE_SKIPALL;
875 else
877 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
878 if (return_status == FILE_SKIPALL)
879 ctx->skip_all = TRUE;
880 if (return_status == FILE_RETRY)
881 goto retry_rename;
884 goto ret;
886 #endif
888 /* Failed because filesystem boundary -> copy the file instead */
889 old_ask_overwrite = tctx->ask_overwrite;
890 tctx->ask_overwrite = FALSE;
891 return_status = copy_file_file (tctx, ctx, s, d);
892 tctx->ask_overwrite = old_ask_overwrite;
893 if (return_status != FILE_CONT)
894 goto ret;
896 copy_done = TRUE;
898 file_progress_show_source (ctx, NULL);
899 file_progress_show (ctx, 0, 0, "", FALSE);
901 return_status = check_progress_buttons (ctx);
902 if (return_status != FILE_CONT)
903 goto ret;
904 mc_refresh ();
906 retry_src_remove:
907 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
909 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
910 if (return_status == FILE_RETRY)
911 goto retry_src_remove;
912 if (return_status == FILE_SKIPALL)
913 ctx->skip_all = TRUE;
914 goto ret;
917 if (!copy_done)
918 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
920 ret:
921 vfs_path_free (src_vpath);
922 vfs_path_free (dst_vpath);
924 return return_status;
927 /* }}} */
929 /* --------------------------------------------------------------------------------------------- */
930 /* {{{ Erase routines */
931 /** Don't update progress status if progress_count==NULL */
933 static FileProgressStatus
934 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * vpath)
936 int return_status;
937 struct stat buf;
938 char *s;
940 s = vfs_path_to_str (vpath);
941 file_progress_show_deleting (ctx, s);
942 if (check_progress_buttons (ctx) == FILE_ABORT)
944 g_free (s);
945 return FILE_ABORT;
947 mc_refresh ();
949 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
951 /* ignore, most likely the mc_unlink fails, too */
952 buf.st_size = 0;
955 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
957 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
958 if (return_status == FILE_ABORT)
960 g_free (s);
961 return return_status;
963 if (return_status == FILE_RETRY)
964 continue;
965 if (return_status == FILE_SKIPALL)
966 ctx->skip_all = TRUE;
967 break;
969 g_free (s);
970 if (tctx->progress_count == 0)
971 return FILE_CONT;
972 return progress_update_one (tctx, ctx, buf.st_size);
975 /* --------------------------------------------------------------------------------------------- */
978 Recursive remove of files
979 abort->cancel stack
980 skip ->warn every level, gets default
981 skipall->remove as much as possible
983 static FileProgressStatus
984 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
986 struct dirent *next;
987 struct stat buf;
988 DIR *reading;
989 char *path;
990 FileProgressStatus return_status = FILE_CONT;
991 vfs_path_t *vpath;
993 if (strcmp (s, "..") == 0)
994 return FILE_RETRY;
996 vpath = vfs_path_from_str (s);
997 reading = mc_opendir (vpath);
999 if (reading == NULL)
1001 return_status = FILE_RETRY;
1002 goto ret;
1005 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1007 vfs_path_t *tmp_vpath;
1009 if (!strcmp (next->d_name, "."))
1010 continue;
1011 if (!strcmp (next->d_name, ".."))
1012 continue;
1013 path = mc_build_filename (s, next->d_name, NULL);
1014 tmp_vpath = vfs_path_from_str (path);
1015 if (mc_lstat (tmp_vpath, &buf) != 0)
1017 g_free (path);
1018 mc_closedir (reading);
1019 vfs_path_free (tmp_vpath);
1020 return_status = FILE_RETRY;
1021 goto ret;
1023 if (S_ISDIR (buf.st_mode))
1024 return_status = recursive_erase (tctx, ctx, path);
1025 else
1026 return_status = erase_file (tctx, ctx, tmp_vpath);
1027 vfs_path_free (tmp_vpath);
1028 g_free (path);
1030 mc_closedir (reading);
1031 if (return_status == FILE_ABORT)
1032 goto ret;
1034 file_progress_show_deleting (ctx, s);
1035 if (check_progress_buttons (ctx) == FILE_ABORT)
1037 return_status = FILE_ABORT;
1038 goto ret;
1040 mc_refresh ();
1042 while (my_rmdir (s) != 0 && !ctx->skip_all)
1044 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1045 if (return_status == FILE_RETRY)
1046 continue;
1047 if (return_status == FILE_ABORT)
1048 goto ret;
1049 if (return_status == FILE_SKIPALL)
1050 ctx->skip_all = TRUE;
1051 break;
1054 ret:
1055 vfs_path_free (vpath);
1056 return return_status;
1059 /* --------------------------------------------------------------------------------------------- */
1060 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1061 in the directory path points to, 0 else. */
1063 static int
1064 check_dir_is_empty (const vfs_path_t * vpath)
1066 DIR *dir;
1067 struct dirent *d;
1068 int i;
1070 dir = mc_opendir (vpath);
1071 if (!dir)
1072 return -1;
1074 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1076 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1077 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1078 continue; /* "." or ".." */
1079 i = 0;
1080 break;
1083 mc_closedir (dir);
1084 return i;
1087 /* --------------------------------------------------------------------------------------------- */
1089 static FileProgressStatus
1090 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1092 FileProgressStatus error;
1093 vfs_path_t *s_vpath;
1095 if (strcmp (s, "..") == 0)
1096 return FILE_SKIP;
1098 if (strcmp (s, ".") == 0)
1099 return FILE_SKIP;
1101 file_progress_show_deleting (ctx, s);
1102 if (check_progress_buttons (ctx) == FILE_ABORT)
1103 return FILE_ABORT;
1105 mc_refresh ();
1107 s_vpath = vfs_path_from_str (s);
1109 if (check_dir_is_empty (s_vpath) == 1) /* not empty or error */
1111 while (my_rmdir (s) != 0 && !ctx->skip_all)
1113 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1114 if (error == FILE_SKIPALL)
1115 ctx->skip_all = TRUE;
1116 if (error != FILE_RETRY)
1118 vfs_path_free (s_vpath);
1119 return error;
1124 vfs_path_free (s_vpath);
1125 return FILE_CONT;
1128 /* }}} */
1130 /* --------------------------------------------------------------------------------------------- */
1131 /* {{{ Panel operate routines */
1134 * Return currently selected entry name or the name of the first marked
1135 * entry if there is one.
1138 static char *
1139 panel_get_file (WPanel * panel)
1141 if (get_current_type () == view_tree)
1143 WTree *tree;
1145 tree = (WTree *) get_panel_widget (get_current_index ());
1146 return vfs_path_to_str (tree_selected_name (tree));
1149 if (panel->marked != 0)
1151 int i;
1153 for (i = 0; i < panel->count; i++)
1154 if (panel->dir.list[i].f.marked)
1155 return g_strdup (panel->dir.list[i].fname);
1157 return g_strdup (panel->dir.list[panel->selected].fname);
1160 /* --------------------------------------------------------------------------------------------- */
1162 * panel_compute_totals:
1164 * compute the number of files and the number of bytes
1165 * used up by the whole selection, recursing directories
1166 * as required. In addition, it checks to see if it will
1167 * overwrite any files by doing the copy.
1170 static FileProgressStatus
1171 panel_compute_totals (const WPanel * panel, const void *ui,
1172 compute_dir_size_callback cback,
1173 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1175 int i;
1177 *ret_marked = 0;
1178 *ret_total = 0;
1180 for (i = 0; i < panel->count; i++)
1182 struct stat *s;
1184 if (!panel->dir.list[i].f.marked)
1185 continue;
1187 s = &panel->dir.list[i].st;
1189 if (S_ISDIR (s->st_mode))
1191 vfs_path_t *p;
1192 size_t subdir_count = 0;
1193 uintmax_t subdir_bytes = 0;
1194 FileProgressStatus status;
1196 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, NULL);
1197 status =
1198 compute_dir_size (p, ui, cback, &subdir_count, &subdir_bytes, compute_symlinks);
1199 vfs_path_free (p);
1201 if (status != FILE_CONT)
1202 return FILE_ABORT;
1204 *ret_marked += subdir_count;
1205 *ret_total += subdir_bytes;
1207 else
1209 (*ret_marked)++;
1210 *ret_total += (uintmax_t) s->st_size;
1214 return FILE_CONT;
1217 /* --------------------------------------------------------------------------------------------- */
1219 /** Initialize variables for progress bars */
1220 static FileProgressStatus
1221 panel_operate_init_totals (FileOperation operation,
1222 const WPanel * panel, const char *source, FileOpContext * ctx)
1224 FileProgressStatus status;
1226 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1228 ComputeDirSizeUI *ui;
1230 ui = compute_dir_size_create_ui ();
1232 if (source == NULL)
1233 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1234 &ctx->progress_count, &ctx->progress_bytes,
1235 ctx->follow_links);
1236 else
1238 vfs_path_t *p;
1240 p = vfs_path_from_str (source);
1241 status = compute_dir_size (p, ui, compute_dir_size_update_ui,
1242 &ctx->progress_count, &ctx->progress_bytes,
1243 ctx->follow_links);
1244 vfs_path_free (p);
1247 compute_dir_size_destroy_ui (ui);
1249 ctx->progress_totals_computed = (status == FILE_CONT);
1251 else
1253 status = FILE_CONT;
1254 ctx->progress_count = panel->marked;
1255 ctx->progress_bytes = panel->total;
1256 ctx->progress_totals_computed = FALSE;
1259 return status;
1262 /* --------------------------------------------------------------------------------------------- */
1264 * Generate user prompt for panel operation.
1265 * single_source is the name if the source entry or NULL for multiple
1266 * entries.
1267 * src_stat is only used when single_source is not NULL.
1270 static char *
1271 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1272 gboolean single_source, const struct stat *src_stat)
1274 const char *sp, *cp;
1275 char format_string[BUF_MEDIUM];
1276 char *dp = format_string;
1277 gboolean build_question = FALSE;
1279 static gboolean i18n_flag = FALSE;
1280 if (!i18n_flag)
1282 size_t i;
1284 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1285 op_names1[i] = Q_ (op_names1[i]);
1287 #ifdef ENABLE_NLS
1288 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1289 prompt_parts[i] = _(prompt_parts[i]);
1291 one_format = _(one_format);
1292 many_format = _(many_format);
1293 question_format = _(question_format);
1294 #endif /* ENABLE_NLS */
1295 i18n_flag = TRUE;
1298 sp = single_source ? one_format : many_format;
1300 while (*sp != '\0')
1302 switch (*sp)
1304 case '%':
1305 cp = NULL;
1306 switch (sp[1])
1308 case 'o':
1309 cp = op_names1[operation];
1310 break;
1311 case 'm':
1312 if (operation == OP_DELETE)
1314 cp = "";
1315 build_question = TRUE;
1317 else
1318 cp = prompt_parts[5];
1319 break;
1320 case 'e':
1321 if (operation == OP_DELETE)
1323 cp = "";
1324 build_question = TRUE;
1326 else
1327 cp = prompt_parts[6];
1328 break;
1329 case 'f':
1330 if (single_source)
1331 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1332 else
1333 cp = (panel->marked == panel->dirs_marked)
1334 ? prompt_parts[3]
1335 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1336 break;
1337 default:
1338 *dp++ = *sp++;
1341 if (cp != NULL)
1343 sp += 2;
1344 while (*cp != '\0')
1345 *dp++ = *cp++;
1347 break;
1348 default:
1349 *dp++ = *sp++;
1352 *dp = '\0';
1354 if (build_question)
1356 char tmp[BUF_MEDIUM];
1358 memmove (tmp, format_string, sizeof (tmp));
1359 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1362 return g_strdup (format_string);
1365 /* --------------------------------------------------------------------------------------------- */
1367 #ifdef ENABLE_BACKGROUND
1368 static int
1369 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1371 int pid = ctx->pid;
1373 (void) mode;
1374 ctx->pid = 0;
1376 unregister_task_with_pid (pid);
1377 /* file_op_context_destroy(ctx); */
1378 return 1;
1380 #endif
1381 /* }}} */
1383 /* --------------------------------------------------------------------------------------------- */
1384 /*** public functions ****************************************************************************/
1385 /* --------------------------------------------------------------------------------------------- */
1387 FileProgressStatus
1388 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1389 const char *src_path, const char *dst_path)
1391 uid_t src_uid = (uid_t) (-1);
1392 gid_t src_gid = (gid_t) (-1);
1394 int src_desc, dest_desc = -1;
1395 int n_read, n_written;
1396 mode_t src_mode = 0; /* The mode of the source file */
1397 struct stat sb, sb2;
1398 struct utimbuf utb;
1399 gboolean dst_exists = FALSE, appending = FALSE;
1400 off_t file_size = -1;
1401 FileProgressStatus return_status, temp_status;
1402 struct timeval tv_transfer_start;
1403 dest_status_t dst_status = DEST_NONE;
1404 int open_flags;
1405 gboolean is_first_time = TRUE;
1406 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1408 /* FIXME: We should not be using global variables! */
1409 ctx->do_reget = 0;
1410 return_status = FILE_RETRY;
1412 dst_vpath = vfs_path_from_str (dst_path);
1413 src_vpath = vfs_path_from_str (src_path);
1415 file_progress_show_source (ctx, src_vpath);
1416 file_progress_show_target (ctx, dst_vpath);
1418 if (check_progress_buttons (ctx) == FILE_ABORT)
1420 return_status = FILE_ABORT;
1421 goto ret_fast;
1424 mc_refresh ();
1426 while (mc_stat (dst_vpath, &sb2) == 0)
1428 if (S_ISDIR (sb2.st_mode))
1430 if (ctx->skip_all)
1431 return_status = FILE_SKIPALL;
1432 else
1434 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1435 if (return_status == FILE_SKIPALL)
1436 ctx->skip_all = TRUE;
1437 if (return_status == FILE_RETRY)
1438 continue;
1440 goto ret_fast;
1443 dst_exists = TRUE;
1444 break;
1447 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1449 if (ctx->skip_all)
1450 return_status = FILE_SKIPALL;
1451 else
1453 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1454 if (return_status == FILE_SKIPALL)
1455 ctx->skip_all = TRUE;
1458 if (return_status != FILE_RETRY)
1459 goto ret_fast;
1462 if (dst_exists)
1464 /* Destination already exists */
1465 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1467 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1468 src_path, dst_path);
1469 goto ret_fast;
1472 /* Should we replace destination? */
1473 if (tctx->ask_overwrite)
1475 ctx->do_reget = 0;
1476 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1477 if (return_status != FILE_CONT)
1478 goto ret_fast;
1482 if (!ctx->do_append)
1484 /* Check the hardlinks */
1485 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &sb))
1487 /* We have made a hardlink - no more processing is necessary */
1488 return_status = FILE_CONT;
1489 goto ret_fast;
1492 if (S_ISLNK (sb.st_mode))
1494 return_status = make_symlink (ctx, src_path, dst_path);
1495 goto ret_fast;
1498 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1499 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1501 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1502 && !ctx->skip_all)
1504 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1505 if (return_status == FILE_RETRY)
1506 continue;
1507 if (return_status == FILE_SKIPALL)
1508 ctx->skip_all = TRUE;
1509 goto ret_fast;
1511 /* Success */
1513 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1514 && !ctx->skip_all)
1516 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1517 if (temp_status == FILE_SKIP)
1518 break;
1519 if (temp_status == FILE_SKIPALL)
1520 ctx->skip_all = TRUE;
1521 if (temp_status != FILE_RETRY)
1523 return_status = temp_status;
1524 goto ret_fast;
1528 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1529 && !ctx->skip_all)
1531 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1532 if (temp_status == FILE_SKIP)
1533 break;
1534 if (temp_status == FILE_SKIPALL)
1535 ctx->skip_all = TRUE;
1536 if (temp_status != FILE_RETRY)
1538 return_status = temp_status;
1539 goto ret_fast;
1543 return_status = FILE_CONT;
1544 goto ret_fast;
1548 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1550 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1552 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1553 if (return_status == FILE_RETRY)
1554 continue;
1555 if (return_status == FILE_SKIPALL)
1556 ctx->skip_all = TRUE;
1557 if (return_status == FILE_SKIP)
1558 break;
1559 ctx->do_append = 0;
1560 goto ret_fast;
1563 if (ctx->do_reget != 0)
1565 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1567 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1568 ctx->do_reget = 0;
1569 ctx->do_append = FALSE;
1573 while (mc_fstat (src_desc, &sb) != 0)
1575 if (ctx->skip_all)
1576 return_status = FILE_SKIPALL;
1577 else
1579 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1580 if (return_status == FILE_RETRY)
1581 continue;
1582 if (return_status == FILE_SKIPALL)
1583 ctx->skip_all = TRUE;
1584 ctx->do_append = FALSE;
1586 goto ret;
1589 src_mode = sb.st_mode;
1590 src_uid = sb.st_uid;
1591 src_gid = sb.st_gid;
1592 utb.actime = sb.st_atime;
1593 utb.modtime = sb.st_mtime;
1594 file_size = sb.st_size;
1596 open_flags = O_WRONLY;
1597 if (dst_exists)
1599 if (ctx->do_append != 0)
1600 open_flags |= O_APPEND;
1601 else
1602 open_flags |= O_CREAT | O_TRUNC;
1604 else
1606 open_flags |= O_CREAT | O_EXCL;
1609 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
1611 if (errno != EEXIST)
1613 if (ctx->skip_all)
1614 return_status = FILE_SKIPALL;
1615 else
1617 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1618 if (return_status == FILE_RETRY)
1619 continue;
1620 if (return_status == FILE_SKIPALL)
1621 ctx->skip_all = TRUE;
1622 ctx->do_append = FALSE;
1625 goto ret;
1627 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1629 appending = ctx->do_append;
1630 ctx->do_append = FALSE;
1632 /* Find out the optimal buffer size. */
1633 while (mc_fstat (dest_desc, &sb) != 0)
1635 if (ctx->skip_all)
1636 return_status = FILE_SKIPALL;
1637 else
1639 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1640 if (return_status == FILE_RETRY)
1641 continue;
1642 if (return_status == FILE_SKIPALL)
1643 ctx->skip_all = TRUE;
1645 goto ret;
1648 while (TRUE)
1650 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1651 if (errno == 0)
1652 break;
1654 if (ctx->skip_all)
1655 return_status = FILE_SKIPALL;
1656 else
1658 return_status =
1659 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1660 if (return_status == FILE_RETRY)
1661 continue;
1662 if (return_status == FILE_SKIPALL)
1663 ctx->skip_all = TRUE;
1665 mc_close (dest_desc);
1666 dest_desc = -1;
1667 mc_unlink (dst_vpath);
1668 dst_status = DEST_NONE;
1669 goto ret;
1672 ctx->eta_secs = 0.0;
1673 ctx->bps = 0;
1675 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1676 file_progress_show (ctx, 0, file_size, "", TRUE);
1677 else
1678 file_progress_show (ctx, 1, 1, "", TRUE);
1679 return_status = check_progress_buttons (ctx);
1680 mc_refresh ();
1682 if (return_status != FILE_CONT)
1683 goto ret;
1686 off_t n_read_total = 0;
1687 struct timeval tv_current, tv_last_update, tv_last_input;
1688 int secs, update_secs;
1689 const char *stalled_msg = "";
1691 tv_last_update = tv_transfer_start;
1693 while (TRUE)
1695 char buf[BUF_8K];
1697 /* src_read */
1698 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1699 n_read = -1;
1700 else
1701 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1703 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1704 if (return_status == FILE_RETRY)
1705 continue;
1706 if (return_status == FILE_SKIPALL)
1707 ctx->skip_all = TRUE;
1708 goto ret;
1710 if (n_read == 0)
1711 break;
1713 gettimeofday (&tv_current, NULL);
1715 if (n_read > 0)
1717 char *t = buf;
1718 n_read_total += n_read;
1720 /* Windows NT ftp servers report that files have no
1721 * permissions: -------, so if we happen to have actually
1722 * read something, we should fix the permissions.
1724 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1725 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1726 gettimeofday (&tv_last_input, NULL);
1728 /* dst_write */
1729 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read && !ctx->skip_all)
1731 if (n_written > 0)
1733 n_read -= n_written;
1734 t += n_written;
1735 continue;
1737 return_status = file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1738 if (return_status == FILE_SKIP)
1739 break;
1740 if (return_status == FILE_SKIPALL)
1741 ctx->skip_all = TRUE;
1742 if (return_status != FILE_RETRY)
1743 goto ret;
1747 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1749 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1750 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1752 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1754 copy_file_file_display_progress (tctx, ctx,
1755 tv_current,
1756 tv_transfer_start, file_size, n_read_total);
1757 tv_last_update = tv_current;
1759 is_first_time = FALSE;
1761 if (update_secs > FILEOP_STALLING_INTERVAL)
1763 stalled_msg = _("(stalled)");
1767 gboolean force_update;
1769 force_update =
1770 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1772 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1774 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1775 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1778 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1779 force_update);
1781 mc_refresh ();
1783 return_status = check_progress_buttons (ctx);
1785 if (return_status != FILE_CONT)
1787 mc_refresh ();
1788 goto ret;
1793 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1795 ret:
1796 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1798 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1799 if (temp_status == FILE_RETRY)
1800 continue;
1801 if (temp_status == FILE_ABORT)
1802 return_status = temp_status;
1803 if (temp_status == FILE_SKIPALL)
1804 ctx->skip_all = TRUE;
1805 break;
1808 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1810 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1811 if (temp_status == FILE_RETRY)
1812 continue;
1813 if (temp_status == FILE_SKIPALL)
1814 ctx->skip_all = TRUE;
1815 return_status = temp_status;
1816 break;
1819 if (dst_status == DEST_SHORT)
1821 /* Remove short file */
1822 int result;
1824 result = query_dialog (Q_ ("DialogTitle|Copy"),
1825 _("Incomplete file was retrieved. Keep it?"),
1826 D_ERROR, 2, _("&Delete"), _("&Keep"));
1827 if (result == 0)
1828 mc_unlink (dst_vpath);
1830 else if (dst_status == DEST_FULL)
1832 /* Copy has succeeded */
1833 if (!appending && ctx->preserve_uidgid)
1835 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1837 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1838 if (temp_status == FILE_RETRY)
1839 continue;
1840 if (temp_status == FILE_SKIPALL)
1842 ctx->skip_all = TRUE;
1843 return_status = FILE_CONT;
1845 if (temp_status == FILE_SKIP)
1846 return_status = FILE_CONT;
1847 break;
1851 if (!appending)
1853 if (ctx->preserve)
1855 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1857 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1858 if (temp_status == FILE_RETRY)
1859 continue;
1860 if (temp_status == FILE_SKIPALL)
1862 ctx->skip_all = TRUE;
1863 return_status = FILE_CONT;
1865 if (temp_status == FILE_SKIP)
1866 return_status = FILE_CONT;
1867 break;
1870 else
1872 src_mode = umask (-1);
1873 umask (src_mode);
1874 src_mode = 0100666 & ~src_mode;
1875 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
1877 mc_utime (dst_vpath, &utb);
1881 if (return_status == FILE_CONT)
1882 return_status = progress_update_one (tctx, ctx, file_size);
1884 ret_fast:
1885 vfs_path_free (src_vpath);
1886 vfs_path_free (dst_vpath);
1887 return return_status;
1890 /* --------------------------------------------------------------------------------------------- */
1892 * I think these copy_*_* functions should have a return type.
1893 * anyway, this function *must* have two directories as arguments.
1895 /* FIXME: This function needs to check the return values of the
1896 function calls */
1898 FileProgressStatus
1899 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1900 gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs)
1902 struct dirent *next;
1903 struct stat buf, cbuf;
1904 DIR *reading;
1905 char *dest_dir = NULL;
1906 FileProgressStatus return_status = FILE_CONT;
1907 struct utimbuf utb;
1908 struct link *lp;
1909 char *d;
1910 vfs_path_t *src_vpath, *dst_vpath, *dest_dir_vpath = NULL;
1912 d = g_strdup (_d);
1914 src_vpath = vfs_path_from_str (s);
1915 dst_vpath = vfs_path_from_str (_d);
1917 /* First get the mode of the source dir */
1919 retry_src_stat:
1920 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
1922 if (ctx->skip_all)
1923 return_status = FILE_SKIPALL;
1924 else
1926 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1927 if (return_status == FILE_RETRY)
1928 goto retry_src_stat;
1929 if (return_status == FILE_SKIPALL)
1930 ctx->skip_all = TRUE;
1932 goto ret_fast;
1935 if (is_in_linklist (dest_dirs, src_vpath, &cbuf))
1937 /* Don't copy a directory we created before (we don't want to copy
1938 infinitely if a directory is copied into itself) */
1939 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1940 return_status = FILE_CONT;
1941 goto ret_fast;
1944 /* Hmm, hardlink to directory??? - Norbert */
1945 /* FIXME: In this step we should do something
1946 in case the destination already exist */
1947 /* Check the hardlinks */
1948 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &cbuf))
1950 /* We have made a hardlink - no more processing is necessary */
1951 goto ret_fast;
1954 if (!S_ISDIR (cbuf.st_mode))
1956 if (ctx->skip_all)
1957 return_status = FILE_SKIPALL;
1958 else
1960 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1961 if (return_status == FILE_RETRY)
1962 goto retry_src_stat;
1963 if (return_status == FILE_SKIPALL)
1964 ctx->skip_all = TRUE;
1966 goto ret_fast;
1969 if (is_in_linklist (parent_dirs, src_vpath, &cbuf))
1971 /* we found a cyclic symbolic link */
1972 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
1973 return_status = FILE_SKIP;
1974 goto ret_fast;
1977 lp = g_new0 (struct link, 1);
1978 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
1979 lp->ino = cbuf.st_ino;
1980 lp->dev = cbuf.st_dev;
1981 parent_dirs = g_slist_prepend (parent_dirs, lp);
1983 retry_dst_stat:
1984 /* Now, check if the dest dir exists, if not, create it. */
1985 if (mc_stat (dst_vpath, &buf) != 0)
1987 /* Here the dir doesn't exist : make it ! */
1988 if (move_over)
1990 if (mc_rename (src_vpath, dst_vpath) == 0)
1992 return_status = FILE_CONT;
1993 goto ret;
1996 dest_dir = d;
1997 d = NULL;
1999 else
2002 * If the destination directory exists, we want to copy the whole
2003 * directory, but we only want this to happen once.
2005 * Escape sequences added to the * to compiler warnings.
2006 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2007 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2009 if (!S_ISDIR (buf.st_mode))
2011 if (ctx->skip_all)
2012 return_status = FILE_SKIPALL;
2013 else
2015 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2016 if (return_status == FILE_SKIPALL)
2017 ctx->skip_all = TRUE;
2018 if (return_status == FILE_RETRY)
2019 goto retry_dst_stat;
2021 goto ret;
2023 /* Dive into subdir if exists */
2024 if (toplevel && ctx->dive_into_subdirs)
2026 dest_dir = mc_build_filename (d, x_basename (s), NULL);
2028 else
2030 dest_dir = d;
2031 d = NULL;
2032 goto dont_mkdir;
2035 dest_dir_vpath = vfs_path_from_str (dest_dir);
2036 while (my_mkdir (dest_dir_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2038 if (ctx->skip_all)
2039 return_status = FILE_SKIPALL;
2040 else
2042 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
2043 if (return_status == FILE_SKIPALL)
2044 ctx->skip_all = TRUE;
2046 if (return_status != FILE_RETRY)
2047 goto ret;
2050 lp = g_new0 (struct link, 1);
2051 mc_stat (dest_dir_vpath, &buf);
2052 lp->vfs = vfs_path_get_by_index (dest_dir_vpath, -1)->class;
2053 lp->ino = buf.st_ino;
2054 lp->dev = buf.st_dev;
2055 dest_dirs = g_slist_prepend (dest_dirs, lp);
2057 if (ctx->preserve_uidgid)
2059 while (mc_chown (dest_dir_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2061 if (ctx->skip_all)
2062 return_status = FILE_SKIPALL;
2063 else
2065 return_status =
2066 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
2067 if (return_status == FILE_SKIPALL)
2068 ctx->skip_all = TRUE;
2070 if (return_status != FILE_RETRY)
2071 goto ret;
2075 dont_mkdir:
2076 /* open the source dir for reading */
2077 reading = mc_opendir (src_vpath);
2078 if (reading == NULL)
2079 goto ret;
2081 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2083 char *path;
2084 vfs_path_t *tmp_vpath;
2086 * Now, we don't want '.' and '..' to be created / copied at any time
2088 if (!strcmp (next->d_name, "."))
2089 continue;
2090 if (!strcmp (next->d_name, ".."))
2091 continue;
2093 /* get the filename and add it to the src directory */
2094 path = mc_build_filename (s, next->d_name, NULL);
2095 tmp_vpath = vfs_path_from_str (path);
2097 (*ctx->stat_func) (tmp_vpath, &buf);
2098 if (S_ISDIR (buf.st_mode))
2100 char *mdpath;
2102 mdpath = mc_build_filename (dest_dir, next->d_name, NULL);
2104 * From here, we just intend to recursively copy subdirs, not
2105 * the double functionality of copying different when the target
2106 * dir already exists. So, we give the recursive call the flag 0
2107 * meaning no toplevel.
2109 return_status =
2110 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2111 g_free (mdpath);
2113 else
2115 char *dest_file;
2117 dest_file = mc_build_filename (dest_dir, x_basename (path), NULL);
2118 return_status = copy_file_file (tctx, ctx, path, dest_file);
2119 g_free (dest_file);
2121 if (do_delete && return_status == FILE_CONT)
2123 if (ctx->erase_at_end)
2125 lp = g_new0 (struct link, 1);
2126 lp->src_vpath = vfs_path_clone (tmp_vpath);
2127 lp->st_mode = buf.st_mode;
2128 erase_list = g_slist_append (erase_list, lp);
2130 else if (S_ISDIR (buf.st_mode))
2131 return_status = erase_dir_iff_empty (ctx, path);
2132 else
2133 return_status = erase_file (tctx, ctx, tmp_vpath);
2135 g_free (path);
2136 vfs_path_free (tmp_vpath);
2138 mc_closedir (reading);
2140 if (ctx->preserve)
2142 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2143 utb.actime = cbuf.st_atime;
2144 utb.modtime = cbuf.st_mtime;
2145 mc_utime (dest_dir_vpath, &utb);
2147 else
2149 cbuf.st_mode = umask (-1);
2150 umask (cbuf.st_mode);
2151 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2152 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2155 ret:
2156 g_free (dest_dir);
2157 vfs_path_free (dest_dir_vpath);
2158 free_link (parent_dirs->data);
2159 g_slist_free_1 (parent_dirs);
2160 ret_fast:
2161 g_free (d);
2162 vfs_path_free (src_vpath);
2163 vfs_path_free (dst_vpath);
2164 return return_status;
2167 /* }}} */
2169 /* --------------------------------------------------------------------------------------------- */
2170 /* {{{ Move routines */
2172 FileProgressStatus
2173 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2175 struct stat sbuf, dbuf, destbuf;
2176 struct link *lp;
2177 char *destdir;
2178 FileProgressStatus return_status;
2179 gboolean move_over = FALSE;
2180 gboolean dstat_ok;
2181 vfs_path_t *src_vpath, *dst_vpath, *destdir_vpath;
2183 src_vpath = vfs_path_from_str (s);
2184 dst_vpath = vfs_path_from_str (d);
2186 file_progress_show_source (ctx, src_vpath);
2187 file_progress_show_target (ctx, dst_vpath);
2189 if (check_progress_buttons (ctx) == FILE_ABORT)
2191 return_status = FILE_ABORT;
2192 goto ret_fast;
2195 mc_refresh ();
2197 mc_stat (src_vpath, &sbuf);
2199 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2200 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2202 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2203 goto ret_fast;
2206 if (!dstat_ok)
2207 destdir = g_strdup (d); /* destination doesn't exist */
2208 else if (!ctx->dive_into_subdirs)
2210 destdir = g_strdup (d);
2211 move_over = TRUE;
2213 else
2214 destdir = mc_build_filename (d, x_basename (s), NULL);
2216 destdir_vpath = vfs_path_from_str (destdir);
2218 /* Check if the user inputted an existing dir */
2219 retry_dst_stat:
2220 if (mc_stat (destdir_vpath, &destbuf) == 0)
2222 if (move_over)
2224 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2226 if (return_status != FILE_CONT)
2227 goto ret;
2228 goto oktoret;
2230 else if (ctx->skip_all)
2231 return_status = FILE_SKIPALL;
2232 else
2234 if (S_ISDIR (destbuf.st_mode))
2235 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2236 else
2237 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2238 if (return_status == FILE_SKIPALL)
2239 ctx->skip_all = TRUE;
2240 if (return_status == FILE_RETRY)
2241 goto retry_dst_stat;
2244 g_free (destdir);
2245 vfs_path_free (destdir_vpath);
2246 goto ret_fast;
2249 retry_rename:
2250 if (mc_rename (src_vpath, destdir_vpath) == 0)
2252 return_status = FILE_CONT;
2253 goto ret;
2256 if (errno != EXDEV)
2258 if (!ctx->skip_all)
2260 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2261 if (return_status == FILE_SKIPALL)
2262 ctx->skip_all = TRUE;
2263 if (return_status == FILE_RETRY)
2264 goto retry_rename;
2266 goto ret;
2268 /* Failed because of filesystem boundary -> copy dir instead */
2269 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2271 if (return_status != FILE_CONT)
2272 goto ret;
2273 oktoret:
2274 file_progress_show_source (ctx, NULL);
2275 file_progress_show (ctx, 0, 0, "", FALSE);
2277 return_status = check_progress_buttons (ctx);
2278 if (return_status != FILE_CONT)
2279 goto ret;
2281 mc_refresh ();
2282 if (ctx->erase_at_end)
2284 for (; erase_list != NULL && return_status != FILE_ABORT;)
2286 lp = (struct link *) erase_list->data;
2288 if (S_ISDIR (lp->st_mode))
2290 char *src_path;
2292 src_path = vfs_path_to_str (lp->src_vpath);
2293 return_status = erase_dir_iff_empty (ctx, src_path);
2294 g_free (src_path);
2296 else
2297 return_status = erase_file (tctx, ctx, lp->src_vpath);
2299 erase_list = g_slist_remove (erase_list, lp);
2300 free_link (lp);
2303 erase_dir_iff_empty (ctx, s);
2305 ret:
2306 g_free (destdir);
2307 vfs_path_free (destdir_vpath);
2308 erase_list = free_linklist (erase_list);
2309 ret_fast:
2310 vfs_path_free (src_vpath);
2311 vfs_path_free (dst_vpath);
2312 return return_status;
2315 /* }}} */
2317 /* --------------------------------------------------------------------------------------------- */
2318 /* {{{ Erase routines */
2320 FileProgressStatus
2321 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * s_vpath)
2323 FileProgressStatus error;
2324 char *s;
2326 s = vfs_path_to_str (s_vpath);
2329 if (strcmp (s, "..") == 0)
2330 return FILE_SKIP;
2332 if (strcmp (s, ".") == 0)
2333 return FILE_SKIP;
2336 file_progress_show_deleting (ctx, s);
2337 if (check_progress_buttons (ctx) == FILE_ABORT)
2339 g_free (s);
2340 return FILE_ABORT;
2342 mc_refresh ();
2344 /* The old way to detect a non empty directory was:
2345 error = my_rmdir (s);
2346 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2347 For the linux user space nfs server (nfs-server-2.2beta29-2)
2348 we would have to check also for EIO. I hope the new way is
2349 fool proof. (Norbert)
2351 error = check_dir_is_empty (s_vpath);
2352 if (error == 0)
2353 { /* not empty */
2354 error = query_recursive (ctx, s);
2355 if (error == FILE_CONT)
2356 error = recursive_erase (tctx, ctx, s);
2357 g_free (s);
2358 return error;
2361 while (my_rmdir (s) == -1 && !ctx->skip_all)
2363 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2364 if (error != FILE_RETRY)
2366 g_free (s);
2367 return error;
2371 g_free (s);
2372 return FILE_CONT;
2375 /* }}} */
2377 /* --------------------------------------------------------------------------------------------- */
2378 /* {{{ Panel operate routines */
2380 ComputeDirSizeUI *
2381 compute_dir_size_create_ui (void)
2383 ComputeDirSizeUI *ui;
2385 const char *b_name = N_("&Abort");
2387 #ifdef ENABLE_NLS
2388 b_name = _(b_name);
2389 #endif
2391 ui = g_new (ComputeDirSizeUI, 1);
2393 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
2394 NULL, _("Directory scanning"), DLG_CENTER);
2395 ui->dirname = label_new (3, 3, "");
2396 add_widget (ui->dlg, ui->dirname);
2398 add_widget (ui->dlg,
2399 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
2400 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2402 /* We will manage the dialog without any help,
2403 that's why we have to call init_dlg */
2404 init_dlg (ui->dlg);
2406 return ui;
2409 /* --------------------------------------------------------------------------------------------- */
2411 void
2412 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2414 if (ui != NULL)
2416 /* schedule to update passive panel */
2417 other_panel->dirty = 1;
2419 /* close and destroy dialog */
2420 dlg_run_done (ui->dlg);
2421 destroy_dlg (ui->dlg);
2422 g_free (ui);
2426 /* --------------------------------------------------------------------------------------------- */
2428 FileProgressStatus
2429 compute_dir_size_update_ui (const void *ui, const vfs_path_t * dirname_vpath)
2431 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2432 int c;
2433 Gpm_Event event;
2434 char *dirname;
2436 if (ui == NULL)
2437 return FILE_CONT;
2439 dirname = vfs_path_to_str (dirname_vpath);
2440 label_set_text (this->dirname, str_trunc (dirname, this->dlg->cols - 6));
2441 g_free (dirname);
2443 event.x = -1; /* Don't show the GPM cursor */
2444 c = tty_get_event (&event, FALSE, FALSE);
2445 if (c == EV_NONE)
2446 return FILE_CONT;
2448 /* Reinitialize to avoid old values after events other than
2449 selecting a button */
2450 this->dlg->ret_value = FILE_CONT;
2452 dlg_process_event (this->dlg, c, &event);
2454 switch (this->dlg->ret_value)
2456 case B_CANCEL:
2457 case FILE_ABORT:
2458 return FILE_ABORT;
2459 default:
2460 return FILE_CONT;
2464 /* --------------------------------------------------------------------------------------------- */
2466 * compute_dir_size:
2468 * Computes the number of bytes used by the files in a directory
2471 FileProgressStatus
2472 compute_dir_size (const vfs_path_t * dirname_vpath, const void *ui,
2473 compute_dir_size_callback cback,
2474 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2476 int res;
2477 struct stat s;
2478 DIR *dir;
2479 struct dirent *dirent;
2480 FileProgressStatus ret = FILE_CONT;
2482 if (!compute_symlinks)
2484 res = mc_lstat (dirname_vpath, &s);
2485 if (res != 0)
2486 return ret;
2488 /* don't scan symlink to directory */
2489 if (S_ISLNK (s.st_mode))
2491 (*ret_marked)++;
2492 *ret_total += (uintmax_t) s.st_size;
2493 return ret;
2497 dir = mc_opendir (dirname_vpath);
2499 if (dir == NULL)
2500 return ret;
2502 while ((dirent = mc_readdir (dir)) != NULL)
2504 vfs_path_t *tmp_vpath;
2506 ret = (cback != NULL) ? cback (ui, dirname_vpath) : FILE_CONT;
2508 if (ret != FILE_CONT)
2509 break;
2511 if (strcmp (dirent->d_name, ".") == 0)
2512 continue;
2513 if (strcmp (dirent->d_name, "..") == 0)
2514 continue;
2516 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, NULL);
2517 res = mc_lstat (tmp_vpath, &s);
2518 if (res == 0)
2520 if (S_ISDIR (s.st_mode))
2522 size_t subdir_count = 0;
2523 uintmax_t subdir_bytes = 0;
2525 ret =
2526 compute_dir_size (tmp_vpath, ui, cback, &subdir_count, &subdir_bytes,
2527 compute_symlinks);
2529 if (ret != FILE_CONT)
2531 vfs_path_free (tmp_vpath);
2532 break;
2535 *ret_marked += subdir_count;
2536 *ret_total += subdir_bytes;
2538 else
2540 (*ret_marked)++;
2541 *ret_total += (uintmax_t) s.st_size;
2544 vfs_path_free (tmp_vpath);
2547 mc_closedir (dir);
2548 return ret;
2551 /* --------------------------------------------------------------------------------------------- */
2553 * panel_operate:
2555 * Performs one of the operations on the selection on the source_panel
2556 * (copy, delete, move).
2558 * Returns TRUE if did change the directory
2559 * structure, Returns FALSE if user aborted
2561 * force_single forces operation on the current entry and affects
2562 * default destination. Current filename is used as default.
2565 gboolean
2566 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2568 WPanel *panel = (WPanel *) source_panel;
2569 const gboolean single_entry = force_single || (panel->marked <= 1)
2570 || (get_current_type () == view_tree);
2572 char *source = NULL;
2573 #ifdef WITH_FULL_PATHS
2574 vfs_path_t *source_with_vpath = NULL;
2575 char *source_with_path_str = NULL;
2576 #else
2577 #define source_with_path source
2578 #endif /* !WITH_FULL_PATHS */
2579 char *dest = NULL;
2580 vfs_path_t *dest_vpath = NULL;
2581 char *temp = NULL;
2582 char *save_cwd = NULL, *save_dest = NULL;
2583 struct stat src_stat;
2584 gboolean ret_val = TRUE;
2585 int i;
2586 FileProgressStatus value;
2587 FileOpContext *ctx;
2588 FileOpTotalContext *tctx;
2589 vfs_path_t *tmp_vpath;
2591 gboolean do_bg = FALSE; /* do background operation? */
2593 static gboolean i18n_flag = FALSE;
2594 if (!i18n_flag)
2596 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2597 op_names[i] = Q_ (op_names[i]);
2598 i18n_flag = TRUE;
2601 linklist = free_linklist (linklist);
2602 dest_dirs = free_linklist (dest_dirs);
2604 if (single_entry)
2606 vfs_path_t *source_vpath;
2608 if (force_single)
2609 source = g_strdup (selection (panel)->fname);
2610 else
2611 source = panel_get_file (panel);
2613 if (strcmp (source, "..") == 0)
2615 g_free (source);
2616 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2617 return FALSE;
2620 source_vpath = vfs_path_from_str (source);
2621 /* Update stat to get actual info */
2622 if (mc_lstat (source_vpath, &src_stat) != 0)
2624 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2625 path_trunc (source, 30), unix_error_string (errno));
2627 /* Directory was changed outside MC. Reload it forced */
2628 if (!panel->is_panelized)
2630 panel_update_flags_t flags = UP_RELOAD;
2632 /* don't update panelized panel */
2633 if (get_other_type () == view_listing && other_panel->is_panelized)
2634 flags |= UP_ONLY_CURRENT;
2636 update_panels (flags, UP_KEEPSEL);
2638 vfs_path_free (source_vpath);
2639 return FALSE;
2641 vfs_path_free (source_vpath);
2644 ctx = file_op_context_new (operation);
2646 /* Show confirmation dialog */
2647 if (operation != OP_DELETE)
2649 char *tmp_dest_dir, *dest_dir;
2650 char *format;
2652 /* Forced single operations default to the original name */
2653 if (force_single)
2654 tmp_dest_dir = g_strdup (source);
2655 else if (get_other_type () == view_listing)
2656 tmp_dest_dir = vfs_path_to_str (other_panel->cwd_vpath);
2657 else
2658 tmp_dest_dir = vfs_path_to_str (panel->cwd_vpath);
2660 * Add trailing backslash only when do non-local ops.
2661 * It saves user from occasional file renames (when destination
2662 * dir is deleted)
2664 if (!force_single && tmp_dest_dir[0] != '\0'
2665 && tmp_dest_dir[strlen (tmp_dest_dir) - 1] != PATH_SEP)
2667 /* add trailing separator */
2668 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
2669 g_free (tmp_dest_dir);
2671 else
2673 /* just copy */
2674 dest_dir = tmp_dest_dir;
2676 if (dest_dir == NULL)
2678 ret_val = FALSE;
2679 goto ret_fast;
2682 /* Generate confirmation prompt */
2683 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2685 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2686 source != NULL ? (void *) source
2687 : (void *) &panel->marked, dest_dir, &do_bg);
2689 g_free (format);
2690 g_free (dest_dir);
2692 if (dest == NULL || dest[0] == '\0')
2694 g_free (dest);
2695 ret_val = FALSE;
2696 goto ret_fast;
2698 dest_vpath = vfs_path_from_str (dest);
2700 else if (confirm_delete)
2702 char *format;
2703 char fmd_buf[BUF_MEDIUM];
2705 /* Generate confirmation prompt */
2706 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2708 if (source == NULL)
2709 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2710 else
2712 const int fmd_xlen = 64;
2713 i = fmd_xlen - str_term_width1 (format) - 4;
2714 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2717 g_free (format);
2719 if (safe_delete)
2720 query_set_sel (1);
2722 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2724 if (i != 0)
2726 ret_val = FALSE;
2727 goto ret_fast;
2731 tctx = file_op_total_context_new ();
2732 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2735 filegui_dialog_type_t dialog_type;
2737 if (operation == OP_DELETE)
2738 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2739 else
2741 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2742 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2744 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2745 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2748 /* Background also need ctx->ui, but not full */
2749 if (do_bg)
2750 file_op_context_create_ui_without_init (ctx, TRUE, dialog_type);
2751 else
2752 file_op_context_create_ui (ctx, TRUE, dialog_type);
2755 #ifdef ENABLE_BACKGROUND
2756 /* Did the user select to do a background operation? */
2757 if (do_bg)
2759 int v;
2760 char *cwd_str;
2762 cwd_str = vfs_path_to_str (panel->cwd_vpath);
2763 v = do_background (ctx, g_strconcat (op_names[operation], ": ", cwd_str, (char *) NULL));
2764 g_free (cwd_str);
2765 if (v == -1)
2766 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2768 /* If we are the parent */
2769 if (v == 1)
2771 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
2773 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
2774 vfs_path_free (dest_vpath);
2775 g_free (dest);
2776 /* file_op_context_destroy (ctx); */
2777 return FALSE;
2780 #endif /* ENABLE_BACKGROUND */
2782 /* Initialize things */
2783 /* We do not want to trash cache every time file is
2784 created/touched. However, this will make our cache contain
2785 invalid data. */
2786 if ((dest != NULL) && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2787 save_dest = g_strdup (dest);
2789 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
2790 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2791 save_cwd = vfs_path_to_str (panel->cwd_vpath);
2793 /* Now, let's do the job */
2795 /* This code is only called by the tree and panel code */
2796 if (single_entry)
2798 /* We now have ETA in all cases */
2800 /* One file: FIXME mc_chdir will take user out of any vfs */
2801 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2803 vfs_path_t *vpath;
2804 int chdir_retcode;
2806 vpath = vfs_path_from_str (PATH_SEP_STR);
2807 chdir_retcode = mc_chdir (vpath);
2808 vfs_path_free (vpath);
2809 if (chdir_retcode < 0)
2811 ret_val = FALSE;
2812 goto clean_up;
2816 /* The source and src_stat variables have been initialized before */
2817 #ifdef WITH_FULL_PATHS
2818 if (g_path_is_absolute (source))
2819 source_with_vpath = vfs_path_from_str (source);
2820 else
2821 source_with_vpath = vfs_path_append_new (panel->cwd_vpath, source, (char *) NULL);
2822 source_with_path_str = vfs_path_to_str (source_with_vpath);
2823 #endif /* WITH_FULL_PATHS */
2824 if (panel_operate_init_totals (operation, panel, source_with_path_str, ctx) == FILE_CONT)
2826 if (operation == OP_DELETE)
2828 if (S_ISDIR (src_stat.st_mode))
2829 value = erase_dir (tctx, ctx, source_with_vpath);
2830 else
2831 value = erase_file (tctx, ctx, source_with_vpath);
2833 else
2835 temp = transform_source (ctx, source_with_path_str);
2836 if (temp == NULL)
2837 value = transform_error;
2838 else
2840 char *repl_dest, *temp2;
2842 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2843 temp2 = mc_build_filename (repl_dest, temp, NULL);
2844 g_free (temp);
2845 g_free (repl_dest);
2846 g_free (dest);
2847 vfs_path_free (dest_vpath);
2848 dest = temp2;
2849 dest_vpath = vfs_path_from_str (dest);
2851 switch (operation)
2853 case OP_COPY:
2854 /* we use file_mask_op_follow_links only with OP_COPY */
2855 ctx->stat_func (source_with_vpath, &src_stat);
2857 if (S_ISDIR (src_stat.st_mode))
2858 value = copy_dir_dir (tctx, ctx, source_with_path_str, dest,
2859 TRUE, FALSE, FALSE, NULL);
2860 else
2861 value = copy_file_file (tctx, ctx, source_with_path_str, dest);
2862 break;
2864 case OP_MOVE:
2865 if (S_ISDIR (src_stat.st_mode))
2866 value = move_dir_dir (tctx, ctx, source_with_path_str, dest);
2867 else
2868 value = move_file_file (tctx, ctx, source_with_path_str, dest);
2869 break;
2871 default:
2872 /* Unknown file operation */
2873 abort ();
2876 } /* Copy or move operation */
2878 if ((value == FILE_CONT) && !force_single)
2879 unmark_files (panel);
2882 else
2884 /* Many files */
2886 /* Check destination for copy or move operation */
2887 while (operation != OP_DELETE)
2889 int dst_result;
2890 struct stat dst_stat;
2892 dst_result = mc_stat (dest_vpath, &dst_stat);
2894 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2895 break;
2897 if (ctx->skip_all
2898 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2899 goto clean_up;
2902 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2904 /* Loop for every file, perform the actual copy operation */
2905 for (i = 0; i < panel->count; i++)
2907 const char *source2;
2909 if (!panel->dir.list[i].f.marked)
2910 continue; /* Skip the unmarked ones */
2912 source2 = panel->dir.list[i].fname;
2913 src_stat = panel->dir.list[i].st;
2915 #ifdef WITH_FULL_PATHS
2916 g_free (source_with_path_str);
2917 vfs_path_free (source_with_vpath);
2918 if (g_path_is_absolute (source2))
2919 source_with_vpath = vfs_path_from_str (source2);
2920 else
2921 source_with_vpath =
2922 vfs_path_append_new (panel->cwd_vpath, source2, (char *) NULL);
2923 source_with_path_str = vfs_path_to_str (source_with_vpath);
2924 #endif /* WITH_FULL_PATHS */
2926 if (operation == OP_DELETE)
2928 if (S_ISDIR (src_stat.st_mode))
2929 value = erase_dir (tctx, ctx, source_with_vpath);
2930 else
2931 value = erase_file (tctx, ctx, source_with_vpath);
2933 else
2935 temp = transform_source (ctx, source_with_path_str);
2937 if (temp == NULL)
2938 value = transform_error;
2939 else
2941 char *temp2, *temp3, *repl_dest;
2943 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2944 temp2 = mc_build_filename (repl_dest, temp, NULL);
2945 g_free (temp);
2946 g_free (repl_dest);
2947 temp3 = source_with_path_str;
2948 source_with_path_str = strutils_shell_unescape (source_with_path_str);
2949 g_free (temp3);
2950 temp3 = temp2;
2951 temp2 = strutils_shell_unescape (temp2);
2952 g_free (temp3);
2954 switch (operation)
2956 case OP_COPY:
2957 /* we use file_mask_op_follow_links only with OP_COPY */
2959 vfs_path_t *vpath;
2961 vpath = vfs_path_from_str (source_with_path_str);
2962 ctx->stat_func (vpath, &src_stat);
2963 vfs_path_free (vpath);
2965 if (S_ISDIR (src_stat.st_mode))
2966 value = copy_dir_dir (tctx, ctx, source_with_path_str, temp2,
2967 TRUE, FALSE, FALSE, NULL);
2968 else
2969 value = copy_file_file (tctx, ctx, source_with_path_str, temp2);
2970 dest_dirs = free_linklist (dest_dirs);
2971 break;
2973 case OP_MOVE:
2974 if (S_ISDIR (src_stat.st_mode))
2975 value = move_dir_dir (tctx, ctx, source_with_path_str, temp2);
2976 else
2977 value = move_file_file (tctx, ctx, source_with_path_str, temp2);
2978 break;
2980 default:
2981 /* Unknown file operation */
2982 abort ();
2985 g_free (temp2);
2987 } /* Copy or move operation */
2989 if (value == FILE_ABORT)
2990 break;
2992 if (value == FILE_CONT)
2993 do_file_mark (panel, i, 0);
2995 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2997 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2998 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3001 if (operation != OP_DELETE)
3002 file_progress_show (ctx, 0, 0, "", FALSE);
3004 if (check_progress_buttons (ctx) == FILE_ABORT)
3005 break;
3007 mc_refresh ();
3008 } /* Loop for every file */
3010 } /* Many entries */
3012 clean_up:
3013 /* Clean up */
3014 if (save_cwd != NULL)
3016 tmp_vpath = vfs_path_from_str (save_cwd);
3017 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3018 vfs_path_free (tmp_vpath);
3019 g_free (save_cwd);
3022 if (save_dest != NULL)
3024 tmp_vpath = vfs_path_from_str (save_dest);
3025 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3026 vfs_path_free (tmp_vpath);
3027 g_free (save_dest);
3030 linklist = free_linklist (linklist);
3031 dest_dirs = free_linklist (dest_dirs);
3032 #ifdef WITH_FULL_PATHS
3033 g_free (source_with_path_str);
3034 vfs_path_free (source_with_vpath);
3035 #endif /* WITH_FULL_PATHS */
3036 g_free (dest);
3037 vfs_path_free (dest_vpath);
3038 g_free (ctx->dest_mask);
3039 ctx->dest_mask = NULL;
3041 #ifdef ENABLE_BACKGROUND
3042 /* Let our parent know we are saying bye bye */
3043 if (mc_global.we_are_background)
3045 int cur_pid = getpid ();
3046 /* Send pid to parent with child context, it is fork and
3047 don't modify real parent ctx */
3048 ctx->pid = cur_pid;
3049 parent_call ((void *) end_bg_process, ctx, 0);
3051 vfs_shut ();
3052 _exit (0);
3054 #endif /* ENABLE_BACKGROUND */
3056 file_op_total_context_destroy (tctx);
3057 ret_fast:
3058 file_op_context_destroy (ctx);
3059 g_free (source);
3061 return ret_val;
3064 /* }}} */
3066 /* --------------------------------------------------------------------------------------------- */
3067 /* {{{ Query/status report routines */
3068 /** Report error with one file */
3069 FileProgressStatus
3070 file_error (const char *format, const char *file)
3072 char buf[BUF_MEDIUM];
3074 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3076 return do_file_error (buf);
3079 /* --------------------------------------------------------------------------------------------- */
3082 Cause emacs to enter folding mode for this file:
3083 Local variables:
3084 end: