(copy_dir_dir): refactoring: get rid of goto dont_mkdir.
[midnight-commander.git] / src / filemanager / file.c
blob79ec70c99152c4e5737246f646f808f910c61a3b
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)
584 if (ctx->recursive_result < RECURSIVE_ALWAYS)
587 const char *msg;
588 char *text;
590 msg = mode == Foreground
591 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
592 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
593 text = g_strdup_printf (msg, path_trunc (s, 30));
595 if (safe_delete)
596 query_set_sel (1);
598 ctx->recursive_result =
599 (FileCopyMode) query_dialog (op_names[OP_DELETE], text, D_ERROR, 5,
600 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
601 g_free (text);
603 if (ctx->recursive_result != RECURSIVE_ABORT)
604 do_refresh ();
607 switch (ctx->recursive_result)
609 case RECURSIVE_YES:
610 case RECURSIVE_ALWAYS:
611 return FILE_CONT;
613 case RECURSIVE_NO:
614 case RECURSIVE_NEVER:
615 return FILE_SKIP;
617 case RECURSIVE_ABORT:
618 default:
619 return FILE_ABORT;
623 /* --------------------------------------------------------------------------------------------- */
625 #ifdef ENABLE_BACKGROUND
626 static FileProgressStatus
627 do_file_error (const char *str)
629 union
631 void *p;
632 FileProgressStatus (*f) (enum OperationMode, const char *);
633 } pntr;
634 pntr.f = real_do_file_error;
636 if (mc_global.we_are_background)
637 return parent_call (pntr.p, NULL, 1, strlen (str), str);
638 else
639 return real_do_file_error (Foreground, str);
642 /* --------------------------------------------------------------------------------------------- */
644 static FileProgressStatus
645 query_recursive (FileOpContext * ctx, const char *s)
647 union
649 void *p;
650 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
651 } pntr;
652 pntr.f = real_query_recursive;
654 if (mc_global.we_are_background)
655 return parent_call (pntr.p, ctx, 1, strlen (s), s);
656 else
657 return real_query_recursive (ctx, Foreground, s);
660 /* --------------------------------------------------------------------------------------------- */
662 static FileProgressStatus
663 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
664 struct stat *_d_stat)
666 union
668 void *p;
669 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
670 struct stat *, struct stat *);
671 } pntr;
672 pntr.f = file_progress_real_query_replace;
674 if (mc_global.we_are_background)
675 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
676 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
677 else
678 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
681 #else
682 /* --------------------------------------------------------------------------------------------- */
684 static FileProgressStatus
685 do_file_error (const char *str)
687 return real_do_file_error (Foreground, str);
690 /* --------------------------------------------------------------------------------------------- */
692 static FileProgressStatus
693 query_recursive (FileOpContext * ctx, const char *s)
695 return real_query_recursive (ctx, Foreground, s);
698 /* --------------------------------------------------------------------------------------------- */
700 static FileProgressStatus
701 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
702 struct stat *_d_stat)
704 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
707 #endif /* !ENABLE_BACKGROUND */
709 /* --------------------------------------------------------------------------------------------- */
710 /** Report error with two files */
712 static FileProgressStatus
713 files_error (const char *format, const char *file1, const char *file2)
715 char buf[BUF_MEDIUM];
716 char *nfile1 = g_strdup (path_trunc (file1, 15));
717 char *nfile2 = g_strdup (path_trunc (file2, 15));
719 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
721 g_free (nfile1);
722 g_free (nfile2);
724 return do_file_error (buf);
727 /* }}} */
729 /* --------------------------------------------------------------------------------------------- */
731 static void
732 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
733 struct timeval tv_current, struct timeval tv_transfer_start,
734 off_t file_size, off_t n_read_total)
736 long dt;
738 /* 1. Update rotating dash after some time */
739 rotate_dash ();
741 /* 3. Compute ETA */
742 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
744 if (n_read_total == 0)
745 ctx->eta_secs = 0.0;
746 else
748 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
749 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
752 /* 4. Compute BPS rate */
753 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
754 if (ctx->bps_time < 1)
755 ctx->bps_time = 1;
756 ctx->bps = n_read_total / ctx->bps_time;
758 /* 5. Compute total ETA and BPS */
759 if (ctx->progress_bytes != 0)
761 uintmax_t remain_bytes;
763 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
764 #if 1
766 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
768 if (total_secs < 1)
769 total_secs = 1;
771 tctx->bps = tctx->copied_bytes / total_secs;
772 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
774 #else
775 /* broken on lot of little files */
776 tctx->bps_count++;
777 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
778 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
779 #endif
783 /* --------------------------------------------------------------------------------------------- */
785 /* {{{ Move routines */
786 static FileProgressStatus
787 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
789 struct stat src_stats, dst_stats;
790 FileProgressStatus return_status = FILE_CONT;
791 gboolean copy_done = FALSE;
792 gboolean old_ask_overwrite;
793 vfs_path_t *src_vpath, *dst_vpath;
795 src_vpath = vfs_path_from_str (s);
796 dst_vpath = vfs_path_from_str (d);
798 file_progress_show_source (ctx, src_vpath);
799 file_progress_show_target (ctx, dst_vpath);
801 if (check_progress_buttons (ctx) == FILE_ABORT)
803 return_status = FILE_ABORT;
804 goto ret;
807 mc_refresh ();
809 while (mc_lstat (src_vpath, &src_stats) != 0)
811 /* Source doesn't exist */
812 if (ctx->skip_all)
813 return_status = FILE_SKIPALL;
814 else
816 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
817 if (return_status == FILE_SKIPALL)
818 ctx->skip_all = TRUE;
821 if (return_status != FILE_RETRY)
822 goto ret;
825 if (mc_lstat (dst_vpath, &dst_stats) == 0)
827 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
829 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
830 goto ret;
833 if (S_ISDIR (dst_stats.st_mode))
835 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
836 do_refresh ();
837 return_status = FILE_SKIP;
838 goto ret;
841 if (confirm_overwrite)
843 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
844 if (return_status != FILE_CONT)
845 goto ret;
847 /* Ok to overwrite */
850 if (!ctx->do_append)
852 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
854 return_status = make_symlink (ctx, s, d);
855 if (return_status == FILE_CONT)
856 goto retry_src_remove;
857 goto ret;
860 if (mc_rename (src_vpath, dst_vpath) == 0)
862 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
863 goto ret;
866 #if 0
867 /* Comparison to EXDEV seems not to work in nfs if you're moving from
868 one nfs to the same, but on the server it is on two different
869 filesystems. Then nfs returns EIO instead of EXDEV.
870 Hope it will not hurt if we always in case of error try to copy/delete. */
871 else
872 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
874 if (errno != EXDEV)
876 if (ctx->skip_all)
877 return_status = FILE_SKIPALL;
878 else
880 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
881 if (return_status == FILE_SKIPALL)
882 ctx->skip_all = TRUE;
883 if (return_status == FILE_RETRY)
884 goto retry_rename;
887 goto ret;
889 #endif
891 /* Failed because filesystem boundary -> copy the file instead */
892 old_ask_overwrite = tctx->ask_overwrite;
893 tctx->ask_overwrite = FALSE;
894 return_status = copy_file_file (tctx, ctx, s, d);
895 tctx->ask_overwrite = old_ask_overwrite;
896 if (return_status != FILE_CONT)
897 goto ret;
899 copy_done = TRUE;
901 file_progress_show_source (ctx, NULL);
902 file_progress_show (ctx, 0, 0, "", FALSE);
904 return_status = check_progress_buttons (ctx);
905 if (return_status != FILE_CONT)
906 goto ret;
907 mc_refresh ();
909 retry_src_remove:
910 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
912 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
913 if (return_status == FILE_RETRY)
914 goto retry_src_remove;
915 if (return_status == FILE_SKIPALL)
916 ctx->skip_all = TRUE;
917 goto ret;
920 if (!copy_done)
921 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
923 ret:
924 vfs_path_free (src_vpath);
925 vfs_path_free (dst_vpath);
927 return return_status;
930 /* }}} */
932 /* --------------------------------------------------------------------------------------------- */
933 /* {{{ Erase routines */
934 /** Don't update progress status if progress_count==NULL */
936 static FileProgressStatus
937 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * vpath)
939 int return_status;
940 struct stat buf;
941 char *s;
943 s = vfs_path_to_str (vpath);
944 file_progress_show_deleting (ctx, s);
945 if (check_progress_buttons (ctx) == FILE_ABORT)
947 g_free (s);
948 return FILE_ABORT;
950 mc_refresh ();
952 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
954 /* ignore, most likely the mc_unlink fails, too */
955 buf.st_size = 0;
958 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
960 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
961 if (return_status == FILE_ABORT)
963 g_free (s);
964 return return_status;
966 if (return_status == FILE_RETRY)
967 continue;
968 if (return_status == FILE_SKIPALL)
969 ctx->skip_all = TRUE;
970 break;
972 g_free (s);
973 if (tctx->progress_count == 0)
974 return FILE_CONT;
975 return progress_update_one (tctx, ctx, buf.st_size);
978 /* --------------------------------------------------------------------------------------------- */
981 Recursive remove of files
982 abort->cancel stack
983 skip ->warn every level, gets default
984 skipall->remove as much as possible
986 static FileProgressStatus
987 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
989 struct dirent *next;
990 struct stat buf;
991 DIR *reading;
992 char *path;
993 FileProgressStatus return_status = FILE_CONT;
994 vfs_path_t *vpath;
996 if (strcmp (s, "..") == 0)
997 return FILE_RETRY;
999 vpath = vfs_path_from_str (s);
1000 reading = mc_opendir (vpath);
1002 if (reading == NULL)
1004 return_status = FILE_RETRY;
1005 goto ret;
1008 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1010 vfs_path_t *tmp_vpath;
1012 if (!strcmp (next->d_name, "."))
1013 continue;
1014 if (!strcmp (next->d_name, ".."))
1015 continue;
1016 path = mc_build_filename (s, next->d_name, NULL);
1017 tmp_vpath = vfs_path_from_str (path);
1018 if (mc_lstat (tmp_vpath, &buf) != 0)
1020 g_free (path);
1021 mc_closedir (reading);
1022 vfs_path_free (tmp_vpath);
1023 return_status = FILE_RETRY;
1024 goto ret;
1026 if (S_ISDIR (buf.st_mode))
1027 return_status = recursive_erase (tctx, ctx, path);
1028 else
1029 return_status = erase_file (tctx, ctx, tmp_vpath);
1030 vfs_path_free (tmp_vpath);
1031 g_free (path);
1033 mc_closedir (reading);
1034 if (return_status == FILE_ABORT)
1035 goto ret;
1037 file_progress_show_deleting (ctx, s);
1038 if (check_progress_buttons (ctx) == FILE_ABORT)
1040 return_status = FILE_ABORT;
1041 goto ret;
1043 mc_refresh ();
1045 while (my_rmdir (s) != 0 && !ctx->skip_all)
1047 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1048 if (return_status == FILE_RETRY)
1049 continue;
1050 if (return_status == FILE_ABORT)
1051 goto ret;
1052 if (return_status == FILE_SKIPALL)
1053 ctx->skip_all = TRUE;
1054 break;
1057 ret:
1058 vfs_path_free (vpath);
1059 return return_status;
1062 /* --------------------------------------------------------------------------------------------- */
1063 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1064 in the directory path points to, 0 else. */
1066 static int
1067 check_dir_is_empty (const vfs_path_t * vpath)
1069 DIR *dir;
1070 struct dirent *d;
1071 int i;
1073 dir = mc_opendir (vpath);
1074 if (!dir)
1075 return -1;
1077 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1079 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1080 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1081 continue; /* "." or ".." */
1082 i = 0;
1083 break;
1086 mc_closedir (dir);
1087 return i;
1090 /* --------------------------------------------------------------------------------------------- */
1092 static FileProgressStatus
1093 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1095 FileProgressStatus error;
1096 vfs_path_t *s_vpath;
1098 if (strcmp (s, "..") == 0)
1099 return FILE_SKIP;
1101 if (strcmp (s, ".") == 0)
1102 return FILE_SKIP;
1104 file_progress_show_deleting (ctx, s);
1105 if (check_progress_buttons (ctx) == FILE_ABORT)
1106 return FILE_ABORT;
1108 mc_refresh ();
1110 s_vpath = vfs_path_from_str (s);
1112 if (check_dir_is_empty (s_vpath) == 1) /* not empty or error */
1114 while (my_rmdir (s) != 0 && !ctx->skip_all)
1116 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1117 if (error == FILE_SKIPALL)
1118 ctx->skip_all = TRUE;
1119 if (error != FILE_RETRY)
1121 vfs_path_free (s_vpath);
1122 return error;
1127 vfs_path_free (s_vpath);
1128 return FILE_CONT;
1131 /* }}} */
1133 /* --------------------------------------------------------------------------------------------- */
1134 /* {{{ Panel operate routines */
1137 * Return currently selected entry name or the name of the first marked
1138 * entry if there is one.
1141 static char *
1142 panel_get_file (WPanel * panel)
1144 if (get_current_type () == view_tree)
1146 WTree *tree;
1148 tree = (WTree *) get_panel_widget (get_current_index ());
1149 return vfs_path_to_str (tree_selected_name (tree));
1152 if (panel->marked != 0)
1154 int i;
1156 for (i = 0; i < panel->count; i++)
1157 if (panel->dir.list[i].f.marked)
1158 return g_strdup (panel->dir.list[i].fname);
1160 return g_strdup (panel->dir.list[panel->selected].fname);
1163 /* --------------------------------------------------------------------------------------------- */
1165 * panel_compute_totals:
1167 * compute the number of files and the number of bytes
1168 * used up by the whole selection, recursing directories
1169 * as required. In addition, it checks to see if it will
1170 * overwrite any files by doing the copy.
1173 static FileProgressStatus
1174 panel_compute_totals (const WPanel * panel, const void *ui,
1175 compute_dir_size_callback cback,
1176 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1178 int i;
1180 *ret_marked = 0;
1181 *ret_total = 0;
1183 for (i = 0; i < panel->count; i++)
1185 struct stat *s;
1187 if (!panel->dir.list[i].f.marked)
1188 continue;
1190 s = &panel->dir.list[i].st;
1192 if (S_ISDIR (s->st_mode))
1194 vfs_path_t *p;
1195 size_t subdir_count = 0;
1196 uintmax_t subdir_bytes = 0;
1197 FileProgressStatus status;
1199 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, NULL);
1200 status =
1201 compute_dir_size (p, ui, cback, &subdir_count, &subdir_bytes, compute_symlinks);
1202 vfs_path_free (p);
1204 if (status != FILE_CONT)
1205 return FILE_ABORT;
1207 *ret_marked += subdir_count;
1208 *ret_total += subdir_bytes;
1210 else
1212 (*ret_marked)++;
1213 *ret_total += (uintmax_t) s->st_size;
1217 return FILE_CONT;
1220 /* --------------------------------------------------------------------------------------------- */
1222 /** Initialize variables for progress bars */
1223 static FileProgressStatus
1224 panel_operate_init_totals (FileOperation operation,
1225 const WPanel * panel, const char *source, FileOpContext * ctx)
1227 FileProgressStatus status;
1229 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1231 ComputeDirSizeUI *ui;
1233 ui = compute_dir_size_create_ui ();
1235 if (source == NULL)
1236 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1237 &ctx->progress_count, &ctx->progress_bytes,
1238 ctx->follow_links);
1239 else
1241 vfs_path_t *p;
1243 p = vfs_path_from_str (source);
1244 status = compute_dir_size (p, ui, compute_dir_size_update_ui,
1245 &ctx->progress_count, &ctx->progress_bytes,
1246 ctx->follow_links);
1247 vfs_path_free (p);
1250 compute_dir_size_destroy_ui (ui);
1252 ctx->progress_totals_computed = (status == FILE_CONT);
1254 else
1256 status = FILE_CONT;
1257 ctx->progress_count = panel->marked;
1258 ctx->progress_bytes = panel->total;
1259 ctx->progress_totals_computed = FALSE;
1262 return status;
1265 /* --------------------------------------------------------------------------------------------- */
1267 * Generate user prompt for panel operation.
1268 * single_source is the name if the source entry or NULL for multiple
1269 * entries.
1270 * src_stat is only used when single_source is not NULL.
1273 static char *
1274 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1275 gboolean single_source, const struct stat *src_stat)
1277 const char *sp, *cp;
1278 char format_string[BUF_MEDIUM];
1279 char *dp = format_string;
1280 gboolean build_question = FALSE;
1282 static gboolean i18n_flag = FALSE;
1283 if (!i18n_flag)
1285 size_t i;
1287 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1288 op_names1[i] = Q_ (op_names1[i]);
1290 #ifdef ENABLE_NLS
1291 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1292 prompt_parts[i] = _(prompt_parts[i]);
1294 one_format = _(one_format);
1295 many_format = _(many_format);
1296 question_format = _(question_format);
1297 #endif /* ENABLE_NLS */
1298 i18n_flag = TRUE;
1301 sp = single_source ? one_format : many_format;
1303 while (*sp != '\0')
1305 switch (*sp)
1307 case '%':
1308 cp = NULL;
1309 switch (sp[1])
1311 case 'o':
1312 cp = op_names1[operation];
1313 break;
1314 case 'm':
1315 if (operation == OP_DELETE)
1317 cp = "";
1318 build_question = TRUE;
1320 else
1321 cp = prompt_parts[5];
1322 break;
1323 case 'e':
1324 if (operation == OP_DELETE)
1326 cp = "";
1327 build_question = TRUE;
1329 else
1330 cp = prompt_parts[6];
1331 break;
1332 case 'f':
1333 if (single_source)
1334 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1335 else
1336 cp = (panel->marked == panel->dirs_marked)
1337 ? prompt_parts[3]
1338 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1339 break;
1340 default:
1341 *dp++ = *sp++;
1344 if (cp != NULL)
1346 sp += 2;
1348 while (*cp != '\0')
1349 *dp++ = *cp++;
1351 /* form two-lines query prompt for file deletion */
1352 if (operation == OP_DELETE && sp[-1] == 'f')
1354 *dp++ = '\n';
1356 while (isblank (*sp) != 0)
1357 sp++;
1360 break;
1361 default:
1362 *dp++ = *sp++;
1365 *dp = '\0';
1367 if (build_question)
1369 char tmp[BUF_MEDIUM];
1371 memmove (tmp, format_string, sizeof (tmp));
1372 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1375 return g_strdup (format_string);
1378 /* --------------------------------------------------------------------------------------------- */
1380 #ifdef ENABLE_BACKGROUND
1381 static int
1382 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1384 int pid = ctx->pid;
1386 (void) mode;
1387 ctx->pid = 0;
1389 unregister_task_with_pid (pid);
1390 /* file_op_context_destroy(ctx); */
1391 return 1;
1393 #endif
1394 /* }}} */
1396 /* --------------------------------------------------------------------------------------------- */
1397 /*** public functions ****************************************************************************/
1398 /* --------------------------------------------------------------------------------------------- */
1400 FileProgressStatus
1401 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1402 const char *src_path, const char *dst_path)
1404 uid_t src_uid = (uid_t) (-1);
1405 gid_t src_gid = (gid_t) (-1);
1407 int src_desc, dest_desc = -1;
1408 int n_read, n_written;
1409 mode_t src_mode = 0; /* The mode of the source file */
1410 struct stat sb, sb2;
1411 struct utimbuf utb;
1412 gboolean dst_exists = FALSE, appending = FALSE;
1413 off_t file_size = -1;
1414 FileProgressStatus return_status, temp_status;
1415 struct timeval tv_transfer_start;
1416 dest_status_t dst_status = DEST_NONE;
1417 int open_flags;
1418 gboolean is_first_time = TRUE;
1419 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1420 gboolean write_errno_nospace = FALSE;
1422 /* FIXME: We should not be using global variables! */
1423 ctx->do_reget = 0;
1424 return_status = FILE_RETRY;
1426 dst_vpath = vfs_path_from_str (dst_path);
1427 src_vpath = vfs_path_from_str (src_path);
1429 file_progress_show_source (ctx, src_vpath);
1430 file_progress_show_target (ctx, dst_vpath);
1432 if (check_progress_buttons (ctx) == FILE_ABORT)
1434 return_status = FILE_ABORT;
1435 goto ret_fast;
1438 mc_refresh ();
1440 while (mc_stat (dst_vpath, &sb2) == 0)
1442 if (S_ISDIR (sb2.st_mode))
1444 if (ctx->skip_all)
1445 return_status = FILE_SKIPALL;
1446 else
1448 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1449 if (return_status == FILE_SKIPALL)
1450 ctx->skip_all = TRUE;
1451 if (return_status == FILE_RETRY)
1452 continue;
1454 goto ret_fast;
1457 dst_exists = TRUE;
1458 break;
1461 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1463 if (ctx->skip_all)
1464 return_status = FILE_SKIPALL;
1465 else
1467 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1468 if (return_status == FILE_SKIPALL)
1469 ctx->skip_all = TRUE;
1472 if (return_status != FILE_RETRY)
1473 goto ret_fast;
1476 if (dst_exists)
1478 /* Destination already exists */
1479 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1481 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1482 src_path, dst_path);
1483 goto ret_fast;
1486 /* Should we replace destination? */
1487 if (tctx->ask_overwrite)
1489 ctx->do_reget = 0;
1490 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1491 if (return_status != FILE_CONT)
1492 goto ret_fast;
1496 if (!ctx->do_append)
1498 /* Check the hardlinks */
1499 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &sb))
1501 /* We have made a hardlink - no more processing is necessary */
1502 return_status = FILE_CONT;
1503 goto ret_fast;
1506 if (S_ISLNK (sb.st_mode))
1508 return_status = make_symlink (ctx, src_path, dst_path);
1509 goto ret_fast;
1512 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1513 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1515 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1516 && !ctx->skip_all)
1518 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1519 if (return_status == FILE_RETRY)
1520 continue;
1521 if (return_status == FILE_SKIPALL)
1522 ctx->skip_all = TRUE;
1523 goto ret_fast;
1525 /* Success */
1527 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1528 && !ctx->skip_all)
1530 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1531 if (temp_status == FILE_SKIP)
1532 break;
1533 if (temp_status == FILE_SKIPALL)
1534 ctx->skip_all = TRUE;
1535 if (temp_status != FILE_RETRY)
1537 return_status = temp_status;
1538 goto ret_fast;
1542 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1543 && !ctx->skip_all)
1545 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1546 if (temp_status == FILE_SKIP)
1547 break;
1548 if (temp_status == FILE_SKIPALL)
1549 ctx->skip_all = TRUE;
1550 if (temp_status != FILE_RETRY)
1552 return_status = temp_status;
1553 goto ret_fast;
1557 return_status = FILE_CONT;
1558 goto ret_fast;
1562 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1564 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1566 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1567 if (return_status == FILE_RETRY)
1568 continue;
1569 if (return_status == FILE_SKIPALL)
1570 ctx->skip_all = TRUE;
1571 if (return_status == FILE_SKIP)
1572 break;
1573 ctx->do_append = 0;
1574 goto ret_fast;
1577 if (ctx->do_reget != 0)
1579 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1581 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1582 ctx->do_reget = 0;
1583 ctx->do_append = FALSE;
1587 while (mc_fstat (src_desc, &sb) != 0)
1589 if (ctx->skip_all)
1590 return_status = FILE_SKIPALL;
1591 else
1593 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1594 if (return_status == FILE_RETRY)
1595 continue;
1596 if (return_status == FILE_SKIPALL)
1597 ctx->skip_all = TRUE;
1598 ctx->do_append = FALSE;
1600 goto ret;
1603 src_mode = sb.st_mode;
1604 src_uid = sb.st_uid;
1605 src_gid = sb.st_gid;
1606 utb.actime = sb.st_atime;
1607 utb.modtime = sb.st_mtime;
1608 file_size = sb.st_size;
1610 open_flags = O_WRONLY;
1611 if (dst_exists)
1613 if (ctx->do_append != 0)
1614 open_flags |= O_APPEND;
1615 else
1616 open_flags |= O_CREAT | O_TRUNC;
1618 else
1620 open_flags |= O_CREAT | O_EXCL;
1623 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
1625 if (errno != EEXIST)
1627 if (ctx->skip_all)
1628 return_status = FILE_SKIPALL;
1629 else
1631 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1632 if (return_status == FILE_RETRY)
1633 continue;
1634 if (return_status == FILE_SKIPALL)
1635 ctx->skip_all = TRUE;
1636 ctx->do_append = FALSE;
1639 goto ret;
1641 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1643 appending = ctx->do_append;
1644 ctx->do_append = FALSE;
1646 /* Find out the optimal buffer size. */
1647 while (mc_fstat (dest_desc, &sb) != 0)
1649 if (ctx->skip_all)
1650 return_status = FILE_SKIPALL;
1651 else
1653 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1654 if (return_status == FILE_RETRY)
1655 continue;
1656 if (return_status == FILE_SKIPALL)
1657 ctx->skip_all = TRUE;
1659 goto ret;
1662 while (TRUE)
1664 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1665 if (errno == 0)
1666 break;
1668 if (ctx->skip_all)
1669 return_status = FILE_SKIPALL;
1670 else
1672 return_status =
1673 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1674 if (return_status == FILE_RETRY)
1675 continue;
1676 if (return_status == FILE_SKIPALL)
1677 ctx->skip_all = TRUE;
1679 mc_close (dest_desc);
1680 dest_desc = -1;
1681 mc_unlink (dst_vpath);
1682 dst_status = DEST_NONE;
1683 goto ret;
1686 ctx->eta_secs = 0.0;
1687 ctx->bps = 0;
1689 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1690 file_progress_show (ctx, 0, file_size, "", TRUE);
1691 else
1692 file_progress_show (ctx, 1, 1, "", TRUE);
1693 return_status = check_progress_buttons (ctx);
1694 mc_refresh ();
1696 if (return_status != FILE_CONT)
1697 goto ret;
1700 off_t n_read_total = 0;
1701 struct timeval tv_current, tv_last_update, tv_last_input;
1702 int secs, update_secs;
1703 const char *stalled_msg = "";
1705 tv_last_update = tv_transfer_start;
1707 while (TRUE)
1709 char buf[BUF_8K];
1711 /* src_read */
1712 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1713 n_read = -1;
1714 else
1715 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1717 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1718 if (return_status == FILE_RETRY)
1719 continue;
1720 if (return_status == FILE_SKIPALL)
1721 ctx->skip_all = TRUE;
1722 goto ret;
1724 if (n_read == 0)
1725 break;
1727 gettimeofday (&tv_current, NULL);
1729 if (n_read > 0)
1731 char *t = buf;
1732 n_read_total += n_read;
1734 /* Windows NT ftp servers report that files have no
1735 * permissions: -------, so if we happen to have actually
1736 * read something, we should fix the permissions.
1738 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1739 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1740 gettimeofday (&tv_last_input, NULL);
1742 /* dst_write */
1743 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read)
1745 if (n_written > 0)
1747 n_read -= n_written;
1748 t += n_written;
1749 continue;
1752 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
1754 if (ctx->skip_all)
1755 return_status = FILE_SKIPALL;
1756 else
1757 return_status =
1758 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1760 if (return_status == FILE_SKIP)
1762 if (write_errno_nospace)
1763 goto ret;
1764 break;
1766 if (return_status == FILE_SKIPALL)
1768 ctx->skip_all = TRUE;
1769 if (write_errno_nospace)
1770 goto ret;
1772 if (return_status != FILE_RETRY)
1773 goto ret;
1775 /* User pressed "Retry". Will the next mc_write() call be succesful?
1776 * Reset error flag to be ready for that. */
1777 write_errno_nospace = FALSE;
1781 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1783 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1784 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1786 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1788 copy_file_file_display_progress (tctx, ctx,
1789 tv_current,
1790 tv_transfer_start, file_size, n_read_total);
1791 tv_last_update = tv_current;
1793 is_first_time = FALSE;
1795 if (update_secs > FILEOP_STALLING_INTERVAL)
1797 stalled_msg = _("(stalled)");
1801 gboolean force_update;
1803 force_update =
1804 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1806 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1808 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1809 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1812 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1813 force_update);
1815 mc_refresh ();
1817 return_status = check_progress_buttons (ctx);
1819 if (return_status != FILE_CONT)
1821 mc_refresh ();
1822 goto ret;
1827 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1829 ret:
1830 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1832 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1833 if (temp_status == FILE_RETRY)
1834 continue;
1835 if (temp_status == FILE_ABORT)
1836 return_status = temp_status;
1837 if (temp_status == FILE_SKIPALL)
1838 ctx->skip_all = TRUE;
1839 break;
1842 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1844 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1845 if (temp_status == FILE_RETRY)
1846 continue;
1847 if (temp_status == FILE_SKIPALL)
1848 ctx->skip_all = TRUE;
1849 return_status = temp_status;
1850 break;
1853 if (dst_status == DEST_SHORT)
1855 /* Remove short file */
1856 int result = 0;
1858 /* In case of copy/move to full partition, keep source file
1859 * and remove incomplete destination one */
1860 if (!write_errno_nospace)
1861 result = query_dialog (Q_ ("DialogTitle|Copy"),
1862 _("Incomplete file was retrieved. Keep it?"),
1863 D_ERROR, 2, _("&Delete"), _("&Keep"));
1864 if (result == 0)
1865 mc_unlink (dst_vpath);
1867 else if (dst_status == DEST_FULL)
1869 /* Copy has succeeded */
1870 if (!appending && ctx->preserve_uidgid)
1872 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1874 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1875 if (temp_status == FILE_RETRY)
1876 continue;
1877 if (temp_status == FILE_SKIPALL)
1879 ctx->skip_all = TRUE;
1880 return_status = FILE_CONT;
1882 if (temp_status == FILE_SKIP)
1883 return_status = FILE_CONT;
1884 break;
1888 if (!appending)
1890 if (ctx->preserve)
1892 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1894 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1895 if (temp_status == FILE_RETRY)
1896 continue;
1897 if (temp_status == FILE_SKIPALL)
1899 ctx->skip_all = TRUE;
1900 return_status = FILE_CONT;
1902 if (temp_status == FILE_SKIP)
1903 return_status = FILE_CONT;
1904 break;
1907 else if (!dst_exists)
1909 src_mode = umask (-1);
1910 umask (src_mode);
1911 src_mode = 0100666 & ~src_mode;
1912 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
1914 mc_utime (dst_vpath, &utb);
1918 if (return_status == FILE_CONT)
1919 return_status = progress_update_one (tctx, ctx, file_size);
1921 ret_fast:
1922 vfs_path_free (src_vpath);
1923 vfs_path_free (dst_vpath);
1924 return return_status;
1927 /* --------------------------------------------------------------------------------------------- */
1929 * I think these copy_*_* functions should have a return type.
1930 * anyway, this function *must* have two directories as arguments.
1932 /* FIXME: This function needs to check the return values of the
1933 function calls */
1935 FileProgressStatus
1936 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1937 gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs)
1939 struct dirent *next;
1940 struct stat buf, cbuf;
1941 DIR *reading;
1942 char *dest_dir = NULL;
1943 FileProgressStatus return_status = FILE_CONT;
1944 struct utimbuf utb;
1945 struct link *lp;
1946 char *d;
1947 vfs_path_t *src_vpath, *dst_vpath, *dest_dir_vpath = NULL;
1948 gboolean do_mkdir = TRUE;
1950 d = g_strdup (_d);
1952 src_vpath = vfs_path_from_str (s);
1953 dst_vpath = vfs_path_from_str (_d);
1955 /* First get the mode of the source dir */
1957 retry_src_stat:
1958 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
1960 if (ctx->skip_all)
1961 return_status = FILE_SKIPALL;
1962 else
1964 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1965 if (return_status == FILE_RETRY)
1966 goto retry_src_stat;
1967 if (return_status == FILE_SKIPALL)
1968 ctx->skip_all = TRUE;
1970 goto ret_fast;
1973 if (is_in_linklist (dest_dirs, src_vpath, &cbuf))
1975 /* Don't copy a directory we created before (we don't want to copy
1976 infinitely if a directory is copied into itself) */
1977 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1978 return_status = FILE_CONT;
1979 goto ret_fast;
1982 /* Hmm, hardlink to directory??? - Norbert */
1983 /* FIXME: In this step we should do something
1984 in case the destination already exist */
1985 /* Check the hardlinks */
1986 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &cbuf))
1988 /* We have made a hardlink - no more processing is necessary */
1989 goto ret_fast;
1992 if (!S_ISDIR (cbuf.st_mode))
1994 if (ctx->skip_all)
1995 return_status = FILE_SKIPALL;
1996 else
1998 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1999 if (return_status == FILE_RETRY)
2000 goto retry_src_stat;
2001 if (return_status == FILE_SKIPALL)
2002 ctx->skip_all = TRUE;
2004 goto ret_fast;
2007 if (is_in_linklist (parent_dirs, src_vpath, &cbuf))
2009 /* we found a cyclic symbolic link */
2010 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
2011 return_status = FILE_SKIP;
2012 goto ret_fast;
2015 lp = g_new0 (struct link, 1);
2016 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
2017 lp->ino = cbuf.st_ino;
2018 lp->dev = cbuf.st_dev;
2019 parent_dirs = g_slist_prepend (parent_dirs, lp);
2021 retry_dst_stat:
2022 /* Now, check if the dest dir exists, if not, create it. */
2023 if (mc_stat (dst_vpath, &buf) != 0)
2025 /* Here the dir doesn't exist : make it ! */
2026 if (move_over)
2028 if (mc_rename (src_vpath, dst_vpath) == 0)
2030 return_status = FILE_CONT;
2031 goto ret;
2034 dest_dir = d;
2035 d = NULL;
2037 else
2040 * If the destination directory exists, we want to copy the whole
2041 * directory, but we only want this to happen once.
2043 * Escape sequences added to the * to compiler warnings.
2044 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2045 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2047 if (!S_ISDIR (buf.st_mode))
2049 if (ctx->skip_all)
2050 return_status = FILE_SKIPALL;
2051 else
2053 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2054 if (return_status == FILE_SKIPALL)
2055 ctx->skip_all = TRUE;
2056 if (return_status == FILE_RETRY)
2057 goto retry_dst_stat;
2059 goto ret;
2061 /* Dive into subdir if exists */
2062 if (toplevel && ctx->dive_into_subdirs)
2063 dest_dir = mc_build_filename (d, x_basename (s), NULL);
2064 else
2066 dest_dir = d;
2067 d = NULL;
2068 do_mkdir = FALSE;
2072 dest_dir_vpath = vfs_path_from_str (dest_dir);
2074 if (do_mkdir)
2076 while (my_mkdir (dest_dir_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2078 if (ctx->skip_all)
2079 return_status = FILE_SKIPALL;
2080 else
2082 return_status =
2083 file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
2084 if (return_status == FILE_SKIPALL)
2085 ctx->skip_all = TRUE;
2087 if (return_status != FILE_RETRY)
2088 goto ret;
2091 lp = g_new0 (struct link, 1);
2092 mc_stat (dest_dir_vpath, &buf);
2093 lp->vfs = vfs_path_get_by_index (dest_dir_vpath, -1)->class;
2094 lp->ino = buf.st_ino;
2095 lp->dev = buf.st_dev;
2096 dest_dirs = g_slist_prepend (dest_dirs, lp);
2099 if (ctx->preserve_uidgid)
2101 while (mc_chown (dest_dir_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2103 if (ctx->skip_all)
2104 return_status = FILE_SKIPALL;
2105 else
2107 return_status =
2108 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
2109 if (return_status == FILE_SKIPALL)
2110 ctx->skip_all = TRUE;
2112 if (return_status != FILE_RETRY)
2113 goto ret;
2117 /* open the source dir for reading */
2118 reading = mc_opendir (src_vpath);
2119 if (reading == NULL)
2120 goto ret;
2122 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2124 char *path;
2125 vfs_path_t *tmp_vpath;
2127 * Now, we don't want '.' and '..' to be created / copied at any time
2129 if (!strcmp (next->d_name, "."))
2130 continue;
2131 if (!strcmp (next->d_name, ".."))
2132 continue;
2134 /* get the filename and add it to the src directory */
2135 path = mc_build_filename (s, next->d_name, NULL);
2136 tmp_vpath = vfs_path_from_str (path);
2138 (*ctx->stat_func) (tmp_vpath, &buf);
2139 if (S_ISDIR (buf.st_mode))
2141 char *mdpath;
2143 mdpath = mc_build_filename (dest_dir, next->d_name, NULL);
2145 * From here, we just intend to recursively copy subdirs, not
2146 * the double functionality of copying different when the target
2147 * dir already exists. So, we give the recursive call the flag 0
2148 * meaning no toplevel.
2150 return_status =
2151 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2152 g_free (mdpath);
2154 else
2156 char *dest_file;
2158 dest_file = mc_build_filename (dest_dir, x_basename (path), NULL);
2159 return_status = copy_file_file (tctx, ctx, path, dest_file);
2160 g_free (dest_file);
2162 if (do_delete && return_status == FILE_CONT)
2164 if (ctx->erase_at_end)
2166 lp = g_new0 (struct link, 1);
2167 lp->src_vpath = vfs_path_clone (tmp_vpath);
2168 lp->st_mode = buf.st_mode;
2169 erase_list = g_slist_append (erase_list, lp);
2171 else if (S_ISDIR (buf.st_mode))
2172 return_status = erase_dir_iff_empty (ctx, path);
2173 else
2174 return_status = erase_file (tctx, ctx, tmp_vpath);
2176 g_free (path);
2177 vfs_path_free (tmp_vpath);
2179 mc_closedir (reading);
2181 if (ctx->preserve)
2183 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2184 utb.actime = cbuf.st_atime;
2185 utb.modtime = cbuf.st_mtime;
2186 mc_utime (dest_dir_vpath, &utb);
2188 else
2190 cbuf.st_mode = umask (-1);
2191 umask (cbuf.st_mode);
2192 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2193 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2196 ret:
2197 g_free (dest_dir);
2198 vfs_path_free (dest_dir_vpath);
2199 free_link (parent_dirs->data);
2200 g_slist_free_1 (parent_dirs);
2201 ret_fast:
2202 g_free (d);
2203 vfs_path_free (src_vpath);
2204 vfs_path_free (dst_vpath);
2205 return return_status;
2208 /* }}} */
2210 /* --------------------------------------------------------------------------------------------- */
2211 /* {{{ Move routines */
2213 FileProgressStatus
2214 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2216 struct stat sbuf, dbuf, destbuf;
2217 struct link *lp;
2218 char *destdir;
2219 FileProgressStatus return_status;
2220 gboolean move_over = FALSE;
2221 gboolean dstat_ok;
2222 vfs_path_t *src_vpath, *dst_vpath, *destdir_vpath;
2224 src_vpath = vfs_path_from_str (s);
2225 dst_vpath = vfs_path_from_str (d);
2227 file_progress_show_source (ctx, src_vpath);
2228 file_progress_show_target (ctx, dst_vpath);
2230 if (check_progress_buttons (ctx) == FILE_ABORT)
2232 return_status = FILE_ABORT;
2233 goto ret_fast;
2236 mc_refresh ();
2238 mc_stat (src_vpath, &sbuf);
2240 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2241 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2243 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2244 goto ret_fast;
2247 if (!dstat_ok)
2248 destdir = g_strdup (d); /* destination doesn't exist */
2249 else if (!ctx->dive_into_subdirs)
2251 destdir = g_strdup (d);
2252 move_over = TRUE;
2254 else
2255 destdir = mc_build_filename (d, x_basename (s), NULL);
2257 destdir_vpath = vfs_path_from_str (destdir);
2259 /* Check if the user inputted an existing dir */
2260 retry_dst_stat:
2261 if (mc_stat (destdir_vpath, &destbuf) == 0)
2263 if (move_over)
2265 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2267 if (return_status != FILE_CONT)
2268 goto ret;
2269 goto oktoret;
2271 else if (ctx->skip_all)
2272 return_status = FILE_SKIPALL;
2273 else
2275 if (S_ISDIR (destbuf.st_mode))
2276 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2277 else
2278 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2279 if (return_status == FILE_SKIPALL)
2280 ctx->skip_all = TRUE;
2281 if (return_status == FILE_RETRY)
2282 goto retry_dst_stat;
2285 g_free (destdir);
2286 vfs_path_free (destdir_vpath);
2287 goto ret_fast;
2290 retry_rename:
2291 if (mc_rename (src_vpath, destdir_vpath) == 0)
2293 return_status = FILE_CONT;
2294 goto ret;
2297 if (errno != EXDEV)
2299 if (!ctx->skip_all)
2301 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2302 if (return_status == FILE_SKIPALL)
2303 ctx->skip_all = TRUE;
2304 if (return_status == FILE_RETRY)
2305 goto retry_rename;
2307 goto ret;
2309 /* Failed because of filesystem boundary -> copy dir instead */
2310 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2312 if (return_status != FILE_CONT)
2313 goto ret;
2314 oktoret:
2315 file_progress_show_source (ctx, NULL);
2316 file_progress_show (ctx, 0, 0, "", FALSE);
2318 return_status = check_progress_buttons (ctx);
2319 if (return_status != FILE_CONT)
2320 goto ret;
2322 mc_refresh ();
2323 if (ctx->erase_at_end)
2325 for (; erase_list != NULL && return_status != FILE_ABORT;)
2327 lp = (struct link *) erase_list->data;
2329 if (S_ISDIR (lp->st_mode))
2331 char *src_path;
2333 src_path = vfs_path_to_str (lp->src_vpath);
2334 return_status = erase_dir_iff_empty (ctx, src_path);
2335 g_free (src_path);
2337 else
2338 return_status = erase_file (tctx, ctx, lp->src_vpath);
2340 erase_list = g_slist_remove (erase_list, lp);
2341 free_link (lp);
2344 erase_dir_iff_empty (ctx, s);
2346 ret:
2347 g_free (destdir);
2348 vfs_path_free (destdir_vpath);
2349 erase_list = free_linklist (erase_list);
2350 ret_fast:
2351 vfs_path_free (src_vpath);
2352 vfs_path_free (dst_vpath);
2353 return return_status;
2356 /* }}} */
2358 /* --------------------------------------------------------------------------------------------- */
2359 /* {{{ Erase routines */
2361 FileProgressStatus
2362 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * s_vpath)
2364 FileProgressStatus error;
2365 char *s;
2367 s = vfs_path_to_str (s_vpath);
2370 if (strcmp (s, "..") == 0)
2371 return FILE_SKIP;
2373 if (strcmp (s, ".") == 0)
2374 return FILE_SKIP;
2377 file_progress_show_deleting (ctx, s);
2378 if (check_progress_buttons (ctx) == FILE_ABORT)
2380 g_free (s);
2381 return FILE_ABORT;
2383 mc_refresh ();
2385 /* The old way to detect a non empty directory was:
2386 error = my_rmdir (s);
2387 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2388 For the linux user space nfs server (nfs-server-2.2beta29-2)
2389 we would have to check also for EIO. I hope the new way is
2390 fool proof. (Norbert)
2392 error = check_dir_is_empty (s_vpath);
2393 if (error == 0)
2394 { /* not empty */
2395 error = query_recursive (ctx, s);
2396 if (error == FILE_CONT)
2397 error = recursive_erase (tctx, ctx, s);
2398 g_free (s);
2399 return error;
2402 while (my_rmdir (s) == -1 && !ctx->skip_all)
2404 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2405 if (error != FILE_RETRY)
2407 g_free (s);
2408 return error;
2412 g_free (s);
2413 return FILE_CONT;
2416 /* }}} */
2418 /* --------------------------------------------------------------------------------------------- */
2419 /* {{{ Panel operate routines */
2421 ComputeDirSizeUI *
2422 compute_dir_size_create_ui (void)
2424 ComputeDirSizeUI *ui;
2426 const char *b_name = N_("&Abort");
2428 #ifdef ENABLE_NLS
2429 b_name = _(b_name);
2430 #endif
2432 ui = g_new (ComputeDirSizeUI, 1);
2434 ui->dlg = create_dlg (TRUE, 0, 0, 7, COLS / 2, dialog_colors, NULL, NULL,
2435 NULL, _("Directory scanning"), DLG_CENTER);
2436 ui->dirname = label_new (2, 3, "");
2437 add_widget (ui->dlg, ui->dirname);
2438 add_widget (ui->dlg, hline_new (3, -1, -1));
2439 add_widget_autopos (ui->dlg,
2440 button_new (4, 2, FILE_ABORT, NORMAL_BUTTON, b_name, NULL),
2441 WPOS_KEEP_TOP | WPOS_CENTER_HORZ, NULL);
2443 /* We will manage the dialog without any help,
2444 that's why we have to call init_dlg */
2445 init_dlg (ui->dlg);
2447 return ui;
2450 /* --------------------------------------------------------------------------------------------- */
2452 void
2453 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2455 if (ui != NULL)
2457 /* schedule to update passive panel */
2458 other_panel->dirty = 1;
2460 /* close and destroy dialog */
2461 dlg_run_done (ui->dlg);
2462 destroy_dlg (ui->dlg);
2463 g_free (ui);
2467 /* --------------------------------------------------------------------------------------------- */
2469 FileProgressStatus
2470 compute_dir_size_update_ui (const void *ui, const vfs_path_t * dirname_vpath)
2472 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2473 int c;
2474 Gpm_Event event;
2475 char *dirname;
2477 if (ui == NULL)
2478 return FILE_CONT;
2480 dirname = vfs_path_to_str (dirname_vpath);
2481 label_set_text (this->dirname, str_trunc (dirname, WIDGET (this->dlg)->cols - 6));
2482 g_free (dirname);
2484 event.x = -1; /* Don't show the GPM cursor */
2485 c = tty_get_event (&event, FALSE, FALSE);
2486 if (c == EV_NONE)
2487 return FILE_CONT;
2489 /* Reinitialize to avoid old values after events other than
2490 selecting a button */
2491 this->dlg->ret_value = FILE_CONT;
2493 dlg_process_event (this->dlg, c, &event);
2495 switch (this->dlg->ret_value)
2497 case B_CANCEL:
2498 case FILE_ABORT:
2499 return FILE_ABORT;
2500 default:
2501 return FILE_CONT;
2505 /* --------------------------------------------------------------------------------------------- */
2507 * compute_dir_size:
2509 * Computes the number of bytes used by the files in a directory
2512 FileProgressStatus
2513 compute_dir_size (const vfs_path_t * dirname_vpath, const void *ui,
2514 compute_dir_size_callback cback,
2515 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2517 int res;
2518 struct stat s;
2519 DIR *dir;
2520 struct dirent *dirent;
2521 FileProgressStatus ret = FILE_CONT;
2523 if (!compute_symlinks)
2525 res = mc_lstat (dirname_vpath, &s);
2526 if (res != 0)
2527 return ret;
2529 /* don't scan symlink to directory */
2530 if (S_ISLNK (s.st_mode))
2532 (*ret_marked)++;
2533 *ret_total += (uintmax_t) s.st_size;
2534 return ret;
2538 dir = mc_opendir (dirname_vpath);
2540 if (dir == NULL)
2541 return ret;
2543 while ((dirent = mc_readdir (dir)) != NULL)
2545 vfs_path_t *tmp_vpath;
2547 ret = (cback != NULL) ? cback (ui, dirname_vpath) : FILE_CONT;
2549 if (ret != FILE_CONT)
2550 break;
2552 if (strcmp (dirent->d_name, ".") == 0)
2553 continue;
2554 if (strcmp (dirent->d_name, "..") == 0)
2555 continue;
2557 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, NULL);
2558 res = mc_lstat (tmp_vpath, &s);
2559 if (res == 0)
2561 if (S_ISDIR (s.st_mode))
2563 size_t subdir_count = 0;
2564 uintmax_t subdir_bytes = 0;
2566 ret =
2567 compute_dir_size (tmp_vpath, ui, cback, &subdir_count, &subdir_bytes,
2568 compute_symlinks);
2570 if (ret != FILE_CONT)
2572 vfs_path_free (tmp_vpath);
2573 break;
2576 *ret_marked += subdir_count;
2577 *ret_total += subdir_bytes;
2579 else
2581 (*ret_marked)++;
2582 *ret_total += (uintmax_t) s.st_size;
2585 vfs_path_free (tmp_vpath);
2588 mc_closedir (dir);
2589 return ret;
2592 /* --------------------------------------------------------------------------------------------- */
2594 * panel_operate:
2596 * Performs one of the operations on the selection on the source_panel
2597 * (copy, delete, move).
2599 * Returns TRUE if did change the directory
2600 * structure, Returns FALSE if user aborted
2602 * force_single forces operation on the current entry and affects
2603 * default destination. Current filename is used as default.
2606 gboolean
2607 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2609 WPanel *panel = (WPanel *) source_panel;
2610 const gboolean single_entry = force_single || (panel->marked <= 1)
2611 || (get_current_type () == view_tree);
2613 char *source = NULL;
2614 #ifdef WITH_FULL_PATHS
2615 vfs_path_t *source_with_vpath = NULL;
2616 char *source_with_path_str = NULL;
2617 #else
2618 #define source_with_path source
2619 #endif /* !WITH_FULL_PATHS */
2620 char *dest = NULL;
2621 vfs_path_t *dest_vpath = NULL;
2622 char *temp = NULL;
2623 char *save_cwd = NULL, *save_dest = NULL;
2624 struct stat src_stat;
2625 gboolean ret_val = TRUE;
2626 int i;
2627 FileProgressStatus value;
2628 FileOpContext *ctx;
2629 FileOpTotalContext *tctx;
2630 vfs_path_t *tmp_vpath;
2632 gboolean do_bg = FALSE; /* do background operation? */
2634 static gboolean i18n_flag = FALSE;
2635 if (!i18n_flag)
2637 for (i = sizeof (op_names) / sizeof (op_names[0]); i--;)
2638 op_names[i] = Q_ (op_names[i]);
2639 i18n_flag = TRUE;
2642 linklist = free_linklist (linklist);
2643 dest_dirs = free_linklist (dest_dirs);
2645 if (single_entry)
2647 vfs_path_t *source_vpath;
2649 if (force_single)
2650 source = g_strdup (selection (panel)->fname);
2651 else
2652 source = panel_get_file (panel);
2654 if (strcmp (source, "..") == 0)
2656 g_free (source);
2657 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2658 return FALSE;
2661 source_vpath = vfs_path_from_str (source);
2662 /* Update stat to get actual info */
2663 if (mc_lstat (source_vpath, &src_stat) != 0)
2665 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2666 path_trunc (source, 30), unix_error_string (errno));
2668 /* Directory was changed outside MC. Reload it forced */
2669 if (!panel->is_panelized)
2671 panel_update_flags_t flags = UP_RELOAD;
2673 /* don't update panelized panel */
2674 if (get_other_type () == view_listing && other_panel->is_panelized)
2675 flags |= UP_ONLY_CURRENT;
2677 update_panels (flags, UP_KEEPSEL);
2679 vfs_path_free (source_vpath);
2680 return FALSE;
2682 vfs_path_free (source_vpath);
2685 ctx = file_op_context_new (operation);
2687 /* Show confirmation dialog */
2688 if (operation != OP_DELETE)
2690 char *tmp_dest_dir, *dest_dir;
2691 char *format;
2693 /* Forced single operations default to the original name */
2694 if (force_single)
2695 tmp_dest_dir = g_strdup (source);
2696 else if (get_other_type () == view_listing)
2697 tmp_dest_dir = vfs_path_to_str (other_panel->cwd_vpath);
2698 else
2699 tmp_dest_dir = vfs_path_to_str (panel->cwd_vpath);
2701 * Add trailing backslash only when do non-local ops.
2702 * It saves user from occasional file renames (when destination
2703 * dir is deleted)
2705 if (!force_single && tmp_dest_dir[0] != '\0'
2706 && tmp_dest_dir[strlen (tmp_dest_dir) - 1] != PATH_SEP)
2708 /* add trailing separator */
2709 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
2710 g_free (tmp_dest_dir);
2712 else
2714 /* just copy */
2715 dest_dir = tmp_dest_dir;
2717 if (dest_dir == NULL)
2719 ret_val = FALSE;
2720 goto ret_fast;
2723 /* Generate confirmation prompt */
2724 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2726 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2727 source != NULL ? (void *) source
2728 : (void *) &panel->marked, dest_dir, &do_bg);
2730 g_free (format);
2731 g_free (dest_dir);
2733 if (dest == NULL || dest[0] == '\0')
2735 g_free (dest);
2736 ret_val = FALSE;
2737 goto ret_fast;
2739 dest_vpath = vfs_path_from_str (dest);
2741 else if (confirm_delete)
2743 char *format;
2744 char fmd_buf[BUF_MEDIUM];
2746 /* Generate confirmation prompt */
2747 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2749 if (source == NULL)
2750 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2751 else
2753 const int fmd_xlen = 64;
2754 i = fmd_xlen - str_term_width1 (format) - 4;
2755 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2758 g_free (format);
2760 if (safe_delete)
2761 query_set_sel (1);
2763 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2765 if (i != 0)
2767 ret_val = FALSE;
2768 goto ret_fast;
2772 tctx = file_op_total_context_new ();
2773 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2776 filegui_dialog_type_t dialog_type;
2778 if (operation == OP_DELETE)
2779 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2780 else
2782 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2783 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2785 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2786 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2789 /* Background also need ctx->ui, but not full */
2790 if (do_bg)
2791 file_op_context_create_ui_without_init (ctx, TRUE, dialog_type);
2792 else
2793 file_op_context_create_ui (ctx, TRUE, dialog_type);
2796 #ifdef ENABLE_BACKGROUND
2797 /* Did the user select to do a background operation? */
2798 if (do_bg)
2800 int v;
2801 char *cwd_str;
2803 cwd_str = vfs_path_to_str (panel->cwd_vpath);
2804 v = do_background (ctx, g_strconcat (op_names[operation], ": ", cwd_str, (char *) NULL));
2805 g_free (cwd_str);
2806 if (v == -1)
2807 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2809 /* If we are the parent */
2810 if (v == 1)
2812 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
2814 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
2815 vfs_path_free (dest_vpath);
2816 g_free (dest);
2817 /* file_op_context_destroy (ctx); */
2818 return FALSE;
2821 #endif /* ENABLE_BACKGROUND */
2823 /* Initialize things */
2824 /* We do not want to trash cache every time file is
2825 created/touched. However, this will make our cache contain
2826 invalid data. */
2827 if ((dest != NULL) && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2828 save_dest = g_strdup (dest);
2830 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
2831 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2832 save_cwd = vfs_path_to_str (panel->cwd_vpath);
2834 /* Now, let's do the job */
2836 /* This code is only called by the tree and panel code */
2837 if (single_entry)
2839 /* We now have ETA in all cases */
2841 /* One file: FIXME mc_chdir will take user out of any vfs */
2842 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2844 vfs_path_t *vpath;
2845 int chdir_retcode;
2847 vpath = vfs_path_from_str (PATH_SEP_STR);
2848 chdir_retcode = mc_chdir (vpath);
2849 vfs_path_free (vpath);
2850 if (chdir_retcode < 0)
2852 ret_val = FALSE;
2853 goto clean_up;
2857 /* The source and src_stat variables have been initialized before */
2858 #ifdef WITH_FULL_PATHS
2859 if (g_path_is_absolute (source))
2860 source_with_vpath = vfs_path_from_str (source);
2861 else
2862 source_with_vpath = vfs_path_append_new (panel->cwd_vpath, source, (char *) NULL);
2863 source_with_path_str = vfs_path_to_str (source_with_vpath);
2864 #endif /* WITH_FULL_PATHS */
2865 if (panel_operate_init_totals (operation, panel, source_with_path_str, ctx) == FILE_CONT)
2867 if (operation == OP_DELETE)
2869 if (S_ISDIR (src_stat.st_mode))
2870 value = erase_dir (tctx, ctx, source_with_vpath);
2871 else
2872 value = erase_file (tctx, ctx, source_with_vpath);
2874 else
2876 temp = transform_source (ctx, source_with_path_str);
2877 if (temp == NULL)
2878 value = transform_error;
2879 else
2881 char *repl_dest, *temp2;
2883 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2884 temp2 = mc_build_filename (repl_dest, temp, NULL);
2885 g_free (temp);
2886 g_free (repl_dest);
2887 g_free (dest);
2888 vfs_path_free (dest_vpath);
2889 dest = temp2;
2890 dest_vpath = vfs_path_from_str (dest);
2892 switch (operation)
2894 case OP_COPY:
2895 /* we use file_mask_op_follow_links only with OP_COPY */
2896 ctx->stat_func (source_with_vpath, &src_stat);
2898 if (S_ISDIR (src_stat.st_mode))
2899 value = copy_dir_dir (tctx, ctx, source_with_path_str, dest,
2900 TRUE, FALSE, FALSE, NULL);
2901 else
2902 value = copy_file_file (tctx, ctx, source_with_path_str, dest);
2903 break;
2905 case OP_MOVE:
2906 if (S_ISDIR (src_stat.st_mode))
2907 value = move_dir_dir (tctx, ctx, source_with_path_str, dest);
2908 else
2909 value = move_file_file (tctx, ctx, source_with_path_str, dest);
2910 break;
2912 default:
2913 /* Unknown file operation */
2914 abort ();
2917 } /* Copy or move operation */
2919 if ((value == FILE_CONT) && !force_single)
2920 unmark_files (panel);
2923 else
2925 /* Many files */
2927 /* Check destination for copy or move operation */
2928 while (operation != OP_DELETE)
2930 int dst_result;
2931 struct stat dst_stat;
2933 dst_result = mc_stat (dest_vpath, &dst_stat);
2935 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2936 break;
2938 if (ctx->skip_all
2939 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2940 goto clean_up;
2943 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2945 /* Loop for every file, perform the actual copy operation */
2946 for (i = 0; i < panel->count; i++)
2948 const char *source2;
2950 if (!panel->dir.list[i].f.marked)
2951 continue; /* Skip the unmarked ones */
2953 source2 = panel->dir.list[i].fname;
2954 src_stat = panel->dir.list[i].st;
2956 #ifdef WITH_FULL_PATHS
2957 g_free (source_with_path_str);
2958 vfs_path_free (source_with_vpath);
2959 if (g_path_is_absolute (source2))
2960 source_with_vpath = vfs_path_from_str (source2);
2961 else
2962 source_with_vpath =
2963 vfs_path_append_new (panel->cwd_vpath, source2, (char *) NULL);
2964 source_with_path_str = vfs_path_to_str (source_with_vpath);
2965 #endif /* WITH_FULL_PATHS */
2967 if (operation == OP_DELETE)
2969 if (S_ISDIR (src_stat.st_mode))
2970 value = erase_dir (tctx, ctx, source_with_vpath);
2971 else
2972 value = erase_file (tctx, ctx, source_with_vpath);
2974 else
2976 temp = transform_source (ctx, source_with_path_str);
2978 if (temp == NULL)
2979 value = transform_error;
2980 else
2982 char *temp2, *temp3, *repl_dest;
2984 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2985 temp2 = mc_build_filename (repl_dest, temp, NULL);
2986 g_free (temp);
2987 g_free (repl_dest);
2988 temp3 = source_with_path_str;
2989 source_with_path_str = strutils_shell_unescape (source_with_path_str);
2990 g_free (temp3);
2991 temp3 = temp2;
2992 temp2 = strutils_shell_unescape (temp2);
2993 g_free (temp3);
2995 switch (operation)
2997 case OP_COPY:
2998 /* we use file_mask_op_follow_links only with OP_COPY */
3000 vfs_path_t *vpath;
3002 vpath = vfs_path_from_str (source_with_path_str);
3003 ctx->stat_func (vpath, &src_stat);
3004 vfs_path_free (vpath);
3006 if (S_ISDIR (src_stat.st_mode))
3007 value = copy_dir_dir (tctx, ctx, source_with_path_str, temp2,
3008 TRUE, FALSE, FALSE, NULL);
3009 else
3010 value = copy_file_file (tctx, ctx, source_with_path_str, temp2);
3011 dest_dirs = free_linklist (dest_dirs);
3012 break;
3014 case OP_MOVE:
3015 if (S_ISDIR (src_stat.st_mode))
3016 value = move_dir_dir (tctx, ctx, source_with_path_str, temp2);
3017 else
3018 value = move_file_file (tctx, ctx, source_with_path_str, temp2);
3019 break;
3021 default:
3022 /* Unknown file operation */
3023 abort ();
3026 g_free (temp2);
3028 } /* Copy or move operation */
3030 if (value == FILE_ABORT)
3031 break;
3033 if (value == FILE_CONT)
3034 do_file_mark (panel, i, 0);
3036 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3038 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3039 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3042 if (operation != OP_DELETE)
3043 file_progress_show (ctx, 0, 0, "", FALSE);
3045 if (check_progress_buttons (ctx) == FILE_ABORT)
3046 break;
3048 mc_refresh ();
3049 } /* Loop for every file */
3051 } /* Many entries */
3053 clean_up:
3054 /* Clean up */
3055 if (save_cwd != NULL)
3057 tmp_vpath = vfs_path_from_str (save_cwd);
3058 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3059 vfs_path_free (tmp_vpath);
3060 g_free (save_cwd);
3063 if (save_dest != NULL)
3065 tmp_vpath = vfs_path_from_str (save_dest);
3066 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3067 vfs_path_free (tmp_vpath);
3068 g_free (save_dest);
3071 linklist = free_linklist (linklist);
3072 dest_dirs = free_linklist (dest_dirs);
3073 #ifdef WITH_FULL_PATHS
3074 g_free (source_with_path_str);
3075 vfs_path_free (source_with_vpath);
3076 #endif /* WITH_FULL_PATHS */
3077 g_free (dest);
3078 vfs_path_free (dest_vpath);
3079 g_free (ctx->dest_mask);
3080 ctx->dest_mask = NULL;
3082 #ifdef ENABLE_BACKGROUND
3083 /* Let our parent know we are saying bye bye */
3084 if (mc_global.we_are_background)
3086 int cur_pid = getpid ();
3087 /* Send pid to parent with child context, it is fork and
3088 don't modify real parent ctx */
3089 ctx->pid = cur_pid;
3090 parent_call ((void *) end_bg_process, ctx, 0);
3092 vfs_shut ();
3093 _exit (0);
3095 #endif /* ENABLE_BACKGROUND */
3097 file_op_total_context_destroy (tctx);
3098 ret_fast:
3099 file_op_context_destroy (ctx);
3100 g_free (source);
3102 return ret_val;
3105 /* }}} */
3107 /* --------------------------------------------------------------------------------------------- */
3108 /* {{{ Query/status report routines */
3109 /** Report error with one file */
3110 FileProgressStatus
3111 file_error (const char *format, const char *file)
3113 char buf[BUF_MEDIUM];
3115 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3117 return do_file_error (buf);
3120 /* --------------------------------------------------------------------------------------------- */
3123 Cause emacs to enter folding mode for this file:
3124 Local variables:
3125 end: