Changed interface of following functions to handle vfs_path_t object as parameter:
[midnight-commander.git] / src / filemanager / file.c
blob06821a5da54275bdcbdaf0ca95345afd3c24ebe7
1 /*
2 File management.
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Janne Kukonlehto, 1994, 1995
10 Fred Leeflang, 1994, 1995
11 Miguel de Icaza, 1994, 1995, 1996
12 Jakub Jelinek, 1995, 1996
13 Norbert Warmuth, 1997
14 Pavel Machek, 1998
16 The copy code was based in GNU's cp, and was written by:
17 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
19 The move code was based in GNU's mv, and was written by:
20 Mike Parker and David MacKenzie.
22 Janne Kukonlehto added much error recovery to them for being used
23 in an interactive program.
25 This file is part of the Midnight Commander.
27 The Midnight Commander is free software: you can redistribute it
28 and/or modify it under the terms of the GNU General Public License as
29 published by the Free Software Foundation, either version 3 of the License,
30 or (at your option) any later version.
32 The Midnight Commander is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program. If not, see <http://www.gnu.org/licenses/>.
42 * Please note that all dialogs used here must be safe for background
43 * operations.
46 /** \file file.c
47 * \brief Source: file management
50 /* {{{ Include files */
52 #include <config.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
62 #include <fcntl.h>
64 #include "lib/global.h"
65 #include "lib/tty/tty.h"
66 #include "lib/tty/key.h"
67 #include "lib/search.h"
68 #include "lib/strescape.h"
69 #include "lib/strutil.h"
70 #include "lib/util.h"
71 #include "lib/vfs/vfs.h"
72 #include "lib/widget.h"
74 #include "src/setup.h"
75 #ifdef ENABLE_BACKGROUND
76 #include "src/background.h" /* do_background() */
77 #endif
79 #include "layout.h" /* rotate_dash() */
81 /* Needed for current_panel, other_panel and WTree */
82 #include "dir.h"
83 #include "filegui.h"
84 #include "tree.h"
85 #include "midnight.h" /* current_panel */
87 #include "file.h"
89 /* }}} */
91 /*** global variables ****************************************************************************/
93 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
94 const char *op_names[3] = {
95 N_("DialogTitle|Copy"),
96 N_("DialogTitle|Move"),
97 N_("DialogTitle|Delete")
100 /*** file scope macro definitions ****************************************************************/
102 /* Hack: the vfs code should not rely on this */
103 #define WITH_FULL_PATHS 1
105 #define FILEOP_UPDATE_INTERVAL 2
106 #define FILEOP_STALLING_INTERVAL 4
108 /*** file scope type declarations ****************************************************************/
110 /* This is a hard link cache */
111 struct link
113 struct link *next;
114 struct vfs_class *vfs;
115 dev_t dev;
116 ino_t ino;
117 short linkcount;
118 mode_t st_mode;
119 char name[1];
122 /* Status of the destination file */
123 typedef enum
125 DEST_NONE = 0, /* Not created */
126 DEST_SHORT = 1, /* Created, not fully copied */
127 DEST_FULL = 2 /* Created, fully copied */
128 } dest_status_t;
131 * This array introduced to avoid translation problems. The former (op_names)
132 * is assumed to be nouns, suitable in dialog box titles; this one should
133 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
134 * (I don't use spaces around the words, because someday they could be
135 * dropped, when widgets get smarter)
138 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
139 static const char *op_names1[] = {
140 N_("FileOperation|Copy"),
141 N_("FileOperation|Move"),
142 N_("FileOperation|Delete")
146 * These are formats for building a prompt. Parts encoded as follows:
147 * %o - operation from op_names1
148 * %f - file/files or files/directories, as appropriate
149 * %m - "with source mask" or question mark for delete
150 * %s - source name (truncated)
151 * %d - number of marked files
152 * %e - "to:" or question mark for delete
154 * xgettext:no-c-format */
155 static const char *one_format = N_("%o %f \"%s\"%m");
156 /* xgettext:no-c-format */
157 static const char *many_format = N_("%o %d %f%m");
159 static const char *prompt_parts[] = {
160 N_("file"),
161 N_("files"),
162 N_("directory"),
163 N_("directories"),
164 N_("files/directories"),
165 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
166 N_(" with source mask:"),
167 N_("to:")
170 static const char *question_format = N_("%s?");
172 /*** file scope variables ************************************************************************/
174 /* the hard link cache */
175 static struct link *linklist = NULL;
177 /* the files-to-be-erased list */
178 static struct link *erase_list;
181 * In copy_dir_dir we use two additional single linked lists: The first -
182 * variable name `parent_dirs' - holds information about already copied
183 * directories and is used to detect cyclic symbolic links.
184 * The second (`dest_dirs' below) holds information about just created
185 * target directories and is used to detect when an directory is copied
186 * into itself (we don't want to copy infinitly).
187 * Both lists don't use the linkcount and name structure members of struct
188 * link.
190 static struct link *dest_dirs = NULL;
192 static FileProgressStatus transform_error = FILE_CONT;
194 /*** file scope functions ************************************************************************/
195 /* --------------------------------------------------------------------------------------------- */
197 static char *
198 transform_source (FileOpContext * ctx, const char *source)
200 char *s, *q;
201 char *fnsource;
203 s = g_strdup (source);
205 /* We remove \n from the filename since regex routines would use \n as an anchor */
206 /* this is just to be allowed to maniupulate file names with \n on it */
207 for (q = s; *q != '\0'; q++)
208 if (*q == '\n')
209 *q = ' ';
211 fnsource = (char *) x_basename (s);
213 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
214 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
215 else
217 q = NULL;
218 transform_error = FILE_SKIP;
221 g_free (s);
222 return q;
225 /* --------------------------------------------------------------------------------------------- */
227 static void
228 free_linklist (struct link **lc_linklist)
230 struct link *lp, *lp2;
232 for (lp = *lc_linklist; lp != NULL; lp = lp2)
234 lp2 = lp->next;
235 g_free (lp);
237 *lc_linklist = NULL;
240 /* --------------------------------------------------------------------------------------------- */
242 static int
243 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
245 vfs_path_t *vpath;
246 vfs_path_element_t *vpath_element;
247 ino_t ino = sb->st_ino;
248 dev_t dev = sb->st_dev;
250 vpath = vfs_path_from_str (path);
251 vpath_element = vfs_path_get_by_index (vpath, -1);
253 while (lp != NULL)
255 if (lp->vfs == vpath_element->class)
256 if (lp->ino == ino && lp->dev == dev)
257 return 1;
258 lp = lp->next;
260 vfs_path_free (vpath);
261 return 0;
264 /* --------------------------------------------------------------------------------------------- */
266 * Check and made hardlink
268 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
269 * and a hardlink was succesfully made
272 static gboolean
273 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
275 struct link *lp;
276 vfs_path_t *vpath;
278 struct vfs_class *my_vfs;
279 ino_t ino = pstat->st_ino;
280 dev_t dev = pstat->st_dev;
281 struct stat link_stat;
282 const char *p;
284 vpath = vfs_path_from_str (src_name);
286 if ((vfs_file_class_flags (vpath) & VFSF_NOLINKS) != 0)
288 vfs_path_free (vpath);
289 return FALSE;
291 my_vfs = vfs_path_get_by_index (vpath, -1)->class;
292 vfs_path_free (vpath);
294 for (lp = linklist; lp != NULL; lp = lp->next)
295 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev)
297 struct vfs_class *lp_name_class;
298 int stat_result;
300 vpath = vfs_path_from_str (lp->name);
301 lp_name_class = vfs_path_get_by_index (vpath, -1)->class;
302 stat_result = mc_stat (vpath, &link_stat);
303 vfs_path_free (vpath);
305 if (!stat_result && link_stat.st_ino == ino
306 && link_stat.st_dev == dev && lp_name_class == my_vfs)
308 struct vfs_class *p_class, *dst_name_class;
310 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
311 was copied to */
313 vpath = vfs_path_from_str (dst_name);
314 dst_name_class = vfs_path_get_by_index (vpath, -1)->class;
315 vfs_path_free (vpath);
317 vpath = vfs_path_from_str (p);
318 p_class = vfs_path_get_by_index (vpath, -1)->class;
320 if (dst_name_class == p_class)
322 if (!mc_stat (vpath, &link_stat))
324 if (!mc_link (p, dst_name))
326 vfs_path_free (vpath);
327 return TRUE;
331 vfs_path_free (vpath);
334 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
335 return FALSE;
337 lp = (struct link *) g_try_malloc (sizeof (struct link) + strlen (src_name)
338 + strlen (dst_name) + 1);
339 if (lp)
341 char *lpdstname;
342 lp->vfs = my_vfs;
343 lp->ino = ino;
344 lp->dev = dev;
345 strcpy (lp->name, src_name);
346 lpdstname = lp->name + strlen (lp->name) + 1;
347 strcpy (lpdstname, dst_name);
348 lp->next = linklist;
349 linklist = lp;
351 return FALSE;
354 /* --------------------------------------------------------------------------------------------- */
356 * Duplicate the contents of the symbolic link src_path in dst_path.
357 * Try to make a stable symlink if the option "stable symlink" was
358 * set in the file mask dialog.
359 * If dst_path is an existing symlink it will be deleted silently
360 * (upper levels take already care of existing files at dst_path).
363 static FileProgressStatus
364 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
366 char link_target[MC_MAXPATHLEN];
367 int len;
368 FileProgressStatus return_status;
369 struct stat sb;
370 gboolean dst_is_symlink;
372 vfs_path_t *src_vpath = vfs_path_from_str (src_path);
373 vfs_path_t *dst_vpath = vfs_path_from_str (dst_path);
375 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
377 retry_src_readlink:
378 len = mc_readlink (src_vpath, link_target, MC_MAXPATHLEN - 1);
379 if (len < 0)
381 if (ctx->skip_all)
382 return_status = FILE_SKIPALL;
383 else
385 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
386 if (return_status == FILE_SKIPALL)
387 ctx->skip_all = TRUE;
388 if (return_status == FILE_RETRY)
389 goto retry_src_readlink;
391 return return_status;
393 link_target[len] = 0;
395 if (ctx->stable_symlinks)
398 if (!vfs_file_is_local (src_vpath) || !vfs_file_is_local (dst_vpath))
400 message (D_ERROR, MSG_ERROR,
401 _("Cannot make stable symlinks across"
402 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
403 ctx->stable_symlinks = FALSE;
407 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
409 char *p, *q, *s;
411 const char *r = strrchr (src_path, PATH_SEP);
413 if (r)
415 p = g_strndup (src_path, r - src_path + 1);
416 if (g_path_is_absolute (dst_path))
417 q = g_strdup (dst_path);
418 else
419 q = g_strconcat (p, dst_path, (char *) NULL);
420 s = strrchr (q, PATH_SEP);
421 if (s)
423 s[1] = 0;
424 s = g_strconcat (p, link_target, (char *) NULL);
425 g_free (p);
426 g_strlcpy (link_target, s, sizeof (link_target));
427 g_free (s);
428 s = diff_two_paths (q, link_target);
429 if (s)
431 g_strlcpy (link_target, s, sizeof (link_target));
432 g_free (s);
435 else
436 g_free (p);
437 g_free (q);
440 retry_dst_symlink:
441 if (mc_symlink (link_target, dst_path) == 0)
443 /* Success */
444 vfs_path_free (src_vpath);
445 vfs_path_free (dst_vpath);
446 return FILE_CONT;
449 * if dst_exists, it is obvious that this had failed.
450 * We can delete the old symlink and try again...
452 if (dst_is_symlink)
454 if (mc_unlink (dst_vpath) == 0)
455 if (mc_symlink (link_target, dst_path) == 0)
457 /* Success */
458 vfs_path_free (src_vpath);
459 vfs_path_free (dst_vpath);
461 return FILE_CONT;
464 if (ctx->skip_all)
465 return_status = FILE_SKIPALL;
466 else
468 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
469 if (return_status == FILE_SKIPALL)
470 ctx->skip_all = TRUE;
471 if (return_status == FILE_RETRY)
472 goto retry_dst_symlink;
474 vfs_path_free (src_vpath);
475 vfs_path_free (dst_vpath);
476 return return_status;
479 /* --------------------------------------------------------------------------------------------- */
481 static FileProgressStatus
482 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add)
484 struct timeval tv_current;
485 static struct timeval tv_start = { };
487 tctx->progress_count++;
488 tctx->progress_bytes += (uintmax_t) add;
490 if (tv_start.tv_sec == 0)
492 gettimeofday (&tv_start, (struct timezone *) NULL);
494 gettimeofday (&tv_current, (struct timezone *) NULL);
495 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
497 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
499 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
500 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
502 tv_start.tv_sec = tv_current.tv_sec;
505 return check_progress_buttons (ctx);
508 /* --------------------------------------------------------------------------------------------- */
510 static FileProgressStatus
511 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
513 char *msg;
514 int result = 0;
515 const char *head_msg;
517 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
519 msg = g_strdup_printf (fmt, a, b);
520 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
521 g_free (msg);
522 do_refresh ();
524 return (result == 1) ? FILE_ABORT : FILE_SKIP;
527 /* --------------------------------------------------------------------------------------------- */
529 static FileProgressStatus
530 warn_same_file (const char *fmt, const char *a, const char *b)
532 #ifdef ENABLE_BACKGROUND
533 /* *INDENT-OFF* */
534 union
536 void *p;
537 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
538 const char *a, const char *b);
539 } pntr;
540 /* *INDENT-ON* */
542 pntr.f = real_warn_same_file;
544 if (mc_global.we_are_background)
545 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
546 #endif
547 return real_warn_same_file (Foreground, fmt, a, b);
550 /* --------------------------------------------------------------------------------------------- */
551 /* {{{ Query/status report routines */
553 static FileProgressStatus
554 real_do_file_error (enum OperationMode mode, const char *error)
556 int result;
557 const char *msg;
559 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
560 result =
561 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
563 switch (result)
565 case 0:
566 do_refresh ();
567 return FILE_SKIP;
569 case 1:
570 do_refresh ();
571 return FILE_SKIPALL;
573 case 2:
574 do_refresh ();
575 return FILE_RETRY;
577 case 3:
578 default:
579 return FILE_ABORT;
583 /* --------------------------------------------------------------------------------------------- */
585 static FileProgressStatus
586 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
588 gchar *text;
590 if (ctx->recursive_result < RECURSIVE_ALWAYS)
592 const char *msg = mode == Foreground
593 ? _("\nDirectory not empty.\nDelete it recursively?")
594 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
595 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
597 if (safe_delete)
598 query_set_sel (1);
600 ctx->recursive_result =
601 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
602 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
604 if (ctx->recursive_result != RECURSIVE_ABORT)
605 do_refresh ();
606 g_free (text);
609 switch (ctx->recursive_result)
611 case RECURSIVE_YES:
612 case RECURSIVE_ALWAYS:
613 return FILE_CONT;
615 case RECURSIVE_NO:
616 case RECURSIVE_NEVER:
617 return FILE_SKIP;
619 case RECURSIVE_ABORT:
620 default:
621 return FILE_ABORT;
625 /* --------------------------------------------------------------------------------------------- */
627 #ifdef ENABLE_BACKGROUND
628 static FileProgressStatus
629 do_file_error (const char *str)
631 union
633 void *p;
634 FileProgressStatus (*f) (enum OperationMode, const char *);
635 } pntr;
636 pntr.f = real_do_file_error;
638 if (mc_global.we_are_background)
639 return parent_call (pntr.p, NULL, 1, strlen (str), str);
640 else
641 return real_do_file_error (Foreground, str);
644 /* --------------------------------------------------------------------------------------------- */
646 static FileProgressStatus
647 query_recursive (FileOpContext * ctx, const char *s)
649 union
651 void *p;
652 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
653 } pntr;
654 pntr.f = real_query_recursive;
656 if (mc_global.we_are_background)
657 return parent_call (pntr.p, ctx, 1, strlen (s), s);
658 else
659 return real_query_recursive (ctx, Foreground, s);
662 /* --------------------------------------------------------------------------------------------- */
664 static FileProgressStatus
665 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
666 struct stat *_d_stat)
668 union
670 void *p;
671 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
672 struct stat *, struct stat *);
673 } pntr;
674 pntr.f = file_progress_real_query_replace;
676 if (mc_global.we_are_background)
677 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
678 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
679 else
680 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
683 #else
684 /* --------------------------------------------------------------------------------------------- */
686 static FileProgressStatus
687 do_file_error (const char *str)
689 return real_do_file_error (Foreground, str);
692 /* --------------------------------------------------------------------------------------------- */
694 static FileProgressStatus
695 query_recursive (FileOpContext * ctx, const char *s)
697 return real_query_recursive (ctx, Foreground, s);
700 /* --------------------------------------------------------------------------------------------- */
702 static FileProgressStatus
703 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
704 struct stat *_d_stat)
706 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
709 #endif /* !ENABLE_BACKGROUND */
711 /* --------------------------------------------------------------------------------------------- */
712 /** Report error with two files */
714 static FileProgressStatus
715 files_error (const char *format, const char *file1, const char *file2)
717 char buf[BUF_MEDIUM];
718 char *nfile1 = g_strdup (path_trunc (file1, 15));
719 char *nfile2 = g_strdup (path_trunc (file2, 15));
721 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
723 g_free (nfile1);
724 g_free (nfile2);
726 return do_file_error (buf);
729 /* }}} */
731 /* --------------------------------------------------------------------------------------------- */
733 static void
734 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
735 struct timeval tv_current, struct timeval tv_transfer_start,
736 off_t file_size, off_t n_read_total)
738 long dt;
740 /* 1. Update rotating dash after some time */
741 rotate_dash ();
743 /* 3. Compute ETA */
744 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
746 if (n_read_total == 0)
747 ctx->eta_secs = 0.0;
748 else
750 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
751 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
754 /* 4. Compute BPS rate */
755 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
756 if (ctx->bps_time < 1)
757 ctx->bps_time = 1;
758 ctx->bps = n_read_total / ctx->bps_time;
760 /* 5. Compute total ETA and BPS */
761 if (ctx->progress_bytes != 0)
763 uintmax_t remain_bytes;
765 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
766 #if 1
768 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
770 if (total_secs < 1)
771 total_secs = 1;
773 tctx->bps = tctx->copied_bytes / total_secs;
774 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
776 #else
777 /* broken on lot of little files */
778 tctx->bps_count++;
779 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
780 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
781 #endif
785 /* --------------------------------------------------------------------------------------------- */
787 /* {{{ Move routines */
788 static FileProgressStatus
789 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
791 struct stat src_stats, dst_stats;
792 FileProgressStatus return_status = FILE_CONT;
793 gboolean copy_done = FALSE;
794 gboolean old_ask_overwrite;
795 vfs_path_t *src_vpath, *dst_vpath;
797 file_progress_show_source (ctx, s);
798 file_progress_show_target (ctx, d);
799 if (check_progress_buttons (ctx) == FILE_ABORT)
800 return FILE_ABORT;
802 mc_refresh ();
803 src_vpath = vfs_path_from_str (s);
804 dst_vpath = vfs_path_from_str (d);
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;
817 if (return_status != FILE_RETRY)
819 vfs_path_free (src_vpath);
820 vfs_path_free (dst_vpath);
821 return return_status;
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)
828 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
830 if (S_ISDIR (dst_stats.st_mode))
832 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
833 do_refresh ();
834 vfs_path_free (src_vpath);
835 vfs_path_free (dst_vpath);
836 return FILE_SKIP;
839 if (confirm_overwrite)
841 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
842 if (return_status != FILE_CONT)
844 vfs_path_free (src_vpath);
845 vfs_path_free (dst_vpath);
846 return return_status;
849 /* Ok to overwrite */
852 if (!ctx->do_append)
854 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
856 return_status = make_symlink (ctx, s, d);
857 if (return_status == FILE_CONT)
858 goto retry_src_remove;
859 else
861 vfs_path_free (src_vpath);
862 vfs_path_free (dst_vpath);
863 return return_status;
867 if (mc_rename (s, d) == 0)
869 vfs_path_free (src_vpath);
870 vfs_path_free (dst_vpath);
871 return progress_update_one (tctx, ctx, src_stats.st_size);
874 #if 0
875 /* Comparison to EXDEV seems not to work in nfs if you're moving from
876 one nfs to the same, but on the server it is on two different
877 filesystems. Then nfs returns EIO instead of EXDEV.
878 Hope it will not hurt if we always in case of error try to copy/delete. */
879 else
880 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
882 if (errno != EXDEV)
884 if (ctx->skip_all)
885 return_status = FILE_SKIPALL;
886 else
888 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
889 if (return_status == FILE_SKIPALL)
890 ctx->skip_all = TRUE;
891 if (return_status == FILE_RETRY)
892 goto retry_rename;
894 vfs_path_free (src_vpath);
895 vfs_path_free (dst_vpath);
897 return return_status;
899 #endif
901 /* Failed because filesystem boundary -> copy the file instead */
902 old_ask_overwrite = tctx->ask_overwrite;
903 tctx->ask_overwrite = FALSE;
904 return_status = copy_file_file (tctx, ctx, s, d);
905 tctx->ask_overwrite = old_ask_overwrite;
906 if (return_status != FILE_CONT)
908 vfs_path_free (src_vpath);
909 vfs_path_free (dst_vpath);
910 return return_status;
913 copy_done = TRUE;
915 file_progress_show_source (ctx, NULL);
916 file_progress_show (ctx, 0, 0, "", FALSE);
918 return_status = check_progress_buttons (ctx);
919 if (return_status != FILE_CONT)
921 vfs_path_free (src_vpath);
922 vfs_path_free (dst_vpath);
923 return return_status;
926 mc_refresh ();
928 retry_src_remove:
929 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
931 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
932 if (return_status == FILE_RETRY)
933 goto retry_src_remove;
934 if (return_status == FILE_SKIPALL)
935 ctx->skip_all = TRUE;
937 vfs_path_free (src_vpath);
938 vfs_path_free (dst_vpath);
939 return return_status;
942 if (!copy_done)
943 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
945 vfs_path_free (src_vpath);
946 vfs_path_free (dst_vpath);
948 return return_status;
951 /* }}} */
953 /* --------------------------------------------------------------------------------------------- */
954 /* {{{ Erase routines */
955 /** Don't update progress status if progress_count==NULL */
957 static FileProgressStatus
958 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
960 int return_status;
961 struct stat buf;
962 vfs_path_t *vpath = vfs_path_from_str (s);
964 file_progress_show_deleting (ctx, s);
965 if (check_progress_buttons (ctx) == FILE_ABORT)
966 return FILE_ABORT;
967 mc_refresh ();
969 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
971 /* ignore, most likely the mc_unlink fails, too */
972 buf.st_size = 0;
975 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
977 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
978 if (return_status == FILE_ABORT)
980 vfs_path_free (vpath);
981 return return_status;
983 if (return_status == FILE_RETRY)
984 continue;
985 if (return_status == FILE_SKIPALL)
986 ctx->skip_all = TRUE;
987 break;
990 vfs_path_free (vpath);
991 if (tctx->progress_count == 0)
992 return FILE_CONT;
993 return progress_update_one (tctx, ctx, buf.st_size);
996 /* --------------------------------------------------------------------------------------------- */
999 Recursive remove of files
1000 abort->cancel stack
1001 skip ->warn every level, gets default
1002 skipall->remove as much as possible
1004 static FileProgressStatus
1005 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
1007 struct dirent *next;
1008 struct stat buf;
1009 DIR *reading;
1010 char *path;
1011 FileProgressStatus return_status = FILE_CONT;
1012 vfs_path_t *vpath;
1014 if (strcmp (s, "..") == 0)
1015 return FILE_RETRY;
1017 vpath = vfs_path_from_str (s);
1018 reading = mc_opendir (vpath);
1020 if (reading == NULL)
1022 return_status = FILE_RETRY;
1023 goto ret;
1026 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1028 vfs_path_t *tmp_vpath;
1030 if (!strcmp (next->d_name, "."))
1031 continue;
1032 if (!strcmp (next->d_name, ".."))
1033 continue;
1034 path = concat_dir_and_file (s, next->d_name);
1035 tmp_vpath = vfs_path_from_str (path);
1036 if (mc_lstat (tmp_vpath, &buf) != 0)
1038 g_free (path);
1039 mc_closedir (reading);
1040 vfs_path_free (tmp_vpath);
1041 return_status = FILE_RETRY;
1042 goto ret;
1044 if (S_ISDIR (buf.st_mode))
1045 return_status = recursive_erase (tctx, ctx, path);
1046 else
1047 return_status = erase_file (tctx, ctx, path);
1048 vfs_path_free (tmp_vpath);
1049 g_free (path);
1051 mc_closedir (reading);
1052 if (return_status == FILE_ABORT)
1053 goto ret;
1055 file_progress_show_deleting (ctx, s);
1056 if (check_progress_buttons (ctx) == FILE_ABORT)
1058 return_status = FILE_ABORT;
1059 goto ret;
1061 mc_refresh ();
1063 while (my_rmdir (s) != 0 && !ctx->skip_all)
1065 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1066 if (return_status == FILE_RETRY)
1067 continue;
1068 if (return_status == FILE_ABORT)
1069 goto ret;
1070 if (return_status == FILE_SKIPALL)
1071 ctx->skip_all = TRUE;
1072 break;
1075 ret:
1076 vfs_path_free (vpath);
1077 return return_status;
1080 /* --------------------------------------------------------------------------------------------- */
1081 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1082 in the directory path points to, 0 else. */
1084 static int
1085 check_dir_is_empty (const char *path)
1087 DIR *dir;
1088 struct dirent *d;
1089 int i;
1090 vfs_path_t *vpath = vfs_path_from_str (path);
1092 dir = mc_opendir (vpath);
1093 if (!dir)
1095 vfs_path_free (vpath);
1096 return -1;
1099 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1101 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1102 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1103 continue; /* "." or ".." */
1104 i = 0;
1105 break;
1108 mc_closedir (dir);
1109 vfs_path_free (vpath);
1110 return i;
1113 /* --------------------------------------------------------------------------------------------- */
1115 static FileProgressStatus
1116 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1118 FileProgressStatus error;
1120 if (strcmp (s, "..") == 0)
1121 return FILE_SKIP;
1123 if (strcmp (s, ".") == 0)
1124 return FILE_SKIP;
1126 file_progress_show_deleting (ctx, s);
1127 if (check_progress_buttons (ctx) == FILE_ABORT)
1128 return FILE_ABORT;
1129 mc_refresh ();
1131 if (1 != check_dir_is_empty (s)) /* not empty or error */
1132 return FILE_CONT;
1134 while (my_rmdir (s) != 0 && !ctx->skip_all)
1136 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1137 if (error == FILE_SKIPALL)
1138 ctx->skip_all = TRUE;
1139 if (error != FILE_RETRY)
1140 return error;
1143 return FILE_CONT;
1146 /* }}} */
1148 /* --------------------------------------------------------------------------------------------- */
1149 /* {{{ Panel operate routines */
1152 * Return currently selected entry name or the name of the first marked
1153 * entry if there is one.
1156 static char *
1157 panel_get_file (WPanel * panel)
1159 if (get_current_type () == view_tree)
1161 WTree *tree;
1163 tree = (WTree *) get_panel_widget (get_current_index ());
1164 return tree_selected_name (tree);
1167 if (panel->marked != 0)
1169 int i;
1171 for (i = 0; i < panel->count; i++)
1172 if (panel->dir.list[i].f.marked)
1173 return panel->dir.list[i].fname;
1176 return panel->dir.list[panel->selected].fname;
1179 /* --------------------------------------------------------------------------------------------- */
1181 * panel_compute_totals:
1183 * compute the number of files and the number of bytes
1184 * used up by the whole selection, recursing directories
1185 * as required. In addition, it checks to see if it will
1186 * overwrite any files by doing the copy.
1189 static FileProgressStatus
1190 panel_compute_totals (const WPanel * panel, const void *ui,
1191 compute_dir_size_callback cback,
1192 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1194 int i;
1196 *ret_marked = 0;
1197 *ret_total = 0;
1199 for (i = 0; i < panel->count; i++)
1201 struct stat *s;
1203 if (!panel->dir.list[i].f.marked)
1204 continue;
1206 s = &panel->dir.list[i].st;
1208 if (S_ISDIR (s->st_mode))
1210 char *dir_name;
1211 size_t subdir_count = 0;
1212 uintmax_t subdir_bytes = 0;
1213 FileProgressStatus status;
1215 dir_name = concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1217 status = compute_dir_size (dir_name, ui, cback,
1218 &subdir_count, &subdir_bytes, compute_symlinks);
1219 g_free (dir_name);
1221 if (status != FILE_CONT)
1222 return FILE_ABORT;
1224 *ret_marked += subdir_count;
1225 *ret_total += subdir_bytes;
1227 else
1229 (*ret_marked)++;
1230 *ret_total += (uintmax_t) s->st_size;
1234 return FILE_CONT;
1237 /* --------------------------------------------------------------------------------------------- */
1239 /** Initialize variables for progress bars */
1240 static FileProgressStatus
1241 panel_operate_init_totals (FileOperation operation,
1242 const WPanel * panel, const char *source, FileOpContext * ctx)
1244 FileProgressStatus status;
1246 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1248 ComputeDirSizeUI *ui;
1250 ui = compute_dir_size_create_ui ();
1252 if (source != NULL)
1253 status = compute_dir_size (source, ui, compute_dir_size_update_ui,
1254 &ctx->progress_count, &ctx->progress_bytes,
1255 ctx->follow_links);
1256 else
1257 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1258 &ctx->progress_count, &ctx->progress_bytes,
1259 ctx->follow_links);
1261 compute_dir_size_destroy_ui (ui);
1263 ctx->progress_totals_computed = (status == FILE_CONT);
1265 else
1267 status = FILE_CONT;
1268 ctx->progress_count = panel->marked;
1269 ctx->progress_bytes = panel->total;
1270 ctx->progress_totals_computed = FALSE;
1273 return status;
1276 /* --------------------------------------------------------------------------------------------- */
1278 * Generate user prompt for panel operation.
1279 * single_source is the name if the source entry or NULL for multiple
1280 * entries.
1281 * src_stat is only used when single_source is not NULL.
1284 static char *
1285 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1286 gboolean single_source, const struct stat *src_stat)
1288 const char *sp, *cp;
1289 char format_string[BUF_MEDIUM];
1290 char *dp = format_string;
1291 gboolean build_question = FALSE;
1293 static gboolean i18n_flag = FALSE;
1294 if (!i18n_flag)
1296 size_t i;
1298 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1299 op_names1[i] = Q_ (op_names1[i]);
1301 #ifdef ENABLE_NLS
1302 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1303 prompt_parts[i] = _(prompt_parts[i]);
1305 one_format = _(one_format);
1306 many_format = _(many_format);
1307 question_format = _(question_format);
1308 #endif /* ENABLE_NLS */
1309 i18n_flag = TRUE;
1312 sp = single_source ? one_format : many_format;
1314 while (*sp != '\0')
1316 switch (*sp)
1318 case '%':
1319 cp = NULL;
1320 switch (sp[1])
1322 case 'o':
1323 cp = op_names1[operation];
1324 break;
1325 case 'm':
1326 if (operation == OP_DELETE)
1328 cp = "";
1329 build_question = TRUE;
1331 else
1332 cp = prompt_parts[5];
1333 break;
1334 case 'e':
1335 if (operation == OP_DELETE)
1337 cp = "";
1338 build_question = TRUE;
1340 else
1341 cp = prompt_parts[6];
1342 break;
1343 case 'f':
1344 if (single_source)
1345 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1346 else
1347 cp = (panel->marked == panel->dirs_marked)
1348 ? prompt_parts[3]
1349 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1350 break;
1351 default:
1352 *dp++ = *sp++;
1355 if (cp != NULL)
1357 sp += 2;
1358 while (*cp != '\0')
1359 *dp++ = *cp++;
1361 break;
1362 default:
1363 *dp++ = *sp++;
1366 *dp = '\0';
1368 if (build_question)
1370 char tmp[BUF_MEDIUM];
1372 memmove (tmp, format_string, sizeof (tmp));
1373 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1376 return g_strdup (format_string);
1379 /* --------------------------------------------------------------------------------------------- */
1381 #ifdef ENABLE_BACKGROUND
1382 static int
1383 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1385 int pid = ctx->pid;
1387 (void) mode;
1388 ctx->pid = 0;
1390 unregister_task_with_pid (pid);
1391 /* file_op_context_destroy(ctx); */
1392 return 1;
1394 #endif
1395 /* }}} */
1397 /* --------------------------------------------------------------------------------------------- */
1398 /*** public functions ****************************************************************************/
1399 /* --------------------------------------------------------------------------------------------- */
1401 FileProgressStatus
1402 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1403 const char *src_path, const char *dst_path)
1405 uid_t src_uid = (uid_t) - 1;
1406 gid_t src_gid = (gid_t) - 1;
1408 int src_desc, dest_desc = -1;
1409 int n_read, n_written;
1410 mode_t src_mode = 0; /* The mode of the source file */
1411 struct stat sb, sb2;
1412 struct utimbuf utb;
1413 gboolean dst_exists = FALSE, appending = FALSE;
1414 off_t file_size = -1;
1415 FileProgressStatus return_status, temp_status;
1416 struct timeval tv_transfer_start;
1417 dest_status_t dst_status = DEST_NONE;
1418 int open_flags;
1419 gboolean is_first_time = TRUE;
1420 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1422 /* FIXME: We should not be using global variables! */
1423 ctx->do_reget = 0;
1424 return_status = FILE_RETRY;
1426 file_progress_show_source (ctx, src_path);
1427 file_progress_show_target (ctx, dst_path);
1428 if (check_progress_buttons (ctx) == FILE_ABORT)
1429 return FILE_ABORT;
1431 mc_refresh ();
1433 dst_vpath = vfs_path_from_str (dst_path);
1434 while (mc_stat (dst_vpath, &sb2) == 0)
1436 if (S_ISDIR (sb2.st_mode))
1438 if (ctx->skip_all)
1439 return_status = FILE_SKIPALL;
1440 else
1442 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1443 if (return_status == FILE_SKIPALL)
1444 ctx->skip_all = TRUE;
1445 if (return_status == FILE_RETRY)
1446 continue;
1448 goto ret_fast;
1450 dst_exists = TRUE;
1451 break;
1454 src_vpath = vfs_path_from_str (src_path);
1455 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1457 if (ctx->skip_all)
1458 return_status = FILE_SKIPALL;
1459 else
1461 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1462 if (return_status == FILE_SKIPALL)
1463 ctx->skip_all = TRUE;
1465 if (return_status != FILE_RETRY)
1466 goto ret_fast;
1469 if (dst_exists)
1471 /* Destination already exists */
1472 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1474 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1475 src_path, dst_path);
1476 goto ret_fast;
1478 /* Should we replace destination? */
1479 if (tctx->ask_overwrite)
1481 ctx->do_reget = 0;
1482 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1483 if (return_status != FILE_CONT)
1484 goto ret_fast;
1488 if (!ctx->do_append)
1490 /* Check the hardlinks */
1491 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb))
1493 /* We have made a hardlink - no more processing is necessary */
1494 return_status = FILE_CONT;
1495 goto ret_fast;
1498 if (S_ISLNK (sb.st_mode))
1500 return_status = make_symlink (ctx, src_path, dst_path);
1501 goto ret_fast;
1504 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1505 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1507 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1508 && !ctx->skip_all)
1510 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1511 if (return_status == FILE_RETRY)
1512 continue;
1513 if (return_status == FILE_SKIPALL)
1514 ctx->skip_all = TRUE;
1515 goto ret_fast;
1517 /* Success */
1519 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1520 && !ctx->skip_all)
1522 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1523 if (temp_status == FILE_SKIP)
1524 break;
1525 if (temp_status == FILE_SKIPALL)
1526 ctx->skip_all = TRUE;
1527 if (temp_status != FILE_RETRY)
1529 return_status = temp_status;
1530 goto ret_fast;
1534 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1535 && !ctx->skip_all)
1537 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1538 if (temp_status == FILE_SKIP)
1539 break;
1540 if (temp_status == FILE_SKIPALL)
1541 ctx->skip_all = TRUE;
1542 if (temp_status != FILE_RETRY)
1544 return_status = temp_status;
1545 goto ret_fast;
1549 return_status = FILE_CONT;
1550 goto ret_fast;
1554 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1556 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1558 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1559 if (return_status == FILE_RETRY)
1560 continue;
1561 if (return_status == FILE_SKIPALL)
1562 ctx->skip_all = TRUE;
1563 if (return_status == FILE_SKIP)
1564 break;
1565 ctx->do_append = 0;
1566 goto ret_fast;
1569 if (ctx->do_reget != 0)
1571 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1573 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1574 ctx->do_reget = 0;
1575 ctx->do_append = FALSE;
1579 while (mc_fstat (src_desc, &sb) != 0)
1581 if (ctx->skip_all)
1582 return_status = FILE_SKIPALL;
1583 else
1585 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1586 if (return_status == FILE_RETRY)
1587 continue;
1588 if (return_status == FILE_SKIPALL)
1589 ctx->skip_all = TRUE;
1590 ctx->do_append = FALSE;
1592 goto ret;
1594 src_mode = sb.st_mode;
1595 src_uid = sb.st_uid;
1596 src_gid = sb.st_gid;
1597 utb.actime = sb.st_atime;
1598 utb.modtime = sb.st_mtime;
1599 file_size = sb.st_size;
1601 open_flags = O_WRONLY;
1602 if (dst_exists)
1604 if (ctx->do_append != 0)
1605 open_flags |= O_APPEND;
1606 else
1607 open_flags |= O_CREAT | O_TRUNC;
1609 else
1611 open_flags |= O_CREAT | O_EXCL;
1614 while ((dest_desc = mc_open (dst_path, open_flags, src_mode)) < 0)
1616 if (errno != EEXIST)
1618 if (ctx->skip_all)
1619 return_status = FILE_SKIPALL;
1620 else
1622 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1623 if (return_status == FILE_RETRY)
1624 continue;
1625 if (return_status == FILE_SKIPALL)
1626 ctx->skip_all = TRUE;
1627 ctx->do_append = FALSE;
1630 goto ret;
1632 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1634 appending = ctx->do_append;
1635 ctx->do_append = FALSE;
1637 /* Find out the optimal buffer size. */
1638 while (mc_fstat (dest_desc, &sb) != 0)
1640 if (ctx->skip_all)
1641 return_status = FILE_SKIPALL;
1642 else
1644 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1645 if (return_status == FILE_RETRY)
1646 continue;
1647 if (return_status == FILE_SKIPALL)
1648 ctx->skip_all = TRUE;
1650 goto ret;
1653 while (TRUE)
1655 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1656 if (errno == 0)
1657 break;
1659 if (ctx->skip_all)
1660 return_status = FILE_SKIPALL;
1661 else
1663 return_status =
1664 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1665 if (return_status == FILE_RETRY)
1666 continue;
1667 if (return_status == FILE_SKIPALL)
1668 ctx->skip_all = TRUE;
1670 mc_close (dest_desc);
1671 dest_desc = -1;
1672 mc_unlink (dst_vpath);
1673 dst_status = DEST_NONE;
1674 goto ret;
1677 ctx->eta_secs = 0.0;
1678 ctx->bps = 0;
1680 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1681 file_progress_show (ctx, 0, file_size, "", TRUE);
1682 else
1683 file_progress_show (ctx, 1, 1, "", TRUE);
1684 return_status = check_progress_buttons (ctx);
1685 mc_refresh ();
1687 if (return_status != FILE_CONT)
1688 goto ret;
1691 off_t n_read_total = 0;
1692 struct timeval tv_current, tv_last_update, tv_last_input;
1693 int secs, update_secs;
1694 const char *stalled_msg = "";
1696 tv_last_update = tv_transfer_start;
1698 while (TRUE)
1700 char buf[BUF_8K];
1702 /* src_read */
1703 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1704 n_read = -1;
1705 else
1706 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1708 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1709 if (return_status == FILE_RETRY)
1710 continue;
1711 if (return_status == FILE_SKIPALL)
1712 ctx->skip_all = TRUE;
1713 goto ret;
1715 if (n_read == 0)
1716 break;
1718 gettimeofday (&tv_current, NULL);
1720 if (n_read > 0)
1722 char *t = buf;
1723 n_read_total += n_read;
1725 /* Windows NT ftp servers report that files have no
1726 * permissions: -------, so if we happen to have actually
1727 * read something, we should fix the permissions.
1729 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1730 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1731 gettimeofday (&tv_last_input, NULL);
1733 /* dst_write */
1734 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read && !ctx->skip_all)
1736 if (n_written > 0)
1738 n_read -= n_written;
1739 t += n_written;
1740 continue;
1742 return_status = file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1743 if (return_status == FILE_SKIP)
1744 break;
1745 if (return_status == FILE_SKIPALL)
1746 ctx->skip_all = TRUE;
1747 if (return_status != FILE_RETRY)
1748 goto ret;
1752 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1754 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1755 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1757 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1759 copy_file_file_display_progress (tctx, ctx,
1760 tv_current,
1761 tv_transfer_start, file_size, n_read_total);
1762 tv_last_update = tv_current;
1764 is_first_time = FALSE;
1766 if (update_secs > FILEOP_STALLING_INTERVAL)
1768 stalled_msg = _("(stalled)");
1772 gboolean force_update;
1774 force_update =
1775 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1777 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1779 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1780 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1783 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1784 force_update);
1786 mc_refresh ();
1788 return_status = check_progress_buttons (ctx);
1790 if (return_status != FILE_CONT)
1792 mc_refresh ();
1793 goto ret;
1798 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1800 ret:
1801 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1803 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1804 if (temp_status == FILE_RETRY)
1805 continue;
1806 if (temp_status == FILE_ABORT)
1807 return_status = temp_status;
1808 if (temp_status == FILE_SKIPALL)
1809 ctx->skip_all = TRUE;
1810 break;
1813 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1815 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1816 if (temp_status == FILE_RETRY)
1817 continue;
1818 if (temp_status == FILE_SKIPALL)
1819 ctx->skip_all = TRUE;
1820 return_status = temp_status;
1821 break;
1824 if (dst_status == DEST_SHORT)
1826 /* Remove short file */
1827 int result;
1829 result = query_dialog (Q_ ("DialogTitle|Copy"),
1830 _("Incomplete file was retrieved. Keep it?"),
1831 D_ERROR, 2, _("&Delete"), _("&Keep"));
1832 if (result == 0)
1833 mc_unlink (dst_vpath);
1835 else if (dst_status == DEST_FULL)
1837 /* Copy has succeeded */
1838 if (!appending && ctx->preserve_uidgid)
1840 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1842 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1843 if (temp_status == FILE_RETRY)
1844 continue;
1845 if (temp_status == FILE_SKIPALL)
1847 ctx->skip_all = TRUE;
1848 return_status = FILE_CONT;
1850 if (temp_status == FILE_SKIP)
1851 return_status = FILE_CONT;
1852 break;
1856 if (!appending)
1858 if (ctx->preserve)
1860 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1862 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1863 if (temp_status == FILE_RETRY)
1864 continue;
1865 if (temp_status == FILE_SKIPALL)
1867 ctx->skip_all = TRUE;
1868 return_status = FILE_CONT;
1870 if (temp_status == FILE_SKIP)
1871 return_status = FILE_CONT;
1872 break;
1875 else if (!dst_exists)
1877 src_mode = umask (-1);
1878 umask (src_mode);
1879 src_mode = 0100666 & ~src_mode;
1880 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
1882 mc_utime (dst_vpath, &utb);
1886 if (return_status == FILE_CONT)
1887 return_status = progress_update_one (tctx, ctx, file_size);
1889 ret_fast:
1890 vfs_path_free (src_vpath);
1891 vfs_path_free (dst_vpath);
1892 return return_status;
1895 /* --------------------------------------------------------------------------------------------- */
1897 * I think these copy_*_* functions should have a return type.
1898 * anyway, this function *must* have two directories as arguments.
1900 /* FIXME: This function needs to check the return values of the
1901 function calls */
1903 FileProgressStatus
1904 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1905 gboolean toplevel, gboolean move_over, gboolean do_delete, struct link * parent_dirs)
1907 struct dirent *next;
1908 struct stat buf, cbuf;
1909 DIR *reading;
1910 char *dest_dir = NULL;
1911 FileProgressStatus return_status = FILE_CONT;
1912 struct utimbuf utb;
1913 struct link *lp;
1914 char *d;
1915 vfs_path_t *src_vpath, *dst_vpath, *dest_dir_vpath = NULL;
1917 d = g_strdup (_d);
1919 src_vpath = vfs_path_from_str (s);
1920 dst_vpath = vfs_path_from_str (_d);
1922 /* First get the mode of the source dir */
1924 retry_src_stat:
1925 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
1927 if (ctx->skip_all)
1928 return_status = FILE_SKIPALL;
1929 else
1931 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1932 if (return_status == FILE_RETRY)
1933 goto retry_src_stat;
1934 if (return_status == FILE_SKIPALL)
1935 ctx->skip_all = TRUE;
1937 goto ret_fast;
1940 if (is_in_linklist (dest_dirs, s, &cbuf))
1942 /* Don't copy a directory we created before (we don't want to copy
1943 infinitely if a directory is copied into itself) */
1944 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1945 return_status = FILE_CONT;
1946 goto ret_fast;
1949 /* Hmm, hardlink to directory??? - Norbert */
1950 /* FIXME: In this step we should do something
1951 in case the destination already exist */
1952 /* Check the hardlinks */
1953 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf))
1955 /* We have made a hardlink - no more processing is necessary */
1956 goto ret_fast;
1959 if (!S_ISDIR (cbuf.st_mode))
1961 if (ctx->skip_all)
1962 return_status = FILE_SKIPALL;
1963 else
1965 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1966 if (return_status == FILE_RETRY)
1967 goto retry_src_stat;
1968 if (return_status == FILE_SKIPALL)
1969 ctx->skip_all = TRUE;
1971 goto ret_fast;
1974 if (is_in_linklist (parent_dirs, s, &cbuf))
1976 /* we found a cyclic symbolic link */
1977 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
1978 return_status = FILE_SKIP;
1979 goto ret_fast;
1982 lp = g_new (struct link, 1);
1983 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
1984 lp->ino = cbuf.st_ino;
1985 lp->dev = cbuf.st_dev;
1986 lp->next = parent_dirs;
1987 parent_dirs = lp;
1989 retry_dst_stat:
1990 /* Now, check if the dest dir exists, if not, create it. */
1991 if (mc_stat (dst_vpath, &buf) != 0)
1993 /* Here the dir doesn't exist : make it ! */
1994 if (move_over)
1996 if (mc_rename (s, d) == 0)
1998 return_status = FILE_CONT;
1999 goto ret;
2002 dest_dir = d;
2003 d = NULL;
2005 else
2008 * If the destination directory exists, we want to copy the whole
2009 * directory, but we only want this to happen once.
2011 * Escape sequences added to the * to compiler warnings.
2012 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2013 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2015 if (!S_ISDIR (buf.st_mode))
2017 if (ctx->skip_all)
2018 return_status = FILE_SKIPALL;
2019 else
2021 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2022 if (return_status == FILE_SKIPALL)
2023 ctx->skip_all = TRUE;
2024 if (return_status == FILE_RETRY)
2025 goto retry_dst_stat;
2027 goto ret;
2029 /* Dive into subdir if exists */
2030 if (toplevel && ctx->dive_into_subdirs)
2032 dest_dir = concat_dir_and_file (d, x_basename (s));
2034 else
2036 dest_dir = d;
2037 d = NULL;
2038 goto dont_mkdir;
2041 dest_dir_vpath = vfs_path_from_str (dest_dir);
2042 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU))
2044 if (ctx->skip_all)
2045 return_status = FILE_SKIPALL;
2046 else
2048 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
2049 if (return_status == FILE_SKIPALL)
2050 ctx->skip_all = TRUE;
2052 if (return_status != FILE_RETRY)
2053 goto ret;
2056 lp = g_new0 (struct link, 1);
2057 mc_stat (dest_dir_vpath, &buf);
2058 lp->vfs = vfs_path_get_by_index (dest_dir_vpath, -1)->class;
2059 lp->ino = buf.st_ino;
2060 lp->dev = buf.st_dev;
2061 lp->next = dest_dirs;
2062 dest_dirs = lp;
2064 if (ctx->preserve_uidgid)
2066 while (mc_chown (dest_dir_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2068 if (ctx->skip_all)
2069 return_status = FILE_SKIPALL;
2070 else
2072 return_status =
2073 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
2074 if (return_status == FILE_SKIPALL)
2075 ctx->skip_all = TRUE;
2077 if (return_status != FILE_RETRY)
2078 goto ret;
2082 dont_mkdir:
2083 /* open the source dir for reading */
2084 reading = mc_opendir (src_vpath);
2085 if (reading == NULL)
2086 goto ret;
2088 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2090 char *path;
2091 vfs_path_t *tmp_vpath;
2093 * Now, we don't want '.' and '..' to be created / copied at any time
2095 if (!strcmp (next->d_name, "."))
2096 continue;
2097 if (!strcmp (next->d_name, ".."))
2098 continue;
2100 /* get the filename and add it to the src directory */
2101 path = concat_dir_and_file (s, next->d_name);
2102 tmp_vpath = vfs_path_from_str (path);
2104 (*ctx->stat_func) (tmp_vpath, &buf);
2105 if (S_ISDIR (buf.st_mode))
2107 char *mdpath;
2109 mdpath = concat_dir_and_file (dest_dir, next->d_name);
2111 * From here, we just intend to recursively copy subdirs, not
2112 * the double functionality of copying different when the target
2113 * dir already exists. So, we give the recursive call the flag 0
2114 * meaning no toplevel.
2116 return_status =
2117 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2118 g_free (mdpath);
2120 else
2122 char *dest_file;
2124 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
2125 return_status = copy_file_file (tctx, ctx, path, dest_file);
2126 g_free (dest_file);
2128 if (do_delete && return_status == FILE_CONT)
2130 if (ctx->erase_at_end)
2132 static struct link *tail;
2133 size_t len = strlen (path);
2134 lp = g_malloc (sizeof (struct link) + len);
2135 strncpy (lp->name, path, len + 1);
2136 lp->st_mode = buf.st_mode;
2137 lp->next = NULL;
2138 if (erase_list != NULL)
2140 tail->next = lp;
2141 tail = lp;
2143 else
2144 erase_list = tail = lp;
2146 else
2148 if (S_ISDIR (buf.st_mode))
2150 return_status = erase_dir_iff_empty (ctx, path);
2152 else
2153 return_status = erase_file (tctx, ctx, path);
2156 g_free (path);
2157 vfs_path_free (tmp_vpath);
2159 mc_closedir (reading);
2161 if (ctx->preserve)
2163 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2164 utb.actime = cbuf.st_atime;
2165 utb.modtime = cbuf.st_mtime;
2166 mc_utime (dest_dir_vpath, &utb);
2168 else
2170 cbuf.st_mode = umask (-1);
2171 umask (cbuf.st_mode);
2172 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2173 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2176 ret:
2177 g_free (dest_dir);
2178 vfs_path_free (dest_dir_vpath);
2179 g_free (parent_dirs);
2180 ret_fast:
2181 g_free (d);
2182 vfs_path_free (src_vpath);
2183 vfs_path_free (dst_vpath);
2184 return return_status;
2187 /* }}} */
2189 /* --------------------------------------------------------------------------------------------- */
2190 /* {{{ Move routines */
2192 FileProgressStatus
2193 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2195 struct stat sbuf, dbuf, destbuf;
2196 struct link *lp;
2197 char *destdir;
2198 FileProgressStatus return_status;
2199 gboolean move_over = FALSE;
2200 gboolean dstat_ok;
2201 vfs_path_t *src_vpath, *dst_vpath, *destdir_vpath;
2203 src_vpath = vfs_path_from_str (s);
2204 dst_vpath = vfs_path_from_str (d);
2206 file_progress_show_source (ctx, s);
2207 file_progress_show_target (ctx, d);
2208 if (check_progress_buttons (ctx) == FILE_ABORT)
2209 return FILE_ABORT;
2211 mc_refresh ();
2213 mc_stat (src_vpath, &sbuf);
2215 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2217 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2218 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2220 if (!dstat_ok)
2221 destdir = g_strdup (d); /* destination doesn't exist */
2222 else if (!ctx->dive_into_subdirs)
2224 destdir = g_strdup (d);
2225 move_over = TRUE;
2227 else
2228 destdir = concat_dir_and_file (d, x_basename (s));
2230 destdir_vpath = vfs_path_from_str (destdir);
2232 /* Check if the user inputted an existing dir */
2233 retry_dst_stat:
2234 if (mc_stat (destdir_vpath, &destbuf) == 0)
2236 if (move_over)
2238 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2240 if (return_status != FILE_CONT)
2241 goto ret;
2242 goto oktoret;
2244 else if (ctx->skip_all)
2245 return_status = FILE_SKIPALL;
2246 else
2248 if (S_ISDIR (destbuf.st_mode))
2249 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2250 else
2251 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2252 if (return_status == FILE_SKIPALL)
2253 ctx->skip_all = TRUE;
2254 if (return_status == FILE_RETRY)
2255 goto retry_dst_stat;
2257 g_free (destdir);
2258 vfs_path_free (destdir_vpath);
2259 vfs_path_free (src_vpath);
2260 vfs_path_free (dst_vpath);
2261 return return_status;
2264 retry_rename:
2265 if (mc_rename (s, destdir) == 0)
2267 return_status = FILE_CONT;
2268 goto ret;
2271 if (errno != EXDEV)
2273 if (!ctx->skip_all)
2275 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2276 if (return_status == FILE_SKIPALL)
2277 ctx->skip_all = TRUE;
2278 if (return_status == FILE_RETRY)
2279 goto retry_rename;
2281 goto ret;
2283 /* Failed because of filesystem boundary -> copy dir instead */
2284 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2286 if (return_status != FILE_CONT)
2287 goto ret;
2288 oktoret:
2289 file_progress_show_source (ctx, NULL);
2290 file_progress_show (ctx, 0, 0, "", FALSE);
2292 return_status = check_progress_buttons (ctx);
2293 if (return_status != FILE_CONT)
2294 goto ret;
2296 mc_refresh ();
2297 if (ctx->erase_at_end)
2299 for (; erase_list && return_status != FILE_ABORT;)
2301 if (S_ISDIR (erase_list->st_mode))
2303 return_status = erase_dir_iff_empty (ctx, erase_list->name);
2305 else
2306 return_status = erase_file (tctx, ctx, erase_list->name);
2307 lp = erase_list;
2308 erase_list = erase_list->next;
2309 g_free (lp);
2312 erase_dir_iff_empty (ctx, s);
2314 ret:
2315 g_free (destdir);
2316 vfs_path_free (destdir_vpath);
2317 while (erase_list)
2319 lp = erase_list;
2320 erase_list = erase_list->next;
2321 g_free (lp);
2323 vfs_path_free (src_vpath);
2324 vfs_path_free (dst_vpath);
2325 return return_status;
2328 /* }}} */
2330 /* --------------------------------------------------------------------------------------------- */
2331 /* {{{ Erase routines */
2333 FileProgressStatus
2334 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
2336 FileProgressStatus error;
2338 if (strcmp (s, "..") == 0)
2339 return FILE_SKIP;
2341 if (strcmp (s, ".") == 0)
2342 return FILE_SKIP;
2344 file_progress_show_deleting (ctx, s);
2345 if (check_progress_buttons (ctx) == FILE_ABORT)
2346 return FILE_ABORT;
2347 mc_refresh ();
2349 /* The old way to detect a non empty directory was:
2350 error = my_rmdir (s);
2351 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2352 For the linux user space nfs server (nfs-server-2.2beta29-2)
2353 we would have to check also for EIO. I hope the new way is
2354 fool proof. (Norbert)
2356 error = check_dir_is_empty (s);
2357 if (error == 0)
2358 { /* not empty */
2359 error = query_recursive (ctx, s);
2360 if (error == FILE_CONT)
2361 return recursive_erase (tctx, ctx, s);
2362 else
2363 return error;
2366 while (my_rmdir (s) == -1 && !ctx->skip_all)
2368 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2369 if (error != FILE_RETRY)
2370 return error;
2373 return FILE_CONT;
2376 /* }}} */
2378 /* --------------------------------------------------------------------------------------------- */
2379 /* {{{ Panel operate routines */
2381 ComputeDirSizeUI *
2382 compute_dir_size_create_ui (void)
2384 ComputeDirSizeUI *ui;
2386 const char *b_name = N_("&Abort");
2388 #ifdef ENABLE_NLS
2389 b_name = _(b_name);
2390 #endif
2392 ui = g_new (ComputeDirSizeUI, 1);
2394 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
2395 NULL, _("Directory scanning"), DLG_CENTER);
2396 ui->dirname = label_new (3, 3, "");
2397 add_widget (ui->dlg, ui->dirname);
2399 add_widget (ui->dlg,
2400 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
2401 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2403 /* We will manage the dialog without any help,
2404 that's why we have to call init_dlg */
2405 init_dlg (ui->dlg);
2407 return ui;
2410 /* --------------------------------------------------------------------------------------------- */
2412 void
2413 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2415 if (ui != NULL)
2417 /* schedule to update passive panel */
2418 other_panel->dirty = 1;
2420 /* close and destroy dialog */
2421 dlg_run_done (ui->dlg);
2422 destroy_dlg (ui->dlg);
2423 g_free (ui);
2427 /* --------------------------------------------------------------------------------------------- */
2429 FileProgressStatus
2430 compute_dir_size_update_ui (const void *ui, const char *dirname)
2432 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2433 int c;
2434 Gpm_Event event;
2436 if (ui == NULL)
2437 return FILE_CONT;
2439 label_set_text (this->dirname, str_trunc (dirname, this->dlg->cols - 6));
2441 event.x = -1; /* Don't show the GPM cursor */
2442 c = tty_get_event (&event, FALSE, FALSE);
2443 if (c == EV_NONE)
2444 return FILE_CONT;
2446 /* Reinitialize to avoid old values after events other than
2447 selecting a button */
2448 this->dlg->ret_value = FILE_CONT;
2450 dlg_process_event (this->dlg, c, &event);
2452 switch (this->dlg->ret_value)
2454 case B_CANCEL:
2455 case FILE_ABORT:
2456 return FILE_ABORT;
2457 default:
2458 return FILE_CONT;
2462 /* --------------------------------------------------------------------------------------------- */
2464 * compute_dir_size:
2466 * Computes the number of bytes used by the files in a directory
2469 FileProgressStatus
2470 compute_dir_size (const char *dirname, const void *ui,
2471 compute_dir_size_callback cback,
2472 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2474 int res;
2475 struct stat s;
2476 DIR *dir;
2477 struct dirent *dirent;
2478 FileProgressStatus ret = FILE_CONT;
2479 vfs_path_t *vpath;
2481 vpath = vfs_path_from_str (dirname);
2483 if (!compute_symlinks)
2485 res = mc_lstat (vpath, &s);
2486 if (res != 0)
2487 goto ret;
2489 /* don't scan symlink to directory */
2490 if (S_ISLNK (s.st_mode))
2492 (*ret_marked)++;
2493 *ret_total += (uintmax_t) s.st_size;
2494 goto ret;
2498 dir = mc_opendir (vpath);
2500 if (dir == NULL)
2501 goto ret;
2503 while ((dirent = mc_readdir (dir)) != NULL)
2505 char *fullname;
2506 vfs_path_t *tmp_vpath;
2508 ret = (cback != NULL) ? cback (ui, dirname) : FILE_CONT;
2510 if (ret != FILE_CONT)
2511 break;
2513 if (strcmp (dirent->d_name, ".") == 0)
2514 continue;
2515 if (strcmp (dirent->d_name, "..") == 0)
2516 continue;
2518 fullname = concat_dir_and_file (dirname, dirent->d_name);
2519 tmp_vpath = vfs_path_from_str (fullname);
2520 res = mc_lstat (tmp_vpath, &s);
2522 if (res != 0)
2524 g_free (fullname);
2525 vfs_path_free (tmp_vpath);
2526 continue;
2529 if (S_ISDIR (s.st_mode))
2531 size_t subdir_count = 0;
2532 uintmax_t subdir_bytes = 0;
2534 ret =
2535 compute_dir_size (fullname, ui, cback, &subdir_count, &subdir_bytes,
2536 compute_symlinks);
2538 if (ret != FILE_CONT)
2540 g_free (fullname);
2541 vfs_path_free (tmp_vpath);
2542 break;
2545 *ret_marked += subdir_count;
2546 *ret_total += subdir_bytes;
2548 else
2550 (*ret_marked)++;
2551 *ret_total += (uintmax_t) s.st_size;
2554 g_free (fullname);
2555 vfs_path_free (tmp_vpath);
2558 mc_closedir (dir);
2559 ret:
2560 vfs_path_free (vpath);
2561 return ret;
2564 /* --------------------------------------------------------------------------------------------- */
2566 * panel_operate:
2568 * Performs one of the operations on the selection on the source_panel
2569 * (copy, delete, move).
2571 * Returns TRUE if did change the directory
2572 * structure, Returns FALSE if user aborted
2574 * force_single forces operation on the current entry and affects
2575 * default destination. Current filename is used as default.
2578 gboolean
2579 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2581 WPanel *panel = (WPanel *) source_panel;
2582 const gboolean single_entry = force_single || (panel->marked <= 1)
2583 || (get_current_type () == view_tree);
2585 char *source = NULL;
2586 #ifdef WITH_FULL_PATHS
2587 char *source_with_path = NULL;
2588 #else
2589 #define source_with_path source
2590 #endif /* !WITH_FULL_PATHS */
2591 char *dest = NULL;
2592 vfs_path_t *dest_vpath = NULL;
2593 char *temp = NULL;
2594 char *save_cwd = NULL, *save_dest = NULL;
2595 struct stat src_stat;
2596 gboolean ret_val = TRUE;
2597 int i;
2598 FileProgressStatus value;
2599 FileOpContext *ctx;
2600 FileOpTotalContext *tctx;
2601 vfs_path_t *tmp_vpath;
2603 gboolean do_bg = FALSE; /* do background operation? */
2605 static gboolean i18n_flag = FALSE;
2606 if (!i18n_flag)
2608 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2609 op_names[i] = Q_ (op_names[i]);
2610 i18n_flag = TRUE;
2613 free_linklist (&linklist);
2614 free_linklist (&dest_dirs);
2616 if (single_entry)
2618 vfs_path_t *source_vpath;
2620 if (force_single)
2621 source = selection (panel)->fname;
2622 else
2623 source = panel_get_file (panel);
2625 if (strcmp (source, "..") == 0)
2627 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2628 return FALSE;
2631 source_vpath = vfs_path_from_str (source);
2632 /* Update stat to get actual info */
2633 if (mc_lstat (source_vpath, &src_stat) != 0)
2635 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2636 path_trunc (source, 30), unix_error_string (errno));
2638 /* Directory was changed outside MC. Reload it forced */
2639 if (!panel->is_panelized)
2641 panel_update_flags_t flags = UP_RELOAD;
2643 /* don't update panelized panel */
2644 if (get_other_type () == view_listing && other_panel->is_panelized)
2645 flags |= UP_ONLY_CURRENT;
2647 update_panels (flags, UP_KEEPSEL);
2649 vfs_path_free (source_vpath);
2650 return FALSE;
2652 vfs_path_free (source_vpath);
2655 ctx = file_op_context_new (operation);
2657 /* Show confirmation dialog */
2658 if (operation != OP_DELETE)
2660 char *dest_dir;
2661 char *dest_dir_;
2662 char *format;
2664 /* Forced single operations default to the original name */
2665 if (force_single)
2666 dest_dir = source;
2667 else if (get_other_type () == view_listing)
2668 dest_dir = other_panel->cwd;
2669 else
2670 dest_dir = panel->cwd;
2672 * Add trailing backslash only when do non-local ops.
2673 * It saves user from occasional file renames (when destination
2674 * dir is deleted)
2676 if (!force_single && dest_dir[0] != '\0' && dest_dir[strlen (dest_dir) - 1] != PATH_SEP)
2678 /* add trailing separator */
2679 dest_dir_ = g_strconcat (dest_dir, PATH_SEP_STR, (char *) NULL);
2681 else
2683 /* just copy */
2684 dest_dir_ = g_strdup (dest_dir);
2687 if (dest_dir_ == NULL)
2689 ret_val = FALSE;
2690 goto ret_fast;
2693 /* Generate confirmation prompt */
2694 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2696 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2697 source != NULL ? (void *) source
2698 : (void *) &panel->marked, dest_dir_, &do_bg);
2700 g_free (format);
2701 g_free (dest_dir_);
2703 if (dest == NULL || dest[0] == '\0')
2705 g_free (dest);
2706 ret_val = FALSE;
2707 goto ret_fast;
2709 dest_vpath = vfs_path_from_str (dest);
2711 else if (confirm_delete)
2713 char *format;
2714 char fmd_buf[BUF_MEDIUM];
2716 /* Generate confirmation prompt */
2717 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2719 if (source == NULL)
2720 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2721 else
2723 const int fmd_xlen = 64;
2724 i = fmd_xlen - str_term_width1 (format) - 4;
2725 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2728 g_free (format);
2730 if (safe_delete)
2731 query_set_sel (1);
2733 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2735 if (i != 0)
2737 ret_val = FALSE;
2738 goto ret_fast;
2742 tctx = file_op_total_context_new ();
2743 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2746 filegui_dialog_type_t dialog_type;
2748 if (operation == OP_DELETE)
2749 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2750 else
2752 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2753 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2755 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2756 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2759 /* Background also need ctx->ui, but not full */
2760 if (do_bg)
2761 file_op_context_create_ui_without_init (ctx, TRUE, dialog_type);
2762 else
2763 file_op_context_create_ui (ctx, TRUE, dialog_type);
2766 #ifdef ENABLE_BACKGROUND
2767 /* Did the user select to do a background operation? */
2768 if (do_bg)
2770 int v;
2772 v = do_background (ctx, g_strconcat (op_names[operation], ": ", panel->cwd, (char *) NULL));
2773 if (v == -1)
2774 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2776 /* If we are the parent */
2777 if (v == 1)
2779 tmp_vpath = vfs_path_from_str (panel->cwd);
2780 mc_setctl (tmp_vpath, VFS_SETCTL_FORGET, NULL);
2781 vfs_path_free (tmp_vpath);
2783 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
2784 vfs_path_free (dest_vpath);
2785 g_free (dest);
2786 /* file_op_context_destroy (ctx); */
2787 return FALSE;
2790 #endif /* ENABLE_BACKGROUND */
2792 /* Initialize things */
2793 /* We do not want to trash cache every time file is
2794 created/touched. However, this will make our cache contain
2795 invalid data. */
2796 if ((dest != NULL) && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2797 save_dest = g_strdup (dest);
2799 tmp_vpath = vfs_path_from_str (panel->cwd);
2800 if ((panel->cwd[0] != '\0') && (mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2801 save_cwd = g_strdup (panel->cwd);
2802 vfs_path_free (tmp_vpath);
2804 /* Now, let's do the job */
2806 /* This code is only called by the tree and panel code */
2807 if (single_entry)
2809 /* We now have ETA in all cases */
2811 /* One file: FIXME mc_chdir will take user out of any vfs */
2812 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2814 vfs_path_t *vpath;
2815 int chdir_retcode;
2817 vpath = vfs_path_from_str (PATH_SEP_STR);
2818 chdir_retcode = mc_chdir (vpath);
2819 vfs_path_free (vpath);
2820 if (chdir_retcode < 0)
2822 ret_val = FALSE;
2823 goto clean_up;
2827 /* The source and src_stat variables have been initialized before */
2828 #ifdef WITH_FULL_PATHS
2829 if (g_path_is_absolute (source))
2830 source_with_path = g_strdup (source);
2831 else
2832 source_with_path = mc_build_filename (panel->cwd, source, (char *) NULL);
2833 #endif /* WITH_FULL_PATHS */
2835 if (panel_operate_init_totals (operation, panel, source_with_path, ctx) == FILE_CONT)
2837 if (operation == OP_DELETE)
2839 if (S_ISDIR (src_stat.st_mode))
2840 value = erase_dir (tctx, ctx, source_with_path);
2841 else
2842 value = erase_file (tctx, ctx, source_with_path);
2844 else
2846 temp = transform_source (ctx, source_with_path);
2847 if (temp == NULL)
2848 value = transform_error;
2849 else
2851 char *repl_dest, *temp2;
2853 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2854 temp2 = concat_dir_and_file (repl_dest, temp);
2855 g_free (temp);
2856 g_free (repl_dest);
2857 g_free (dest);
2858 vfs_path_free (dest_vpath);
2859 dest = temp2;
2860 dest_vpath = vfs_path_from_str (dest);
2862 switch (operation)
2864 case OP_COPY:
2865 /* we use file_mask_op_follow_links only with OP_COPY */
2867 vfs_path_t *vpath = vfs_path_from_str (source_with_path);
2868 ctx->stat_func (vpath, &src_stat);
2869 vfs_path_free (vpath);
2872 if (S_ISDIR (src_stat.st_mode))
2873 value = copy_dir_dir (tctx, ctx, source_with_path, dest,
2874 TRUE, FALSE, FALSE, NULL);
2875 else
2876 value = copy_file_file (tctx, ctx, source_with_path, dest);
2877 break;
2879 case OP_MOVE:
2880 if (S_ISDIR (src_stat.st_mode))
2881 value = move_dir_dir (tctx, ctx, source_with_path, dest);
2882 else
2883 value = move_file_file (tctx, ctx, source_with_path, dest);
2884 break;
2886 default:
2887 /* Unknown file operation */
2888 abort ();
2891 } /* Copy or move operation */
2893 if ((value == FILE_CONT) && !force_single)
2894 unmark_files (panel);
2897 else
2899 /* Many files */
2901 /* Check destination for copy or move operation */
2902 while (operation != OP_DELETE)
2904 int dst_result;
2905 struct stat dst_stat;
2907 dst_result = mc_stat (dest_vpath, &dst_stat);
2909 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2910 break;
2912 if (ctx->skip_all
2913 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2914 goto clean_up;
2917 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2919 /* Loop for every file, perform the actual copy operation */
2920 for (i = 0; i < panel->count; i++)
2922 if (!panel->dir.list[i].f.marked)
2923 continue; /* Skip the unmarked ones */
2925 source = panel->dir.list[i].fname;
2926 src_stat = panel->dir.list[i].st;
2928 #ifdef WITH_FULL_PATHS
2929 g_free (source_with_path);
2930 if (g_path_is_absolute (source))
2931 source_with_path = g_strdup (source);
2932 else
2933 source_with_path = mc_build_filename (panel->cwd, source, (char *) NULL);
2934 #endif /* WITH_FULL_PATHS */
2936 if (operation == OP_DELETE)
2938 if (S_ISDIR (src_stat.st_mode))
2939 value = erase_dir (tctx, ctx, source_with_path);
2940 else
2941 value = erase_file (tctx, ctx, source_with_path);
2943 else
2945 temp = transform_source (ctx, source_with_path);
2947 if (temp == NULL)
2948 value = transform_error;
2949 else
2951 char *temp2, *temp3, *repl_dest;
2953 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2954 temp2 = concat_dir_and_file (repl_dest, temp);
2955 g_free (temp);
2956 g_free (repl_dest);
2957 temp3 = source_with_path;
2958 source_with_path = strutils_shell_unescape (source_with_path);
2959 g_free (temp3);
2960 temp3 = temp2;
2961 temp2 = strutils_shell_unescape (temp2);
2962 g_free (temp3);
2964 switch (operation)
2966 case OP_COPY:
2967 /* we use file_mask_op_follow_links only with OP_COPY */
2969 vfs_path_t *vpath;
2971 vpath = vfs_path_from_str (source_with_path);
2972 ctx->stat_func (vpath, &src_stat);
2973 vfs_path_free (vpath);
2975 if (S_ISDIR (src_stat.st_mode))
2976 value = copy_dir_dir (tctx, ctx, source_with_path, temp2,
2977 TRUE, FALSE, FALSE, NULL);
2978 else
2979 value = copy_file_file (tctx, ctx, source_with_path, temp2);
2980 free_linklist (&dest_dirs);
2981 break;
2983 case OP_MOVE:
2984 if (S_ISDIR (src_stat.st_mode))
2985 value = move_dir_dir (tctx, ctx, source_with_path, temp2);
2986 else
2987 value = move_file_file (tctx, ctx, source_with_path, temp2);
2988 break;
2990 default:
2991 /* Unknown file operation */
2992 abort ();
2995 g_free (temp2);
2997 } /* Copy or move operation */
2999 if (value == FILE_ABORT)
3000 break;
3002 if (value == FILE_CONT)
3003 do_file_mark (panel, i, 0);
3005 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3007 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3008 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3011 if (operation != OP_DELETE)
3012 file_progress_show (ctx, 0, 0, "", FALSE);
3014 if (check_progress_buttons (ctx) == FILE_ABORT)
3015 break;
3017 mc_refresh ();
3018 } /* Loop for every file */
3020 } /* Many entries */
3022 clean_up:
3023 /* Clean up */
3024 if (save_cwd != NULL)
3026 tmp_vpath = vfs_path_from_str (save_cwd);
3027 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3028 vfs_path_free (tmp_vpath);
3029 g_free (save_cwd);
3032 if (save_dest != NULL)
3034 tmp_vpath = vfs_path_from_str (save_dest);
3035 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3036 vfs_path_free (tmp_vpath);
3037 g_free (save_dest);
3040 free_linklist (&linklist);
3041 free_linklist (&dest_dirs);
3042 #ifdef WITH_FULL_PATHS
3043 g_free (source_with_path);
3044 #endif /* WITH_FULL_PATHS */
3045 g_free (dest);
3046 vfs_path_free (dest_vpath);
3047 g_free (ctx->dest_mask);
3048 ctx->dest_mask = NULL;
3050 #ifdef ENABLE_BACKGROUND
3051 /* Let our parent know we are saying bye bye */
3052 if (mc_global.we_are_background)
3054 int cur_pid = getpid ();
3055 /* Send pid to parent with child context, it is fork and
3056 don't modify real parent ctx */
3057 ctx->pid = cur_pid;
3058 parent_call ((void *) end_bg_process, ctx, 0);
3060 vfs_shut ();
3061 _exit (0);
3063 #endif /* ENABLE_BACKGROUND */
3065 file_op_total_context_destroy (tctx);
3066 ret_fast:
3067 file_op_context_destroy (ctx);
3069 return ret_val;
3072 /* }}} */
3074 /* --------------------------------------------------------------------------------------------- */
3075 /* {{{ Query/status report routines */
3076 /** Report error with one file */
3077 FileProgressStatus
3078 file_error (const char *format, const char *file)
3080 char buf[BUF_MEDIUM];
3082 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3084 return do_file_error (buf);
3087 /* --------------------------------------------------------------------------------------------- */
3090 Cause emacs to enter folding mode for this file:
3091 Local variables:
3092 end: