Center text in query owerwrite and delete dialogs.
[midnight-commander.git] / src / filemanager / file.c
blob5c0ecc0729dfc5d7c43f3fbfb0d9896ee8b6ac48
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 src/filemanager/file.c
47 * \brief Source: file management
50 /* {{{ Include files */
52 #include <config.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
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;
1345 while (*cp != '\0')
1346 *dp++ = *cp++;
1348 /* form two-lines query prompt for file deletion */
1349 if (operation == OP_DELETE && sp[-1] == 'f')
1351 *dp++ = '\n';
1353 while (isblank (*sp) != 0)
1354 sp++;
1357 break;
1358 default:
1359 *dp++ = *sp++;
1362 *dp = '\0';
1364 if (build_question)
1366 char tmp[BUF_MEDIUM];
1368 memmove (tmp, format_string, sizeof (tmp));
1369 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1372 return g_strdup (format_string);
1375 /* --------------------------------------------------------------------------------------------- */
1377 #ifdef ENABLE_BACKGROUND
1378 static int
1379 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1381 int pid = ctx->pid;
1383 (void) mode;
1384 ctx->pid = 0;
1386 unregister_task_with_pid (pid);
1387 /* file_op_context_destroy(ctx); */
1388 return 1;
1390 #endif
1391 /* }}} */
1393 /* --------------------------------------------------------------------------------------------- */
1394 /*** public functions ****************************************************************************/
1395 /* --------------------------------------------------------------------------------------------- */
1397 FileProgressStatus
1398 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1399 const char *src_path, const char *dst_path)
1401 uid_t src_uid = (uid_t) (-1);
1402 gid_t src_gid = (gid_t) (-1);
1404 int src_desc, dest_desc = -1;
1405 int n_read, n_written;
1406 mode_t src_mode = 0; /* The mode of the source file */
1407 struct stat sb, sb2;
1408 struct utimbuf utb;
1409 gboolean dst_exists = FALSE, appending = FALSE;
1410 off_t file_size = -1;
1411 FileProgressStatus return_status, temp_status;
1412 struct timeval tv_transfer_start;
1413 dest_status_t dst_status = DEST_NONE;
1414 int open_flags;
1415 gboolean is_first_time = TRUE;
1416 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1417 gboolean write_errno_nospace = FALSE;
1419 /* FIXME: We should not be using global variables! */
1420 ctx->do_reget = 0;
1421 return_status = FILE_RETRY;
1423 dst_vpath = vfs_path_from_str (dst_path);
1424 src_vpath = vfs_path_from_str (src_path);
1426 file_progress_show_source (ctx, src_vpath);
1427 file_progress_show_target (ctx, dst_vpath);
1429 if (check_progress_buttons (ctx) == FILE_ABORT)
1431 return_status = FILE_ABORT;
1432 goto ret_fast;
1435 mc_refresh ();
1437 while (mc_stat (dst_vpath, &sb2) == 0)
1439 if (S_ISDIR (sb2.st_mode))
1441 if (ctx->skip_all)
1442 return_status = FILE_SKIPALL;
1443 else
1445 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1446 if (return_status == FILE_SKIPALL)
1447 ctx->skip_all = TRUE;
1448 if (return_status == FILE_RETRY)
1449 continue;
1451 goto ret_fast;
1454 dst_exists = TRUE;
1455 break;
1458 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1460 if (ctx->skip_all)
1461 return_status = FILE_SKIPALL;
1462 else
1464 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1465 if (return_status == FILE_SKIPALL)
1466 ctx->skip_all = TRUE;
1469 if (return_status != FILE_RETRY)
1470 goto ret_fast;
1473 if (dst_exists)
1475 /* Destination already exists */
1476 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1478 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1479 src_path, dst_path);
1480 goto ret_fast;
1483 /* Should we replace destination? */
1484 if (tctx->ask_overwrite)
1486 ctx->do_reget = 0;
1487 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1488 if (return_status != FILE_CONT)
1489 goto ret_fast;
1493 if (!ctx->do_append)
1495 /* Check the hardlinks */
1496 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &sb))
1498 /* We have made a hardlink - no more processing is necessary */
1499 return_status = FILE_CONT;
1500 goto ret_fast;
1503 if (S_ISLNK (sb.st_mode))
1505 return_status = make_symlink (ctx, src_path, dst_path);
1506 goto ret_fast;
1509 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1510 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1512 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1513 && !ctx->skip_all)
1515 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1516 if (return_status == FILE_RETRY)
1517 continue;
1518 if (return_status == FILE_SKIPALL)
1519 ctx->skip_all = TRUE;
1520 goto ret_fast;
1522 /* Success */
1524 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1525 && !ctx->skip_all)
1527 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1528 if (temp_status == FILE_SKIP)
1529 break;
1530 if (temp_status == FILE_SKIPALL)
1531 ctx->skip_all = TRUE;
1532 if (temp_status != FILE_RETRY)
1534 return_status = temp_status;
1535 goto ret_fast;
1539 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1540 && !ctx->skip_all)
1542 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1543 if (temp_status == FILE_SKIP)
1544 break;
1545 if (temp_status == FILE_SKIPALL)
1546 ctx->skip_all = TRUE;
1547 if (temp_status != FILE_RETRY)
1549 return_status = temp_status;
1550 goto ret_fast;
1554 return_status = FILE_CONT;
1555 goto ret_fast;
1559 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1561 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1563 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1564 if (return_status == FILE_RETRY)
1565 continue;
1566 if (return_status == FILE_SKIPALL)
1567 ctx->skip_all = TRUE;
1568 if (return_status == FILE_SKIP)
1569 break;
1570 ctx->do_append = 0;
1571 goto ret_fast;
1574 if (ctx->do_reget != 0)
1576 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1578 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1579 ctx->do_reget = 0;
1580 ctx->do_append = FALSE;
1584 while (mc_fstat (src_desc, &sb) != 0)
1586 if (ctx->skip_all)
1587 return_status = FILE_SKIPALL;
1588 else
1590 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1591 if (return_status == FILE_RETRY)
1592 continue;
1593 if (return_status == FILE_SKIPALL)
1594 ctx->skip_all = TRUE;
1595 ctx->do_append = FALSE;
1597 goto ret;
1600 src_mode = sb.st_mode;
1601 src_uid = sb.st_uid;
1602 src_gid = sb.st_gid;
1603 utb.actime = sb.st_atime;
1604 utb.modtime = sb.st_mtime;
1605 file_size = sb.st_size;
1607 open_flags = O_WRONLY;
1608 if (dst_exists)
1610 if (ctx->do_append != 0)
1611 open_flags |= O_APPEND;
1612 else
1613 open_flags |= O_CREAT | O_TRUNC;
1615 else
1617 open_flags |= O_CREAT | O_EXCL;
1620 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
1622 if (errno != EEXIST)
1624 if (ctx->skip_all)
1625 return_status = FILE_SKIPALL;
1626 else
1628 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1629 if (return_status == FILE_RETRY)
1630 continue;
1631 if (return_status == FILE_SKIPALL)
1632 ctx->skip_all = TRUE;
1633 ctx->do_append = FALSE;
1636 goto ret;
1638 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1640 appending = ctx->do_append;
1641 ctx->do_append = FALSE;
1643 /* Find out the optimal buffer size. */
1644 while (mc_fstat (dest_desc, &sb) != 0)
1646 if (ctx->skip_all)
1647 return_status = FILE_SKIPALL;
1648 else
1650 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1651 if (return_status == FILE_RETRY)
1652 continue;
1653 if (return_status == FILE_SKIPALL)
1654 ctx->skip_all = TRUE;
1656 goto ret;
1659 while (TRUE)
1661 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1662 if (errno == 0)
1663 break;
1665 if (ctx->skip_all)
1666 return_status = FILE_SKIPALL;
1667 else
1669 return_status =
1670 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1671 if (return_status == FILE_RETRY)
1672 continue;
1673 if (return_status == FILE_SKIPALL)
1674 ctx->skip_all = TRUE;
1676 mc_close (dest_desc);
1677 dest_desc = -1;
1678 mc_unlink (dst_vpath);
1679 dst_status = DEST_NONE;
1680 goto ret;
1683 ctx->eta_secs = 0.0;
1684 ctx->bps = 0;
1686 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1687 file_progress_show (ctx, 0, file_size, "", TRUE);
1688 else
1689 file_progress_show (ctx, 1, 1, "", TRUE);
1690 return_status = check_progress_buttons (ctx);
1691 mc_refresh ();
1693 if (return_status != FILE_CONT)
1694 goto ret;
1697 off_t n_read_total = 0;
1698 struct timeval tv_current, tv_last_update, tv_last_input;
1699 int secs, update_secs;
1700 const char *stalled_msg = "";
1702 tv_last_update = tv_transfer_start;
1704 while (TRUE)
1706 char buf[BUF_8K];
1708 /* src_read */
1709 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1710 n_read = -1;
1711 else
1712 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1714 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1715 if (return_status == FILE_RETRY)
1716 continue;
1717 if (return_status == FILE_SKIPALL)
1718 ctx->skip_all = TRUE;
1719 goto ret;
1721 if (n_read == 0)
1722 break;
1724 gettimeofday (&tv_current, NULL);
1726 if (n_read > 0)
1728 char *t = buf;
1729 n_read_total += n_read;
1731 /* Windows NT ftp servers report that files have no
1732 * permissions: -------, so if we happen to have actually
1733 * read something, we should fix the permissions.
1735 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1736 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1737 gettimeofday (&tv_last_input, NULL);
1739 /* dst_write */
1740 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read)
1742 if (n_written > 0)
1744 n_read -= n_written;
1745 t += n_written;
1746 continue;
1749 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
1751 if (ctx->skip_all)
1752 return_status = FILE_SKIPALL;
1753 else
1754 return_status =
1755 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1757 if (return_status == FILE_SKIP)
1759 if (write_errno_nospace)
1760 goto ret;
1761 break;
1763 if (return_status == FILE_SKIPALL)
1765 ctx->skip_all = TRUE;
1766 if (write_errno_nospace)
1767 goto ret;
1769 if (return_status != FILE_RETRY)
1770 goto ret;
1772 /* User pressed "Retry". Will the next mc_write() call be succesful?
1773 * Reset error flag to be ready for that. */
1774 write_errno_nospace = FALSE;
1778 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1780 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1781 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1783 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1785 copy_file_file_display_progress (tctx, ctx,
1786 tv_current,
1787 tv_transfer_start, file_size, n_read_total);
1788 tv_last_update = tv_current;
1790 is_first_time = FALSE;
1792 if (update_secs > FILEOP_STALLING_INTERVAL)
1794 stalled_msg = _("(stalled)");
1798 gboolean force_update;
1800 force_update =
1801 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1803 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1805 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1806 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1809 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1810 force_update);
1812 mc_refresh ();
1814 return_status = check_progress_buttons (ctx);
1816 if (return_status != FILE_CONT)
1818 mc_refresh ();
1819 goto ret;
1824 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1826 ret:
1827 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1829 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1830 if (temp_status == FILE_RETRY)
1831 continue;
1832 if (temp_status == FILE_ABORT)
1833 return_status = temp_status;
1834 if (temp_status == FILE_SKIPALL)
1835 ctx->skip_all = TRUE;
1836 break;
1839 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1841 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1842 if (temp_status == FILE_RETRY)
1843 continue;
1844 if (temp_status == FILE_SKIPALL)
1845 ctx->skip_all = TRUE;
1846 return_status = temp_status;
1847 break;
1850 if (dst_status == DEST_SHORT)
1852 /* Remove short file */
1853 int result = 0;
1855 /* In case of copy/move to full partition, keep source file
1856 * and remove incomplete destination one */
1857 if (!write_errno_nospace)
1858 result = query_dialog (Q_ ("DialogTitle|Copy"),
1859 _("Incomplete file was retrieved. Keep it?"),
1860 D_ERROR, 2, _("&Delete"), _("&Keep"));
1861 if (result == 0)
1862 mc_unlink (dst_vpath);
1864 else if (dst_status == DEST_FULL)
1866 /* Copy has succeeded */
1867 if (!appending && ctx->preserve_uidgid)
1869 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1871 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1872 if (temp_status == FILE_RETRY)
1873 continue;
1874 if (temp_status == FILE_SKIPALL)
1876 ctx->skip_all = TRUE;
1877 return_status = FILE_CONT;
1879 if (temp_status == FILE_SKIP)
1880 return_status = FILE_CONT;
1881 break;
1885 if (!appending)
1887 if (ctx->preserve)
1889 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1891 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1892 if (temp_status == FILE_RETRY)
1893 continue;
1894 if (temp_status == FILE_SKIPALL)
1896 ctx->skip_all = TRUE;
1897 return_status = FILE_CONT;
1899 if (temp_status == FILE_SKIP)
1900 return_status = FILE_CONT;
1901 break;
1904 else if (!dst_exists)
1906 src_mode = umask (-1);
1907 umask (src_mode);
1908 src_mode = 0100666 & ~src_mode;
1909 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
1911 mc_utime (dst_vpath, &utb);
1915 if (return_status == FILE_CONT)
1916 return_status = progress_update_one (tctx, ctx, file_size);
1918 ret_fast:
1919 vfs_path_free (src_vpath);
1920 vfs_path_free (dst_vpath);
1921 return return_status;
1924 /* --------------------------------------------------------------------------------------------- */
1926 * I think these copy_*_* functions should have a return type.
1927 * anyway, this function *must* have two directories as arguments.
1929 /* FIXME: This function needs to check the return values of the
1930 function calls */
1932 FileProgressStatus
1933 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1934 gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs)
1936 struct dirent *next;
1937 struct stat buf, cbuf;
1938 DIR *reading;
1939 char *dest_dir = NULL;
1940 FileProgressStatus return_status = FILE_CONT;
1941 struct utimbuf utb;
1942 struct link *lp;
1943 char *d;
1944 vfs_path_t *src_vpath, *dst_vpath, *dest_dir_vpath = NULL;
1946 d = g_strdup (_d);
1948 src_vpath = vfs_path_from_str (s);
1949 dst_vpath = vfs_path_from_str (_d);
1951 /* First get the mode of the source dir */
1953 retry_src_stat:
1954 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
1956 if (ctx->skip_all)
1957 return_status = FILE_SKIPALL;
1958 else
1960 return_status = file_error (_("Cannot stat source directory \"%s\"\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 (dest_dirs, src_vpath, &cbuf))
1971 /* Don't copy a directory we created before (we don't want to copy
1972 infinitely if a directory is copied into itself) */
1973 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1974 return_status = FILE_CONT;
1975 goto ret_fast;
1978 /* Hmm, hardlink to directory??? - Norbert */
1979 /* FIXME: In this step we should do something
1980 in case the destination already exist */
1981 /* Check the hardlinks */
1982 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &cbuf))
1984 /* We have made a hardlink - no more processing is necessary */
1985 goto ret_fast;
1988 if (!S_ISDIR (cbuf.st_mode))
1990 if (ctx->skip_all)
1991 return_status = FILE_SKIPALL;
1992 else
1994 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1995 if (return_status == FILE_RETRY)
1996 goto retry_src_stat;
1997 if (return_status == FILE_SKIPALL)
1998 ctx->skip_all = TRUE;
2000 goto ret_fast;
2003 if (is_in_linklist (parent_dirs, src_vpath, &cbuf))
2005 /* we found a cyclic symbolic link */
2006 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
2007 return_status = FILE_SKIP;
2008 goto ret_fast;
2011 lp = g_new0 (struct link, 1);
2012 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
2013 lp->ino = cbuf.st_ino;
2014 lp->dev = cbuf.st_dev;
2015 parent_dirs = g_slist_prepend (parent_dirs, lp);
2017 retry_dst_stat:
2018 /* Now, check if the dest dir exists, if not, create it. */
2019 if (mc_stat (dst_vpath, &buf) != 0)
2021 /* Here the dir doesn't exist : make it ! */
2022 if (move_over)
2024 if (mc_rename (src_vpath, dst_vpath) == 0)
2026 return_status = FILE_CONT;
2027 goto ret;
2030 dest_dir = d;
2031 d = NULL;
2033 else
2036 * If the destination directory exists, we want to copy the whole
2037 * directory, but we only want this to happen once.
2039 * Escape sequences added to the * to compiler warnings.
2040 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2041 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2043 if (!S_ISDIR (buf.st_mode))
2045 if (ctx->skip_all)
2046 return_status = FILE_SKIPALL;
2047 else
2049 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2050 if (return_status == FILE_SKIPALL)
2051 ctx->skip_all = TRUE;
2052 if (return_status == FILE_RETRY)
2053 goto retry_dst_stat;
2055 goto ret;
2057 /* Dive into subdir if exists */
2058 if (toplevel && ctx->dive_into_subdirs)
2060 dest_dir = mc_build_filename (d, x_basename (s), NULL);
2062 else
2064 dest_dir = d;
2065 d = NULL;
2066 goto dont_mkdir;
2069 dest_dir_vpath = vfs_path_from_str (dest_dir);
2070 while (my_mkdir (dest_dir_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2072 if (ctx->skip_all)
2073 return_status = FILE_SKIPALL;
2074 else
2076 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
2077 if (return_status == FILE_SKIPALL)
2078 ctx->skip_all = TRUE;
2080 if (return_status != FILE_RETRY)
2081 goto ret;
2084 lp = g_new0 (struct link, 1);
2085 mc_stat (dest_dir_vpath, &buf);
2086 lp->vfs = vfs_path_get_by_index (dest_dir_vpath, -1)->class;
2087 lp->ino = buf.st_ino;
2088 lp->dev = buf.st_dev;
2089 dest_dirs = g_slist_prepend (dest_dirs, lp);
2091 if (ctx->preserve_uidgid)
2093 while (mc_chown (dest_dir_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2095 if (ctx->skip_all)
2096 return_status = FILE_SKIPALL;
2097 else
2099 return_status =
2100 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
2101 if (return_status == FILE_SKIPALL)
2102 ctx->skip_all = TRUE;
2104 if (return_status != FILE_RETRY)
2105 goto ret;
2109 dont_mkdir:
2110 /* open the source dir for reading */
2111 reading = mc_opendir (src_vpath);
2112 if (reading == NULL)
2113 goto ret;
2115 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2117 char *path;
2118 vfs_path_t *tmp_vpath;
2120 * Now, we don't want '.' and '..' to be created / copied at any time
2122 if (!strcmp (next->d_name, "."))
2123 continue;
2124 if (!strcmp (next->d_name, ".."))
2125 continue;
2127 /* get the filename and add it to the src directory */
2128 path = mc_build_filename (s, next->d_name, NULL);
2129 tmp_vpath = vfs_path_from_str (path);
2131 (*ctx->stat_func) (tmp_vpath, &buf);
2132 if (S_ISDIR (buf.st_mode))
2134 char *mdpath;
2136 mdpath = mc_build_filename (dest_dir, next->d_name, NULL);
2138 * From here, we just intend to recursively copy subdirs, not
2139 * the double functionality of copying different when the target
2140 * dir already exists. So, we give the recursive call the flag 0
2141 * meaning no toplevel.
2143 return_status =
2144 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2145 g_free (mdpath);
2147 else
2149 char *dest_file;
2151 dest_file = mc_build_filename (dest_dir, x_basename (path), NULL);
2152 return_status = copy_file_file (tctx, ctx, path, dest_file);
2153 g_free (dest_file);
2155 if (do_delete && return_status == FILE_CONT)
2157 if (ctx->erase_at_end)
2159 lp = g_new0 (struct link, 1);
2160 lp->src_vpath = vfs_path_clone (tmp_vpath);
2161 lp->st_mode = buf.st_mode;
2162 erase_list = g_slist_append (erase_list, lp);
2164 else if (S_ISDIR (buf.st_mode))
2165 return_status = erase_dir_iff_empty (ctx, path);
2166 else
2167 return_status = erase_file (tctx, ctx, tmp_vpath);
2169 g_free (path);
2170 vfs_path_free (tmp_vpath);
2172 mc_closedir (reading);
2174 if (ctx->preserve)
2176 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2177 utb.actime = cbuf.st_atime;
2178 utb.modtime = cbuf.st_mtime;
2179 mc_utime (dest_dir_vpath, &utb);
2181 else
2183 cbuf.st_mode = umask (-1);
2184 umask (cbuf.st_mode);
2185 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2186 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2189 ret:
2190 g_free (dest_dir);
2191 vfs_path_free (dest_dir_vpath);
2192 free_link (parent_dirs->data);
2193 g_slist_free_1 (parent_dirs);
2194 ret_fast:
2195 g_free (d);
2196 vfs_path_free (src_vpath);
2197 vfs_path_free (dst_vpath);
2198 return return_status;
2201 /* }}} */
2203 /* --------------------------------------------------------------------------------------------- */
2204 /* {{{ Move routines */
2206 FileProgressStatus
2207 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2209 struct stat sbuf, dbuf, destbuf;
2210 struct link *lp;
2211 char *destdir;
2212 FileProgressStatus return_status;
2213 gboolean move_over = FALSE;
2214 gboolean dstat_ok;
2215 vfs_path_t *src_vpath, *dst_vpath, *destdir_vpath;
2217 src_vpath = vfs_path_from_str (s);
2218 dst_vpath = vfs_path_from_str (d);
2220 file_progress_show_source (ctx, src_vpath);
2221 file_progress_show_target (ctx, dst_vpath);
2223 if (check_progress_buttons (ctx) == FILE_ABORT)
2225 return_status = FILE_ABORT;
2226 goto ret_fast;
2229 mc_refresh ();
2231 mc_stat (src_vpath, &sbuf);
2233 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2234 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2236 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2237 goto ret_fast;
2240 if (!dstat_ok)
2241 destdir = g_strdup (d); /* destination doesn't exist */
2242 else if (!ctx->dive_into_subdirs)
2244 destdir = g_strdup (d);
2245 move_over = TRUE;
2247 else
2248 destdir = mc_build_filename (d, x_basename (s), NULL);
2250 destdir_vpath = vfs_path_from_str (destdir);
2252 /* Check if the user inputted an existing dir */
2253 retry_dst_stat:
2254 if (mc_stat (destdir_vpath, &destbuf) == 0)
2256 if (move_over)
2258 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2260 if (return_status != FILE_CONT)
2261 goto ret;
2262 goto oktoret;
2264 else if (ctx->skip_all)
2265 return_status = FILE_SKIPALL;
2266 else
2268 if (S_ISDIR (destbuf.st_mode))
2269 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2270 else
2271 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2272 if (return_status == FILE_SKIPALL)
2273 ctx->skip_all = TRUE;
2274 if (return_status == FILE_RETRY)
2275 goto retry_dst_stat;
2278 g_free (destdir);
2279 vfs_path_free (destdir_vpath);
2280 goto ret_fast;
2283 retry_rename:
2284 if (mc_rename (src_vpath, destdir_vpath) == 0)
2286 return_status = FILE_CONT;
2287 goto ret;
2290 if (errno != EXDEV)
2292 if (!ctx->skip_all)
2294 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2295 if (return_status == FILE_SKIPALL)
2296 ctx->skip_all = TRUE;
2297 if (return_status == FILE_RETRY)
2298 goto retry_rename;
2300 goto ret;
2302 /* Failed because of filesystem boundary -> copy dir instead */
2303 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2305 if (return_status != FILE_CONT)
2306 goto ret;
2307 oktoret:
2308 file_progress_show_source (ctx, NULL);
2309 file_progress_show (ctx, 0, 0, "", FALSE);
2311 return_status = check_progress_buttons (ctx);
2312 if (return_status != FILE_CONT)
2313 goto ret;
2315 mc_refresh ();
2316 if (ctx->erase_at_end)
2318 for (; erase_list != NULL && return_status != FILE_ABORT;)
2320 lp = (struct link *) erase_list->data;
2322 if (S_ISDIR (lp->st_mode))
2324 char *src_path;
2326 src_path = vfs_path_to_str (lp->src_vpath);
2327 return_status = erase_dir_iff_empty (ctx, src_path);
2328 g_free (src_path);
2330 else
2331 return_status = erase_file (tctx, ctx, lp->src_vpath);
2333 erase_list = g_slist_remove (erase_list, lp);
2334 free_link (lp);
2337 erase_dir_iff_empty (ctx, s);
2339 ret:
2340 g_free (destdir);
2341 vfs_path_free (destdir_vpath);
2342 erase_list = free_linklist (erase_list);
2343 ret_fast:
2344 vfs_path_free (src_vpath);
2345 vfs_path_free (dst_vpath);
2346 return return_status;
2349 /* }}} */
2351 /* --------------------------------------------------------------------------------------------- */
2352 /* {{{ Erase routines */
2354 FileProgressStatus
2355 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * s_vpath)
2357 FileProgressStatus error;
2358 char *s;
2360 s = vfs_path_to_str (s_vpath);
2363 if (strcmp (s, "..") == 0)
2364 return FILE_SKIP;
2366 if (strcmp (s, ".") == 0)
2367 return FILE_SKIP;
2370 file_progress_show_deleting (ctx, s);
2371 if (check_progress_buttons (ctx) == FILE_ABORT)
2373 g_free (s);
2374 return FILE_ABORT;
2376 mc_refresh ();
2378 /* The old way to detect a non empty directory was:
2379 error = my_rmdir (s);
2380 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2381 For the linux user space nfs server (nfs-server-2.2beta29-2)
2382 we would have to check also for EIO. I hope the new way is
2383 fool proof. (Norbert)
2385 error = check_dir_is_empty (s_vpath);
2386 if (error == 0)
2387 { /* not empty */
2388 error = query_recursive (ctx, s);
2389 if (error == FILE_CONT)
2390 error = recursive_erase (tctx, ctx, s);
2391 g_free (s);
2392 return error;
2395 while (my_rmdir (s) == -1 && !ctx->skip_all)
2397 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2398 if (error != FILE_RETRY)
2400 g_free (s);
2401 return error;
2405 g_free (s);
2406 return FILE_CONT;
2409 /* }}} */
2411 /* --------------------------------------------------------------------------------------------- */
2412 /* {{{ Panel operate routines */
2414 ComputeDirSizeUI *
2415 compute_dir_size_create_ui (void)
2417 ComputeDirSizeUI *ui;
2419 const char *b_name = N_("&Abort");
2421 #ifdef ENABLE_NLS
2422 b_name = _(b_name);
2423 #endif
2425 ui = g_new (ComputeDirSizeUI, 1);
2427 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL, NULL,
2428 NULL, _("Directory scanning"), DLG_CENTER);
2429 ui->dirname = label_new (3, 3, "");
2430 add_widget (ui->dlg, ui->dirname);
2432 add_widget (ui->dlg,
2433 button_new (5, (WIDGET (ui->dlg)->cols - strlen (b_name)) / 2,
2434 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2436 /* We will manage the dialog without any help,
2437 that's why we have to call init_dlg */
2438 init_dlg (ui->dlg);
2440 return ui;
2443 /* --------------------------------------------------------------------------------------------- */
2445 void
2446 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2448 if (ui != NULL)
2450 /* schedule to update passive panel */
2451 other_panel->dirty = 1;
2453 /* close and destroy dialog */
2454 dlg_run_done (ui->dlg);
2455 destroy_dlg (ui->dlg);
2456 g_free (ui);
2460 /* --------------------------------------------------------------------------------------------- */
2462 FileProgressStatus
2463 compute_dir_size_update_ui (const void *ui, const vfs_path_t * dirname_vpath)
2465 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2466 int c;
2467 Gpm_Event event;
2468 char *dirname;
2470 if (ui == NULL)
2471 return FILE_CONT;
2473 dirname = vfs_path_to_str (dirname_vpath);
2474 label_set_text (this->dirname, str_trunc (dirname, WIDGET (this->dlg)->cols - 6));
2475 g_free (dirname);
2477 event.x = -1; /* Don't show the GPM cursor */
2478 c = tty_get_event (&event, FALSE, FALSE);
2479 if (c == EV_NONE)
2480 return FILE_CONT;
2482 /* Reinitialize to avoid old values after events other than
2483 selecting a button */
2484 this->dlg->ret_value = FILE_CONT;
2486 dlg_process_event (this->dlg, c, &event);
2488 switch (this->dlg->ret_value)
2490 case B_CANCEL:
2491 case FILE_ABORT:
2492 return FILE_ABORT;
2493 default:
2494 return FILE_CONT;
2498 /* --------------------------------------------------------------------------------------------- */
2500 * compute_dir_size:
2502 * Computes the number of bytes used by the files in a directory
2505 FileProgressStatus
2506 compute_dir_size (const vfs_path_t * dirname_vpath, const void *ui,
2507 compute_dir_size_callback cback,
2508 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2510 int res;
2511 struct stat s;
2512 DIR *dir;
2513 struct dirent *dirent;
2514 FileProgressStatus ret = FILE_CONT;
2516 if (!compute_symlinks)
2518 res = mc_lstat (dirname_vpath, &s);
2519 if (res != 0)
2520 return ret;
2522 /* don't scan symlink to directory */
2523 if (S_ISLNK (s.st_mode))
2525 (*ret_marked)++;
2526 *ret_total += (uintmax_t) s.st_size;
2527 return ret;
2531 dir = mc_opendir (dirname_vpath);
2533 if (dir == NULL)
2534 return ret;
2536 while ((dirent = mc_readdir (dir)) != NULL)
2538 vfs_path_t *tmp_vpath;
2540 ret = (cback != NULL) ? cback (ui, dirname_vpath) : FILE_CONT;
2542 if (ret != FILE_CONT)
2543 break;
2545 if (strcmp (dirent->d_name, ".") == 0)
2546 continue;
2547 if (strcmp (dirent->d_name, "..") == 0)
2548 continue;
2550 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, NULL);
2551 res = mc_lstat (tmp_vpath, &s);
2552 if (res == 0)
2554 if (S_ISDIR (s.st_mode))
2556 size_t subdir_count = 0;
2557 uintmax_t subdir_bytes = 0;
2559 ret =
2560 compute_dir_size (tmp_vpath, ui, cback, &subdir_count, &subdir_bytes,
2561 compute_symlinks);
2563 if (ret != FILE_CONT)
2565 vfs_path_free (tmp_vpath);
2566 break;
2569 *ret_marked += subdir_count;
2570 *ret_total += subdir_bytes;
2572 else
2574 (*ret_marked)++;
2575 *ret_total += (uintmax_t) s.st_size;
2578 vfs_path_free (tmp_vpath);
2581 mc_closedir (dir);
2582 return ret;
2585 /* --------------------------------------------------------------------------------------------- */
2587 * panel_operate:
2589 * Performs one of the operations on the selection on the source_panel
2590 * (copy, delete, move).
2592 * Returns TRUE if did change the directory
2593 * structure, Returns FALSE if user aborted
2595 * force_single forces operation on the current entry and affects
2596 * default destination. Current filename is used as default.
2599 gboolean
2600 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2602 WPanel *panel = (WPanel *) source_panel;
2603 const gboolean single_entry = force_single || (panel->marked <= 1)
2604 || (get_current_type () == view_tree);
2606 char *source = NULL;
2607 #ifdef WITH_FULL_PATHS
2608 vfs_path_t *source_with_vpath = NULL;
2609 char *source_with_path_str = NULL;
2610 #else
2611 #define source_with_path source
2612 #endif /* !WITH_FULL_PATHS */
2613 char *dest = NULL;
2614 vfs_path_t *dest_vpath = NULL;
2615 char *temp = NULL;
2616 char *save_cwd = NULL, *save_dest = NULL;
2617 struct stat src_stat;
2618 gboolean ret_val = TRUE;
2619 int i;
2620 FileProgressStatus value;
2621 FileOpContext *ctx;
2622 FileOpTotalContext *tctx;
2623 vfs_path_t *tmp_vpath;
2625 gboolean do_bg = FALSE; /* do background operation? */
2627 static gboolean i18n_flag = FALSE;
2628 if (!i18n_flag)
2630 for (i = sizeof (op_names) / sizeof (op_names[0]); i--;)
2631 op_names[i] = Q_ (op_names[i]);
2632 i18n_flag = TRUE;
2635 linklist = free_linklist (linklist);
2636 dest_dirs = free_linklist (dest_dirs);
2638 if (single_entry)
2640 vfs_path_t *source_vpath;
2642 if (force_single)
2643 source = g_strdup (selection (panel)->fname);
2644 else
2645 source = panel_get_file (panel);
2647 if (strcmp (source, "..") == 0)
2649 g_free (source);
2650 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2651 return FALSE;
2654 source_vpath = vfs_path_from_str (source);
2655 /* Update stat to get actual info */
2656 if (mc_lstat (source_vpath, &src_stat) != 0)
2658 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2659 path_trunc (source, 30), unix_error_string (errno));
2661 /* Directory was changed outside MC. Reload it forced */
2662 if (!panel->is_panelized)
2664 panel_update_flags_t flags = UP_RELOAD;
2666 /* don't update panelized panel */
2667 if (get_other_type () == view_listing && other_panel->is_panelized)
2668 flags |= UP_ONLY_CURRENT;
2670 update_panels (flags, UP_KEEPSEL);
2672 vfs_path_free (source_vpath);
2673 return FALSE;
2675 vfs_path_free (source_vpath);
2678 ctx = file_op_context_new (operation);
2680 /* Show confirmation dialog */
2681 if (operation != OP_DELETE)
2683 char *tmp_dest_dir, *dest_dir;
2684 char *format;
2686 /* Forced single operations default to the original name */
2687 if (force_single)
2688 tmp_dest_dir = g_strdup (source);
2689 else if (get_other_type () == view_listing)
2690 tmp_dest_dir = vfs_path_to_str (other_panel->cwd_vpath);
2691 else
2692 tmp_dest_dir = vfs_path_to_str (panel->cwd_vpath);
2694 * Add trailing backslash only when do non-local ops.
2695 * It saves user from occasional file renames (when destination
2696 * dir is deleted)
2698 if (!force_single && tmp_dest_dir[0] != '\0'
2699 && tmp_dest_dir[strlen (tmp_dest_dir) - 1] != PATH_SEP)
2701 /* add trailing separator */
2702 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
2703 g_free (tmp_dest_dir);
2705 else
2707 /* just copy */
2708 dest_dir = tmp_dest_dir;
2710 if (dest_dir == NULL)
2712 ret_val = FALSE;
2713 goto ret_fast;
2716 /* Generate confirmation prompt */
2717 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2719 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2720 source != NULL ? (void *) source
2721 : (void *) &panel->marked, dest_dir, &do_bg);
2723 g_free (format);
2724 g_free (dest_dir);
2726 if (dest == NULL || dest[0] == '\0')
2728 g_free (dest);
2729 ret_val = FALSE;
2730 goto ret_fast;
2732 dest_vpath = vfs_path_from_str (dest);
2734 else if (confirm_delete)
2736 char *format;
2737 char fmd_buf[BUF_MEDIUM];
2739 /* Generate confirmation prompt */
2740 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2742 if (source == NULL)
2743 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2744 else
2746 const int fmd_xlen = 64;
2747 i = fmd_xlen - str_term_width1 (format) - 4;
2748 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2751 g_free (format);
2753 if (safe_delete)
2754 query_set_sel (1);
2756 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2758 if (i != 0)
2760 ret_val = FALSE;
2761 goto ret_fast;
2765 tctx = file_op_total_context_new ();
2766 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2769 filegui_dialog_type_t dialog_type;
2771 if (operation == OP_DELETE)
2772 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2773 else
2775 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2776 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2778 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2779 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2782 /* Background also need ctx->ui, but not full */
2783 if (do_bg)
2784 file_op_context_create_ui_without_init (ctx, TRUE, dialog_type);
2785 else
2786 file_op_context_create_ui (ctx, TRUE, dialog_type);
2789 #ifdef ENABLE_BACKGROUND
2790 /* Did the user select to do a background operation? */
2791 if (do_bg)
2793 int v;
2794 char *cwd_str;
2796 cwd_str = vfs_path_to_str (panel->cwd_vpath);
2797 v = do_background (ctx, g_strconcat (op_names[operation], ": ", cwd_str, (char *) NULL));
2798 g_free (cwd_str);
2799 if (v == -1)
2800 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2802 /* If we are the parent */
2803 if (v == 1)
2805 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
2807 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
2808 vfs_path_free (dest_vpath);
2809 g_free (dest);
2810 /* file_op_context_destroy (ctx); */
2811 return FALSE;
2814 #endif /* ENABLE_BACKGROUND */
2816 /* Initialize things */
2817 /* We do not want to trash cache every time file is
2818 created/touched. However, this will make our cache contain
2819 invalid data. */
2820 if ((dest != NULL) && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2821 save_dest = g_strdup (dest);
2823 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
2824 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2825 save_cwd = vfs_path_to_str (panel->cwd_vpath);
2827 /* Now, let's do the job */
2829 /* This code is only called by the tree and panel code */
2830 if (single_entry)
2832 /* We now have ETA in all cases */
2834 /* One file: FIXME mc_chdir will take user out of any vfs */
2835 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2837 vfs_path_t *vpath;
2838 int chdir_retcode;
2840 vpath = vfs_path_from_str (PATH_SEP_STR);
2841 chdir_retcode = mc_chdir (vpath);
2842 vfs_path_free (vpath);
2843 if (chdir_retcode < 0)
2845 ret_val = FALSE;
2846 goto clean_up;
2850 /* The source and src_stat variables have been initialized before */
2851 #ifdef WITH_FULL_PATHS
2852 if (g_path_is_absolute (source))
2853 source_with_vpath = vfs_path_from_str (source);
2854 else
2855 source_with_vpath = vfs_path_append_new (panel->cwd_vpath, source, (char *) NULL);
2856 source_with_path_str = vfs_path_to_str (source_with_vpath);
2857 #endif /* WITH_FULL_PATHS */
2858 if (panel_operate_init_totals (operation, panel, source_with_path_str, ctx) == FILE_CONT)
2860 if (operation == OP_DELETE)
2862 if (S_ISDIR (src_stat.st_mode))
2863 value = erase_dir (tctx, ctx, source_with_vpath);
2864 else
2865 value = erase_file (tctx, ctx, source_with_vpath);
2867 else
2869 temp = transform_source (ctx, source_with_path_str);
2870 if (temp == NULL)
2871 value = transform_error;
2872 else
2874 char *repl_dest, *temp2;
2876 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2877 temp2 = mc_build_filename (repl_dest, temp, NULL);
2878 g_free (temp);
2879 g_free (repl_dest);
2880 g_free (dest);
2881 vfs_path_free (dest_vpath);
2882 dest = temp2;
2883 dest_vpath = vfs_path_from_str (dest);
2885 switch (operation)
2887 case OP_COPY:
2888 /* we use file_mask_op_follow_links only with OP_COPY */
2889 ctx->stat_func (source_with_vpath, &src_stat);
2891 if (S_ISDIR (src_stat.st_mode))
2892 value = copy_dir_dir (tctx, ctx, source_with_path_str, dest,
2893 TRUE, FALSE, FALSE, NULL);
2894 else
2895 value = copy_file_file (tctx, ctx, source_with_path_str, dest);
2896 break;
2898 case OP_MOVE:
2899 if (S_ISDIR (src_stat.st_mode))
2900 value = move_dir_dir (tctx, ctx, source_with_path_str, dest);
2901 else
2902 value = move_file_file (tctx, ctx, source_with_path_str, dest);
2903 break;
2905 default:
2906 /* Unknown file operation */
2907 abort ();
2910 } /* Copy or move operation */
2912 if ((value == FILE_CONT) && !force_single)
2913 unmark_files (panel);
2916 else
2918 /* Many files */
2920 /* Check destination for copy or move operation */
2921 while (operation != OP_DELETE)
2923 int dst_result;
2924 struct stat dst_stat;
2926 dst_result = mc_stat (dest_vpath, &dst_stat);
2928 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2929 break;
2931 if (ctx->skip_all
2932 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2933 goto clean_up;
2936 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2938 /* Loop for every file, perform the actual copy operation */
2939 for (i = 0; i < panel->count; i++)
2941 const char *source2;
2943 if (!panel->dir.list[i].f.marked)
2944 continue; /* Skip the unmarked ones */
2946 source2 = panel->dir.list[i].fname;
2947 src_stat = panel->dir.list[i].st;
2949 #ifdef WITH_FULL_PATHS
2950 g_free (source_with_path_str);
2951 vfs_path_free (source_with_vpath);
2952 if (g_path_is_absolute (source2))
2953 source_with_vpath = vfs_path_from_str (source2);
2954 else
2955 source_with_vpath =
2956 vfs_path_append_new (panel->cwd_vpath, source2, (char *) NULL);
2957 source_with_path_str = vfs_path_to_str (source_with_vpath);
2958 #endif /* WITH_FULL_PATHS */
2960 if (operation == OP_DELETE)
2962 if (S_ISDIR (src_stat.st_mode))
2963 value = erase_dir (tctx, ctx, source_with_vpath);
2964 else
2965 value = erase_file (tctx, ctx, source_with_vpath);
2967 else
2969 temp = transform_source (ctx, source_with_path_str);
2971 if (temp == NULL)
2972 value = transform_error;
2973 else
2975 char *temp2, *temp3, *repl_dest;
2977 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2978 temp2 = mc_build_filename (repl_dest, temp, NULL);
2979 g_free (temp);
2980 g_free (repl_dest);
2981 temp3 = source_with_path_str;
2982 source_with_path_str = strutils_shell_unescape (source_with_path_str);
2983 g_free (temp3);
2984 temp3 = temp2;
2985 temp2 = strutils_shell_unescape (temp2);
2986 g_free (temp3);
2988 switch (operation)
2990 case OP_COPY:
2991 /* we use file_mask_op_follow_links only with OP_COPY */
2993 vfs_path_t *vpath;
2995 vpath = vfs_path_from_str (source_with_path_str);
2996 ctx->stat_func (vpath, &src_stat);
2997 vfs_path_free (vpath);
2999 if (S_ISDIR (src_stat.st_mode))
3000 value = copy_dir_dir (tctx, ctx, source_with_path_str, temp2,
3001 TRUE, FALSE, FALSE, NULL);
3002 else
3003 value = copy_file_file (tctx, ctx, source_with_path_str, temp2);
3004 dest_dirs = free_linklist (dest_dirs);
3005 break;
3007 case OP_MOVE:
3008 if (S_ISDIR (src_stat.st_mode))
3009 value = move_dir_dir (tctx, ctx, source_with_path_str, temp2);
3010 else
3011 value = move_file_file (tctx, ctx, source_with_path_str, temp2);
3012 break;
3014 default:
3015 /* Unknown file operation */
3016 abort ();
3019 g_free (temp2);
3021 } /* Copy or move operation */
3023 if (value == FILE_ABORT)
3024 break;
3026 if (value == FILE_CONT)
3027 do_file_mark (panel, i, 0);
3029 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3031 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3032 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3035 if (operation != OP_DELETE)
3036 file_progress_show (ctx, 0, 0, "", FALSE);
3038 if (check_progress_buttons (ctx) == FILE_ABORT)
3039 break;
3041 mc_refresh ();
3042 } /* Loop for every file */
3044 } /* Many entries */
3046 clean_up:
3047 /* Clean up */
3048 if (save_cwd != NULL)
3050 tmp_vpath = vfs_path_from_str (save_cwd);
3051 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3052 vfs_path_free (tmp_vpath);
3053 g_free (save_cwd);
3056 if (save_dest != NULL)
3058 tmp_vpath = vfs_path_from_str (save_dest);
3059 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3060 vfs_path_free (tmp_vpath);
3061 g_free (save_dest);
3064 linklist = free_linklist (linklist);
3065 dest_dirs = free_linklist (dest_dirs);
3066 #ifdef WITH_FULL_PATHS
3067 g_free (source_with_path_str);
3068 vfs_path_free (source_with_vpath);
3069 #endif /* WITH_FULL_PATHS */
3070 g_free (dest);
3071 vfs_path_free (dest_vpath);
3072 g_free (ctx->dest_mask);
3073 ctx->dest_mask = NULL;
3075 #ifdef ENABLE_BACKGROUND
3076 /* Let our parent know we are saying bye bye */
3077 if (mc_global.we_are_background)
3079 int cur_pid = getpid ();
3080 /* Send pid to parent with child context, it is fork and
3081 don't modify real parent ctx */
3082 ctx->pid = cur_pid;
3083 parent_call ((void *) end_bg_process, ctx, 0);
3085 vfs_shut ();
3086 _exit (0);
3088 #endif /* ENABLE_BACKGROUND */
3090 file_op_total_context_destroy (tctx);
3091 ret_fast:
3092 file_op_context_destroy (ctx);
3093 g_free (source);
3095 return ret_val;
3098 /* }}} */
3100 /* --------------------------------------------------------------------------------------------- */
3101 /* {{{ Query/status report routines */
3102 /** Report error with one file */
3103 FileProgressStatus
3104 file_error (const char *format, const char *file)
3106 char buf[BUF_MEDIUM];
3108 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3110 return do_file_error (buf);
3113 /* --------------------------------------------------------------------------------------------- */
3116 Cause emacs to enter folding mode for this file:
3117 Local variables:
3118 end: