Changed interface of mc_stat() and mc_lstat() functions
[midnight-commander.git] / src / filemanager / file.c
blobfd868fdbf5a333e92a418c5d26edf3741bfb9064
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_path, 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_path))
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 (s) != 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 (s) != 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 (s);
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;
1091 dir = mc_opendir (path);
1092 if (!dir)
1093 return -1;
1095 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1097 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1098 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1099 continue; /* "." or ".." */
1100 i = 0;
1101 break;
1104 mc_closedir (dir);
1105 return i;
1108 /* --------------------------------------------------------------------------------------------- */
1110 static FileProgressStatus
1111 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1113 FileProgressStatus error;
1115 if (strcmp (s, "..") == 0)
1116 return FILE_SKIP;
1118 if (strcmp (s, ".") == 0)
1119 return FILE_SKIP;
1121 file_progress_show_deleting (ctx, s);
1122 if (check_progress_buttons (ctx) == FILE_ABORT)
1123 return FILE_ABORT;
1124 mc_refresh ();
1126 if (1 != check_dir_is_empty (s)) /* not empty or error */
1127 return FILE_CONT;
1129 while (my_rmdir (s) != 0 && !ctx->skip_all)
1131 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1132 if (error == FILE_SKIPALL)
1133 ctx->skip_all = TRUE;
1134 if (error != FILE_RETRY)
1135 return error;
1138 return FILE_CONT;
1141 /* }}} */
1143 /* --------------------------------------------------------------------------------------------- */
1144 /* {{{ Panel operate routines */
1147 * Return currently selected entry name or the name of the first marked
1148 * entry if there is one.
1151 static char *
1152 panel_get_file (WPanel * panel)
1154 if (get_current_type () == view_tree)
1156 WTree *tree;
1158 tree = (WTree *) get_panel_widget (get_current_index ());
1159 return tree_selected_name (tree);
1162 if (panel->marked != 0)
1164 int i;
1166 for (i = 0; i < panel->count; i++)
1167 if (panel->dir.list[i].f.marked)
1168 return panel->dir.list[i].fname;
1171 return panel->dir.list[panel->selected].fname;
1174 /* --------------------------------------------------------------------------------------------- */
1176 * panel_compute_totals:
1178 * compute the number of files and the number of bytes
1179 * used up by the whole selection, recursing directories
1180 * as required. In addition, it checks to see if it will
1181 * overwrite any files by doing the copy.
1184 static FileProgressStatus
1185 panel_compute_totals (const WPanel * panel, const void *ui,
1186 compute_dir_size_callback cback,
1187 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1189 int i;
1191 *ret_marked = 0;
1192 *ret_total = 0;
1194 for (i = 0; i < panel->count; i++)
1196 struct stat *s;
1198 if (!panel->dir.list[i].f.marked)
1199 continue;
1201 s = &panel->dir.list[i].st;
1203 if (S_ISDIR (s->st_mode))
1205 char *dir_name;
1206 size_t subdir_count = 0;
1207 uintmax_t subdir_bytes = 0;
1208 FileProgressStatus status;
1210 dir_name = concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1212 status = compute_dir_size (dir_name, ui, cback,
1213 &subdir_count, &subdir_bytes, compute_symlinks);
1214 g_free (dir_name);
1216 if (status != FILE_CONT)
1217 return FILE_ABORT;
1219 *ret_marked += subdir_count;
1220 *ret_total += subdir_bytes;
1222 else
1224 (*ret_marked)++;
1225 *ret_total += (uintmax_t) s->st_size;
1229 return FILE_CONT;
1232 /* --------------------------------------------------------------------------------------------- */
1234 /** Initialize variables for progress bars */
1235 static FileProgressStatus
1236 panel_operate_init_totals (FileOperation operation,
1237 const WPanel * panel, const char *source, FileOpContext * ctx)
1239 FileProgressStatus status;
1241 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1243 ComputeDirSizeUI *ui;
1245 ui = compute_dir_size_create_ui ();
1247 if (source != NULL)
1248 status = compute_dir_size (source, ui, compute_dir_size_update_ui,
1249 &ctx->progress_count, &ctx->progress_bytes,
1250 ctx->follow_links);
1251 else
1252 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1253 &ctx->progress_count, &ctx->progress_bytes,
1254 ctx->follow_links);
1256 compute_dir_size_destroy_ui (ui);
1258 ctx->progress_totals_computed = (status == FILE_CONT);
1260 else
1262 status = FILE_CONT;
1263 ctx->progress_count = panel->marked;
1264 ctx->progress_bytes = panel->total;
1265 ctx->progress_totals_computed = FALSE;
1268 return status;
1271 /* --------------------------------------------------------------------------------------------- */
1273 * Generate user prompt for panel operation.
1274 * single_source is the name if the source entry or NULL for multiple
1275 * entries.
1276 * src_stat is only used when single_source is not NULL.
1279 static char *
1280 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1281 gboolean single_source, const struct stat *src_stat)
1283 const char *sp, *cp;
1284 char format_string[BUF_MEDIUM];
1285 char *dp = format_string;
1286 gboolean build_question = FALSE;
1288 static gboolean i18n_flag = FALSE;
1289 if (!i18n_flag)
1291 size_t i;
1293 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1294 op_names1[i] = Q_ (op_names1[i]);
1296 #ifdef ENABLE_NLS
1297 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1298 prompt_parts[i] = _(prompt_parts[i]);
1300 one_format = _(one_format);
1301 many_format = _(many_format);
1302 question_format = _(question_format);
1303 #endif /* ENABLE_NLS */
1304 i18n_flag = TRUE;
1307 sp = single_source ? one_format : many_format;
1309 while (*sp != '\0')
1311 switch (*sp)
1313 case '%':
1314 cp = NULL;
1315 switch (sp[1])
1317 case 'o':
1318 cp = op_names1[operation];
1319 break;
1320 case 'm':
1321 if (operation == OP_DELETE)
1323 cp = "";
1324 build_question = TRUE;
1326 else
1327 cp = prompt_parts[5];
1328 break;
1329 case 'e':
1330 if (operation == OP_DELETE)
1332 cp = "";
1333 build_question = TRUE;
1335 else
1336 cp = prompt_parts[6];
1337 break;
1338 case 'f':
1339 if (single_source)
1340 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1341 else
1342 cp = (panel->marked == panel->dirs_marked)
1343 ? prompt_parts[3]
1344 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1345 break;
1346 default:
1347 *dp++ = *sp++;
1350 if (cp != NULL)
1352 sp += 2;
1353 while (*cp != '\0')
1354 *dp++ = *cp++;
1356 break;
1357 default:
1358 *dp++ = *sp++;
1361 *dp = '\0';
1363 if (build_question)
1365 char tmp[BUF_MEDIUM];
1367 memmove (tmp, format_string, sizeof (tmp));
1368 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1371 return g_strdup (format_string);
1374 /* --------------------------------------------------------------------------------------------- */
1376 #ifdef ENABLE_BACKGROUND
1377 static int
1378 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1380 int pid = ctx->pid;
1382 (void) mode;
1383 ctx->pid = 0;
1385 unregister_task_with_pid (pid);
1386 /* file_op_context_destroy(ctx); */
1387 return 1;
1389 #endif
1390 /* }}} */
1392 /* --------------------------------------------------------------------------------------------- */
1393 /*** public functions ****************************************************************************/
1394 /* --------------------------------------------------------------------------------------------- */
1396 FileProgressStatus
1397 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1398 const char *src_path, const char *dst_path)
1400 uid_t src_uid = (uid_t) - 1;
1401 gid_t src_gid = (gid_t) - 1;
1403 int src_desc, dest_desc = -1;
1404 int n_read, n_written;
1405 mode_t src_mode = 0; /* The mode of the source file */
1406 struct stat sb, sb2;
1407 struct utimbuf utb;
1408 gboolean dst_exists = FALSE, appending = FALSE;
1409 off_t file_size = -1;
1410 FileProgressStatus return_status, temp_status;
1411 struct timeval tv_transfer_start;
1412 dest_status_t dst_status = DEST_NONE;
1413 int open_flags;
1414 gboolean is_first_time = TRUE;
1415 vfs_path_t *src_vpath, *dst_vpath;
1417 /* FIXME: We should not be using global variables! */
1418 ctx->do_reget = 0;
1419 return_status = FILE_RETRY;
1421 file_progress_show_source (ctx, src_path);
1422 file_progress_show_target (ctx, dst_path);
1423 if (check_progress_buttons (ctx) == FILE_ABORT)
1424 return FILE_ABORT;
1426 mc_refresh ();
1428 dst_vpath = vfs_path_from_str (dst_path);
1429 while (mc_stat (dst_vpath, &sb2) == 0)
1431 if (S_ISDIR (sb2.st_mode))
1433 if (ctx->skip_all)
1434 return_status = FILE_SKIPALL;
1435 else
1437 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1438 if (return_status == FILE_SKIPALL)
1439 ctx->skip_all = TRUE;
1440 if (return_status == FILE_RETRY)
1441 continue;
1443 return return_status;
1445 dst_exists = TRUE;
1446 break;
1448 vfs_path_free (dst_vpath);
1450 src_vpath = vfs_path_from_str (src_path);
1451 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1453 if (ctx->skip_all)
1454 return_status = FILE_SKIPALL;
1455 else
1457 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1458 if (return_status == FILE_SKIPALL)
1459 ctx->skip_all = TRUE;
1461 if (return_status != FILE_RETRY)
1463 vfs_path_free (src_vpath);
1464 return return_status;
1467 vfs_path_free (src_vpath);
1469 if (dst_exists)
1471 /* Destination already exists */
1472 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1473 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), src_path, dst_path);
1474 /* Should we replace destination? */
1475 if (tctx->ask_overwrite)
1477 ctx->do_reget = 0;
1478 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1479 if (return_status != FILE_CONT)
1480 return return_status;
1484 if (!ctx->do_append)
1486 /* Check the hardlinks */
1487 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb))
1489 /* We have made a hardlink - no more processing is necessary */
1490 return FILE_CONT;
1493 if (S_ISLNK (sb.st_mode))
1494 return make_symlink (ctx, src_path, dst_path);
1496 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1497 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1499 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1500 && !ctx->skip_all)
1502 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1503 if (return_status == FILE_RETRY)
1504 continue;
1505 if (return_status == FILE_SKIPALL)
1506 ctx->skip_all = TRUE;
1507 return return_status;
1509 /* Success */
1511 while (ctx->preserve_uidgid && mc_chown (dst_path, sb.st_uid, sb.st_gid) != 0
1512 && !ctx->skip_all)
1514 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1515 if (temp_status == FILE_SKIP)
1516 break;
1517 if (temp_status == FILE_SKIPALL)
1518 ctx->skip_all = TRUE;
1519 if (temp_status != FILE_RETRY)
1520 return temp_status;
1523 while (ctx->preserve && mc_chmod (dst_path, sb.st_mode & ctx->umask_kill) != 0
1524 && !ctx->skip_all)
1526 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1527 if (temp_status == FILE_SKIP)
1528 break;
1529 if (temp_status == FILE_SKIPALL)
1530 ctx->skip_all = TRUE;
1531 if (temp_status != FILE_RETRY)
1532 return temp_status;
1535 return FILE_CONT;
1539 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1541 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1543 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1544 if (return_status == FILE_RETRY)
1545 continue;
1546 if (return_status == FILE_SKIPALL)
1547 ctx->skip_all = TRUE;
1548 if (return_status == FILE_SKIP)
1549 break;
1550 ctx->do_append = 0;
1551 return return_status;
1554 if (ctx->do_reget != 0)
1556 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1558 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1559 ctx->do_reget = 0;
1560 ctx->do_append = FALSE;
1564 while (mc_fstat (src_desc, &sb) != 0)
1566 if (ctx->skip_all)
1567 return_status = FILE_SKIPALL;
1568 else
1570 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1571 if (return_status == FILE_RETRY)
1572 continue;
1573 if (return_status == FILE_SKIPALL)
1574 ctx->skip_all = TRUE;
1575 ctx->do_append = FALSE;
1577 goto ret;
1579 src_mode = sb.st_mode;
1580 src_uid = sb.st_uid;
1581 src_gid = sb.st_gid;
1582 utb.actime = sb.st_atime;
1583 utb.modtime = sb.st_mtime;
1584 file_size = sb.st_size;
1586 open_flags = O_WRONLY;
1587 if (dst_exists)
1589 if (ctx->do_append != 0)
1590 open_flags |= O_APPEND;
1591 else
1592 open_flags |= O_CREAT | O_TRUNC;
1594 else
1596 open_flags |= O_CREAT | O_EXCL;
1599 while ((dest_desc = mc_open (dst_path, open_flags, src_mode)) < 0)
1601 if (errno != EEXIST)
1603 if (ctx->skip_all)
1604 return_status = FILE_SKIPALL;
1605 else
1607 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1608 if (return_status == FILE_RETRY)
1609 continue;
1610 if (return_status == FILE_SKIPALL)
1611 ctx->skip_all = TRUE;
1612 ctx->do_append = FALSE;
1615 goto ret;
1617 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1619 appending = ctx->do_append;
1620 ctx->do_append = FALSE;
1622 /* Find out the optimal buffer size. */
1623 while (mc_fstat (dest_desc, &sb) != 0)
1625 if (ctx->skip_all)
1626 return_status = FILE_SKIPALL;
1627 else
1629 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1630 if (return_status == FILE_RETRY)
1631 continue;
1632 if (return_status == FILE_SKIPALL)
1633 ctx->skip_all = TRUE;
1635 goto ret;
1638 while (TRUE)
1640 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1641 if (errno == 0)
1642 break;
1644 if (ctx->skip_all)
1645 return_status = FILE_SKIPALL;
1646 else
1648 return_status =
1649 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1650 if (return_status == FILE_RETRY)
1651 continue;
1652 if (return_status == FILE_SKIPALL)
1653 ctx->skip_all = TRUE;
1655 mc_close (dest_desc);
1656 dest_desc = -1;
1657 mc_unlink (dst_path);
1658 dst_status = DEST_NONE;
1659 goto ret;
1662 ctx->eta_secs = 0.0;
1663 ctx->bps = 0;
1665 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1666 file_progress_show (ctx, 0, file_size, "", TRUE);
1667 else
1668 file_progress_show (ctx, 1, 1, "", TRUE);
1669 return_status = check_progress_buttons (ctx);
1670 mc_refresh ();
1672 if (return_status != FILE_CONT)
1673 goto ret;
1676 off_t n_read_total = 0;
1677 struct timeval tv_current, tv_last_update, tv_last_input;
1678 int secs, update_secs;
1679 const char *stalled_msg = "";
1681 tv_last_update = tv_transfer_start;
1683 while (TRUE)
1685 char buf[BUF_8K];
1687 /* src_read */
1688 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1689 n_read = -1;
1690 else
1691 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1693 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1694 if (return_status == FILE_RETRY)
1695 continue;
1696 if (return_status == FILE_SKIPALL)
1697 ctx->skip_all = TRUE;
1698 goto ret;
1700 if (n_read == 0)
1701 break;
1703 gettimeofday (&tv_current, NULL);
1705 if (n_read > 0)
1707 char *t = buf;
1708 n_read_total += n_read;
1710 /* Windows NT ftp servers report that files have no
1711 * permissions: -------, so if we happen to have actually
1712 * read something, we should fix the permissions.
1714 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1715 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1716 gettimeofday (&tv_last_input, NULL);
1718 /* dst_write */
1719 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read && !ctx->skip_all)
1721 if (n_written > 0)
1723 n_read -= n_written;
1724 t += n_written;
1725 continue;
1727 return_status = file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1728 if (return_status == FILE_SKIP)
1729 break;
1730 if (return_status == FILE_SKIPALL)
1731 ctx->skip_all = TRUE;
1732 if (return_status != FILE_RETRY)
1733 goto ret;
1737 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1739 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1740 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1742 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1744 copy_file_file_display_progress (tctx, ctx,
1745 tv_current,
1746 tv_transfer_start, file_size, n_read_total);
1747 tv_last_update = tv_current;
1749 is_first_time = FALSE;
1751 if (update_secs > FILEOP_STALLING_INTERVAL)
1753 stalled_msg = _("(stalled)");
1757 gboolean force_update;
1759 force_update =
1760 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1762 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1764 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1765 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1768 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1769 force_update);
1771 mc_refresh ();
1773 return_status = check_progress_buttons (ctx);
1775 if (return_status != FILE_CONT)
1777 mc_refresh ();
1778 goto ret;
1783 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1785 ret:
1786 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1788 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1789 if (temp_status == FILE_RETRY)
1790 continue;
1791 if (temp_status == FILE_ABORT)
1792 return_status = temp_status;
1793 if (temp_status == FILE_SKIPALL)
1794 ctx->skip_all = TRUE;
1795 break;
1798 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1800 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1801 if (temp_status == FILE_RETRY)
1802 continue;
1803 if (temp_status == FILE_SKIPALL)
1804 ctx->skip_all = TRUE;
1805 return_status = temp_status;
1806 break;
1809 if (dst_status == DEST_SHORT)
1811 /* Remove short file */
1812 int result;
1813 result = query_dialog (Q_ ("DialogTitle|Copy"),
1814 _("Incomplete file was retrieved. Keep it?"),
1815 D_ERROR, 2, _("&Delete"), _("&Keep"));
1816 if (result == 0)
1817 mc_unlink (dst_path);
1819 else if (dst_status == DEST_FULL)
1821 /* Copy has succeeded */
1822 if (!appending && ctx->preserve_uidgid)
1824 while (mc_chown (dst_path, src_uid, src_gid) != 0 && !ctx->skip_all)
1826 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1827 if (temp_status == FILE_RETRY)
1828 continue;
1829 if (temp_status == FILE_SKIPALL)
1831 ctx->skip_all = TRUE;
1832 return_status = FILE_CONT;
1834 if (temp_status == FILE_SKIP)
1835 return_status = FILE_CONT;
1836 break;
1840 if (!appending)
1842 if (ctx->preserve)
1844 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1846 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1847 if (temp_status == FILE_RETRY)
1848 continue;
1849 if (temp_status == FILE_SKIPALL)
1851 ctx->skip_all = TRUE;
1852 return_status = FILE_CONT;
1854 if (temp_status == FILE_SKIP)
1855 return_status = FILE_CONT;
1856 break;
1859 else if (!dst_exists)
1861 src_mode = umask (-1);
1862 umask (src_mode);
1863 src_mode = 0100666 & ~src_mode;
1864 mc_chmod (dst_path, (src_mode & ctx->umask_kill));
1866 mc_utime (dst_path, &utb);
1870 if (return_status == FILE_CONT)
1871 return_status = progress_update_one (tctx, ctx, file_size);
1873 return return_status;
1876 /* --------------------------------------------------------------------------------------------- */
1878 * I think these copy_*_* functions should have a return type.
1879 * anyway, this function *must* have two directories as arguments.
1881 /* FIXME: This function needs to check the return values of the
1882 function calls */
1884 FileProgressStatus
1885 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1886 gboolean toplevel, gboolean move_over, gboolean do_delete, struct link * parent_dirs)
1888 struct dirent *next;
1889 struct stat buf, cbuf;
1890 DIR *reading;
1891 char *dest_dir = NULL;
1892 FileProgressStatus return_status = FILE_CONT;
1893 struct utimbuf utb;
1894 struct link *lp;
1895 char *d;
1896 vfs_path_t *src_vpath, *dst_vpath;
1898 d = g_strdup (_d);
1900 src_vpath = vfs_path_from_str (s);
1901 dst_vpath = vfs_path_from_str (_d);
1903 /* First get the mode of the source dir */
1905 retry_src_stat:
1906 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
1908 if (ctx->skip_all)
1909 return_status = FILE_SKIPALL;
1910 else
1912 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1913 if (return_status == FILE_RETRY)
1914 goto retry_src_stat;
1915 if (return_status == FILE_SKIPALL)
1916 ctx->skip_all = TRUE;
1918 goto ret_fast;
1921 if (is_in_linklist (dest_dirs, s, &cbuf))
1923 /* Don't copy a directory we created before (we don't want to copy
1924 infinitely if a directory is copied into itself) */
1925 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1926 return_status = FILE_CONT;
1927 goto ret_fast;
1930 /* Hmm, hardlink to directory??? - Norbert */
1931 /* FIXME: In this step we should do something
1932 in case the destination already exist */
1933 /* Check the hardlinks */
1934 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf))
1936 /* We have made a hardlink - no more processing is necessary */
1937 goto ret_fast;
1940 if (!S_ISDIR (cbuf.st_mode))
1942 if (ctx->skip_all)
1943 return_status = FILE_SKIPALL;
1944 else
1946 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1947 if (return_status == FILE_RETRY)
1948 goto retry_src_stat;
1949 if (return_status == FILE_SKIPALL)
1950 ctx->skip_all = TRUE;
1952 goto ret_fast;
1955 if (is_in_linklist (parent_dirs, s, &cbuf))
1957 /* we found a cyclic symbolic link */
1958 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
1959 return_status = FILE_SKIP;
1960 goto ret_fast;
1963 lp = g_new (struct link, 1);
1964 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
1965 lp->ino = cbuf.st_ino;
1966 lp->dev = cbuf.st_dev;
1967 lp->next = parent_dirs;
1968 parent_dirs = lp;
1970 retry_dst_stat:
1971 /* Now, check if the dest dir exists, if not, create it. */
1972 if (mc_stat (dst_vpath, &buf) != 0)
1974 /* Here the dir doesn't exist : make it ! */
1975 if (move_over)
1977 if (mc_rename (s, d) == 0)
1979 return_status = FILE_CONT;
1980 goto ret;
1983 dest_dir = d;
1984 d = NULL;
1986 else
1989 * If the destination directory exists, we want to copy the whole
1990 * directory, but we only want this to happen once.
1992 * Escape sequences added to the * to compiler warnings.
1993 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
1994 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
1996 if (!S_ISDIR (buf.st_mode))
1998 if (ctx->skip_all)
1999 return_status = FILE_SKIPALL;
2000 else
2002 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2003 if (return_status == FILE_SKIPALL)
2004 ctx->skip_all = TRUE;
2005 if (return_status == FILE_RETRY)
2006 goto retry_dst_stat;
2008 goto ret;
2010 /* Dive into subdir if exists */
2011 if (toplevel && ctx->dive_into_subdirs)
2013 dest_dir = concat_dir_and_file (d, x_basename (s));
2015 else
2017 dest_dir = d;
2018 d = NULL;
2019 goto dont_mkdir;
2022 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU))
2024 if (ctx->skip_all)
2025 return_status = FILE_SKIPALL;
2026 else
2028 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
2029 if (return_status == FILE_SKIPALL)
2030 ctx->skip_all = TRUE;
2032 if (return_status != FILE_RETRY)
2033 goto ret;
2036 lp = g_new (struct link, 1);
2038 vfs_path_t *tmp_vpath = vfs_path_from_str (dest_dir);
2039 mc_stat (tmp_vpath, &buf);
2041 lp->vfs = vfs_path_get_by_index (tmp_vpath, -1)->class;
2042 vfs_path_free (tmp_vpath);
2044 lp->ino = buf.st_ino;
2045 lp->dev = buf.st_dev;
2046 lp->next = dest_dirs;
2047 dest_dirs = lp;
2049 if (ctx->preserve_uidgid)
2051 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid) != 0)
2053 if (ctx->skip_all)
2054 return_status = FILE_SKIPALL;
2055 else
2057 return_status =
2058 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
2059 if (return_status == FILE_SKIPALL)
2060 ctx->skip_all = TRUE;
2062 if (return_status != FILE_RETRY)
2063 goto ret;
2067 dont_mkdir:
2068 /* open the source dir for reading */
2069 reading = mc_opendir (s);
2070 if (reading == NULL)
2071 goto ret;
2073 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2075 char *path;
2076 vfs_path_t *tmp_vpath;
2078 * Now, we don't want '.' and '..' to be created / copied at any time
2080 if (!strcmp (next->d_name, "."))
2081 continue;
2082 if (!strcmp (next->d_name, ".."))
2083 continue;
2085 /* get the filename and add it to the src directory */
2086 path = concat_dir_and_file (s, next->d_name);
2087 tmp_vpath = vfs_path_from_str (path);
2089 (*ctx->stat_func) (tmp_vpath, &buf);
2090 if (S_ISDIR (buf.st_mode))
2092 char *mdpath;
2094 mdpath = concat_dir_and_file (dest_dir, next->d_name);
2096 * From here, we just intend to recursively copy subdirs, not
2097 * the double functionality of copying different when the target
2098 * dir already exists. So, we give the recursive call the flag 0
2099 * meaning no toplevel.
2101 return_status =
2102 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2103 g_free (mdpath);
2105 else
2107 char *dest_file;
2109 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
2110 return_status = copy_file_file (tctx, ctx, path, dest_file);
2111 g_free (dest_file);
2113 if (do_delete && return_status == FILE_CONT)
2115 if (ctx->erase_at_end)
2117 static struct link *tail;
2118 size_t len = strlen (path);
2119 lp = g_malloc (sizeof (struct link) + len);
2120 strncpy (lp->name, path, len + 1);
2121 lp->st_mode = buf.st_mode;
2122 lp->next = NULL;
2123 if (erase_list != NULL)
2125 tail->next = lp;
2126 tail = lp;
2128 else
2129 erase_list = tail = lp;
2131 else
2133 if (S_ISDIR (buf.st_mode))
2135 return_status = erase_dir_iff_empty (ctx, path);
2137 else
2138 return_status = erase_file (tctx, ctx, path);
2141 g_free (path);
2142 vfs_path_free (tmp_vpath);
2144 mc_closedir (reading);
2146 if (ctx->preserve)
2148 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
2149 utb.actime = cbuf.st_atime;
2150 utb.modtime = cbuf.st_mtime;
2151 mc_utime (dest_dir, &utb);
2153 else
2155 cbuf.st_mode = umask (-1);
2156 umask (cbuf.st_mode);
2157 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2158 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
2161 ret:
2162 g_free (dest_dir);
2163 g_free (parent_dirs);
2164 ret_fast:
2165 g_free (d);
2166 vfs_path_free (src_vpath);
2167 vfs_path_free (dst_vpath);
2168 return return_status;
2171 /* }}} */
2173 /* --------------------------------------------------------------------------------------------- */
2174 /* {{{ Move routines */
2176 FileProgressStatus
2177 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2179 struct stat sbuf, dbuf, destbuf;
2180 struct link *lp;
2181 char *destdir;
2182 FileProgressStatus return_status;
2183 gboolean move_over = FALSE;
2184 gboolean dstat_ok;
2185 vfs_path_t *src_vpath, *dst_vpath, *destdir_vpath;
2187 src_vpath = vfs_path_from_str (s);
2188 dst_vpath = vfs_path_from_str (d);
2190 file_progress_show_source (ctx, s);
2191 file_progress_show_target (ctx, d);
2192 if (check_progress_buttons (ctx) == FILE_ABORT)
2193 return FILE_ABORT;
2195 mc_refresh ();
2197 mc_stat (src_vpath, &sbuf);
2199 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2201 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2202 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2204 if (!dstat_ok)
2205 destdir = g_strdup (d); /* destination doesn't exist */
2206 else if (!ctx->dive_into_subdirs)
2208 destdir = g_strdup (d);
2209 move_over = TRUE;
2211 else
2212 destdir = concat_dir_and_file (d, x_basename (s));
2214 destdir_vpath = vfs_path_from_str (destdir);
2216 /* Check if the user inputted an existing dir */
2217 retry_dst_stat:
2218 if (mc_stat (destdir_vpath, &destbuf) == 0)
2220 if (move_over)
2222 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2224 if (return_status != FILE_CONT)
2225 goto ret;
2226 goto oktoret;
2228 else if (ctx->skip_all)
2229 return_status = FILE_SKIPALL;
2230 else
2232 if (S_ISDIR (destbuf.st_mode))
2233 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2234 else
2235 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2236 if (return_status == FILE_SKIPALL)
2237 ctx->skip_all = TRUE;
2238 if (return_status == FILE_RETRY)
2239 goto retry_dst_stat;
2241 g_free (destdir);
2242 vfs_path_free (destdir_vpath);
2243 vfs_path_free (src_vpath);
2244 vfs_path_free (dst_vpath);
2245 return return_status;
2248 retry_rename:
2249 if (mc_rename (s, destdir) == 0)
2251 return_status = FILE_CONT;
2252 goto ret;
2255 if (errno != EXDEV)
2257 if (!ctx->skip_all)
2259 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2260 if (return_status == FILE_SKIPALL)
2261 ctx->skip_all = TRUE;
2262 if (return_status == FILE_RETRY)
2263 goto retry_rename;
2265 goto ret;
2267 /* Failed because of filesystem boundary -> copy dir instead */
2268 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2270 if (return_status != FILE_CONT)
2271 goto ret;
2272 oktoret:
2273 file_progress_show_source (ctx, NULL);
2274 file_progress_show (ctx, 0, 0, "", FALSE);
2276 return_status = check_progress_buttons (ctx);
2277 if (return_status != FILE_CONT)
2278 goto ret;
2280 mc_refresh ();
2281 if (ctx->erase_at_end)
2283 for (; erase_list && return_status != FILE_ABORT;)
2285 if (S_ISDIR (erase_list->st_mode))
2287 return_status = erase_dir_iff_empty (ctx, erase_list->name);
2289 else
2290 return_status = erase_file (tctx, ctx, erase_list->name);
2291 lp = erase_list;
2292 erase_list = erase_list->next;
2293 g_free (lp);
2296 erase_dir_iff_empty (ctx, s);
2298 ret:
2299 g_free (destdir);
2300 vfs_path_free (destdir_vpath);
2301 while (erase_list)
2303 lp = erase_list;
2304 erase_list = erase_list->next;
2305 g_free (lp);
2307 vfs_path_free (src_vpath);
2308 vfs_path_free (dst_vpath);
2309 return return_status;
2312 /* }}} */
2314 /* --------------------------------------------------------------------------------------------- */
2315 /* {{{ Erase routines */
2317 FileProgressStatus
2318 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
2320 FileProgressStatus error;
2322 if (strcmp (s, "..") == 0)
2323 return FILE_SKIP;
2325 if (strcmp (s, ".") == 0)
2326 return FILE_SKIP;
2328 file_progress_show_deleting (ctx, s);
2329 if (check_progress_buttons (ctx) == FILE_ABORT)
2330 return FILE_ABORT;
2331 mc_refresh ();
2333 /* The old way to detect a non empty directory was:
2334 error = my_rmdir (s);
2335 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2336 For the linux user space nfs server (nfs-server-2.2beta29-2)
2337 we would have to check also for EIO. I hope the new way is
2338 fool proof. (Norbert)
2340 error = check_dir_is_empty (s);
2341 if (error == 0)
2342 { /* not empty */
2343 error = query_recursive (ctx, s);
2344 if (error == FILE_CONT)
2345 return recursive_erase (tctx, ctx, s);
2346 else
2347 return error;
2350 while (my_rmdir (s) == -1 && !ctx->skip_all)
2352 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2353 if (error != FILE_RETRY)
2354 return error;
2357 return FILE_CONT;
2360 /* }}} */
2362 /* --------------------------------------------------------------------------------------------- */
2363 /* {{{ Panel operate routines */
2365 ComputeDirSizeUI *
2366 compute_dir_size_create_ui (void)
2368 ComputeDirSizeUI *ui;
2370 const char *b_name = N_("&Abort");
2372 #ifdef ENABLE_NLS
2373 b_name = _(b_name);
2374 #endif
2376 ui = g_new (ComputeDirSizeUI, 1);
2378 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
2379 NULL, _("Directory scanning"), DLG_CENTER);
2380 ui->dirname = label_new (3, 3, "");
2381 add_widget (ui->dlg, ui->dirname);
2383 add_widget (ui->dlg,
2384 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
2385 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2387 /* We will manage the dialog without any help,
2388 that's why we have to call init_dlg */
2389 init_dlg (ui->dlg);
2391 return ui;
2394 /* --------------------------------------------------------------------------------------------- */
2396 void
2397 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2399 if (ui != NULL)
2401 /* schedule to update passive panel */
2402 other_panel->dirty = 1;
2404 /* close and destroy dialog */
2405 dlg_run_done (ui->dlg);
2406 destroy_dlg (ui->dlg);
2407 g_free (ui);
2411 /* --------------------------------------------------------------------------------------------- */
2413 FileProgressStatus
2414 compute_dir_size_update_ui (const void *ui, const char *dirname)
2416 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2417 int c;
2418 Gpm_Event event;
2420 if (ui == NULL)
2421 return FILE_CONT;
2423 label_set_text (this->dirname, str_trunc (dirname, this->dlg->cols - 6));
2425 event.x = -1; /* Don't show the GPM cursor */
2426 c = tty_get_event (&event, FALSE, FALSE);
2427 if (c == EV_NONE)
2428 return FILE_CONT;
2430 /* Reinitialize to avoid old values after events other than
2431 selecting a button */
2432 this->dlg->ret_value = FILE_CONT;
2434 dlg_process_event (this->dlg, c, &event);
2436 switch (this->dlg->ret_value)
2438 case B_CANCEL:
2439 case FILE_ABORT:
2440 return FILE_ABORT;
2441 default:
2442 return FILE_CONT;
2446 /* --------------------------------------------------------------------------------------------- */
2448 * compute_dir_size:
2450 * Computes the number of bytes used by the files in a directory
2453 FileProgressStatus
2454 compute_dir_size (const char *dirname, const void *ui,
2455 compute_dir_size_callback cback,
2456 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2458 int res;
2459 struct stat s;
2460 DIR *dir;
2461 struct dirent *dirent;
2462 FileProgressStatus ret = FILE_CONT;
2463 vfs_path_t *vpath;
2465 vpath = vfs_path_from_str (dirname);
2467 if (!compute_symlinks)
2469 res = mc_lstat (vpath, &s);
2470 if (res != 0)
2471 goto ret;
2473 /* don't scan symlink to directory */
2474 if (S_ISLNK (s.st_mode))
2476 (*ret_marked)++;
2477 *ret_total += (uintmax_t) s.st_size;
2478 goto ret;
2482 dir = mc_opendir (dirname);
2484 if (dir == NULL)
2485 goto ret;
2487 while ((dirent = mc_readdir (dir)) != NULL)
2489 char *fullname;
2490 vfs_path_t *tmp_vpath;
2492 ret = (cback != NULL) ? cback (ui, dirname) : FILE_CONT;
2494 if (ret != FILE_CONT)
2495 break;
2497 if (strcmp (dirent->d_name, ".") == 0)
2498 continue;
2499 if (strcmp (dirent->d_name, "..") == 0)
2500 continue;
2502 fullname = concat_dir_and_file (dirname, dirent->d_name);
2503 tmp_vpath = vfs_path_from_str (fullname);
2504 res = mc_lstat (tmp_vpath, &s);
2506 if (res != 0)
2508 g_free (fullname);
2509 vfs_path_free (tmp_vpath);
2510 continue;
2513 if (S_ISDIR (s.st_mode))
2515 size_t subdir_count = 0;
2516 uintmax_t subdir_bytes = 0;
2518 ret =
2519 compute_dir_size (fullname, ui, cback, &subdir_count, &subdir_bytes,
2520 compute_symlinks);
2522 if (ret != FILE_CONT)
2524 g_free (fullname);
2525 vfs_path_free (tmp_vpath);
2526 break;
2529 *ret_marked += subdir_count;
2530 *ret_total += subdir_bytes;
2532 else
2534 (*ret_marked)++;
2535 *ret_total += (uintmax_t) s.st_size;
2538 g_free (fullname);
2539 vfs_path_free (tmp_vpath);
2542 mc_closedir (dir);
2543 ret:
2544 vfs_path_free (vpath);
2545 return ret;
2548 /* --------------------------------------------------------------------------------------------- */
2550 * panel_operate:
2552 * Performs one of the operations on the selection on the source_panel
2553 * (copy, delete, move).
2555 * Returns TRUE if did change the directory
2556 * structure, Returns FALSE if user aborted
2558 * force_single forces operation on the current entry and affects
2559 * default destination. Current filename is used as default.
2562 gboolean
2563 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2565 WPanel *panel = (WPanel *) source_panel;
2566 const gboolean single_entry = force_single || (panel->marked <= 1)
2567 || (get_current_type () == view_tree);
2569 char *source = NULL;
2570 #ifdef WITH_FULL_PATHS
2571 char *source_with_path = NULL;
2572 #else
2573 #define source_with_path source
2574 #endif /* !WITH_FULL_PATHS */
2575 char *dest = NULL;
2576 char *temp = NULL;
2577 char *save_cwd = NULL, *save_dest = NULL;
2578 struct stat src_stat;
2579 gboolean ret_val = TRUE;
2580 int i;
2581 FileProgressStatus value;
2582 FileOpContext *ctx;
2583 FileOpTotalContext *tctx;
2585 gboolean do_bg = FALSE; /* do background operation? */
2587 static gboolean i18n_flag = FALSE;
2588 if (!i18n_flag)
2590 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2591 op_names[i] = Q_ (op_names[i]);
2592 i18n_flag = TRUE;
2595 free_linklist (&linklist);
2596 free_linklist (&dest_dirs);
2598 if (single_entry)
2600 vfs_path_t *source_vpath;
2602 if (force_single)
2603 source = selection (panel)->fname;
2604 else
2605 source = panel_get_file (panel);
2607 if (strcmp (source, "..") == 0)
2609 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2610 return FALSE;
2613 source_vpath = vfs_path_from_str (source);
2614 /* Update stat to get actual info */
2615 if (mc_lstat (source_vpath, &src_stat) != 0)
2617 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2618 path_trunc (source, 30), unix_error_string (errno));
2620 /* Directory was changed outside MC. Reload it forced */
2621 if (!panel->is_panelized)
2623 panel_update_flags_t flags = UP_RELOAD;
2625 /* don't update panelized panel */
2626 if (get_other_type () == view_listing && other_panel->is_panelized)
2627 flags |= UP_ONLY_CURRENT;
2629 update_panels (flags, UP_KEEPSEL);
2631 vfs_path_free (source_vpath);
2632 return FALSE;
2634 vfs_path_free (source_vpath);
2637 ctx = file_op_context_new (operation);
2639 /* Show confirmation dialog */
2640 if (operation != OP_DELETE)
2642 char *dest_dir;
2643 char *dest_dir_;
2644 char *format;
2646 /* Forced single operations default to the original name */
2647 if (force_single)
2648 dest_dir = source;
2649 else if (get_other_type () == view_listing)
2650 dest_dir = other_panel->cwd;
2651 else
2652 dest_dir = panel->cwd;
2654 * Add trailing backslash only when do non-local ops.
2655 * It saves user from occasional file renames (when destination
2656 * dir is deleted)
2658 if (!force_single && dest_dir[0] != '\0' && dest_dir[strlen (dest_dir) - 1] != PATH_SEP)
2660 /* add trailing separator */
2661 dest_dir_ = g_strconcat (dest_dir, PATH_SEP_STR, (char *) NULL);
2663 else
2665 /* just copy */
2666 dest_dir_ = g_strdup (dest_dir);
2669 if (dest_dir_ == NULL)
2671 ret_val = FALSE;
2672 goto ret_fast;
2675 /* Generate confirmation prompt */
2676 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2678 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2679 source != NULL ? (void *) source
2680 : (void *) &panel->marked, dest_dir_, &do_bg);
2682 g_free (format);
2683 g_free (dest_dir_);
2685 if (dest == NULL || dest[0] == '\0')
2687 g_free (dest);
2688 ret_val = FALSE;
2689 goto ret_fast;
2692 else if (confirm_delete)
2694 char *format;
2695 char fmd_buf[BUF_MEDIUM];
2697 /* Generate confirmation prompt */
2698 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2700 if (source == NULL)
2701 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2702 else
2704 const int fmd_xlen = 64;
2705 i = fmd_xlen - str_term_width1 (format) - 4;
2706 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2709 g_free (format);
2711 if (safe_delete)
2712 query_set_sel (1);
2714 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2716 if (i != 0)
2718 ret_val = FALSE;
2719 goto ret_fast;
2723 tctx = file_op_total_context_new ();
2724 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2727 filegui_dialog_type_t dialog_type;
2729 if (operation == OP_DELETE)
2730 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2731 else
2733 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2734 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2736 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2737 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2740 /* Background also need ctx->ui, but not full */
2741 if (do_bg)
2742 file_op_context_create_ui_without_init (ctx, TRUE, dialog_type);
2743 else
2744 file_op_context_create_ui (ctx, TRUE, dialog_type);
2747 #ifdef ENABLE_BACKGROUND
2748 /* Did the user select to do a background operation? */
2749 if (do_bg)
2751 int v;
2753 v = do_background (ctx, g_strconcat (op_names[operation], ": ", panel->cwd, (char *) NULL));
2754 if (v == -1)
2755 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2757 /* If we are the parent */
2758 if (v == 1)
2760 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
2761 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
2762 /* file_op_context_destroy (ctx); */
2763 return FALSE;
2766 #endif /* ENABLE_BACKGROUND */
2768 /* Initialize things */
2769 /* We do not want to trash cache every time file is
2770 created/touched. However, this will make our cache contain
2771 invalid data. */
2772 if ((dest != NULL) && (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1)))
2773 save_dest = g_strdup (dest);
2775 if ((panel->cwd[0] != '\0') && (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1)))
2776 save_cwd = g_strdup (panel->cwd);
2778 /* Now, let's do the job */
2780 /* This code is only called by the tree and panel code */
2781 if (single_entry)
2783 /* We now have ETA in all cases */
2785 /* One file: FIXME mc_chdir will take user out of any vfs */
2786 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2788 vfs_path_t *vpath;
2789 int chdir_retcode;
2791 vpath = vfs_path_from_str (PATH_SEP_STR);
2792 chdir_retcode = mc_chdir (vpath);
2793 vfs_path_free (vpath);
2794 if (chdir_retcode < 0)
2796 ret_val = FALSE;
2797 goto clean_up;
2801 /* The source and src_stat variables have been initialized before */
2802 #ifdef WITH_FULL_PATHS
2803 if (g_path_is_absolute (source))
2804 source_with_path = g_strdup (source);
2805 else
2806 source_with_path = mc_build_filename (panel->cwd, source, (char *) NULL);
2807 #endif /* WITH_FULL_PATHS */
2809 if (panel_operate_init_totals (operation, panel, source_with_path, ctx) == FILE_CONT)
2811 if (operation == OP_DELETE)
2813 if (S_ISDIR (src_stat.st_mode))
2814 value = erase_dir (tctx, ctx, source_with_path);
2815 else
2816 value = erase_file (tctx, ctx, source_with_path);
2818 else
2820 temp = transform_source (ctx, source_with_path);
2821 if (temp == NULL)
2822 value = transform_error;
2823 else
2825 char *repl_dest, *temp2;
2827 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2828 temp2 = concat_dir_and_file (repl_dest, temp);
2829 g_free (temp);
2830 g_free (repl_dest);
2831 g_free (dest);
2832 dest = temp2;
2834 switch (operation)
2836 case OP_COPY:
2837 /* we use file_mask_op_follow_links only with OP_COPY */
2839 vfs_path_t *vpath = vfs_path_from_str (source_with_path);
2840 ctx->stat_func (vpath, &src_stat);
2841 vfs_path_free (vpath);
2844 if (S_ISDIR (src_stat.st_mode))
2845 value = copy_dir_dir (tctx, ctx, source_with_path, dest,
2846 TRUE, FALSE, FALSE, NULL);
2847 else
2848 value = copy_file_file (tctx, ctx, source_with_path, dest);
2849 break;
2851 case OP_MOVE:
2852 if (S_ISDIR (src_stat.st_mode))
2853 value = move_dir_dir (tctx, ctx, source_with_path, dest);
2854 else
2855 value = move_file_file (tctx, ctx, source_with_path, dest);
2856 break;
2858 default:
2859 /* Unknown file operation */
2860 abort ();
2863 } /* Copy or move operation */
2865 if ((value == FILE_CONT) && !force_single)
2866 unmark_files (panel);
2869 else
2871 /* Many files */
2873 /* Check destination for copy or move operation */
2874 while (operation != OP_DELETE)
2876 int dst_result;
2877 struct stat dst_stat;
2878 vfs_path_t *vpath = vfs_path_from_str (dest);
2880 dst_result = mc_stat (vpath, &dst_stat);
2881 vfs_path_free (vpath);
2883 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2884 break;
2886 if (ctx->skip_all
2887 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2888 goto clean_up;
2891 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2893 /* Loop for every file, perform the actual copy operation */
2894 for (i = 0; i < panel->count; i++)
2896 if (!panel->dir.list[i].f.marked)
2897 continue; /* Skip the unmarked ones */
2899 source = panel->dir.list[i].fname;
2900 src_stat = panel->dir.list[i].st;
2902 #ifdef WITH_FULL_PATHS
2903 g_free (source_with_path);
2904 if (g_path_is_absolute (source))
2905 source_with_path = g_strdup (source);
2906 else
2907 source_with_path = mc_build_filename (panel->cwd, source, (char *) NULL);
2908 #endif /* WITH_FULL_PATHS */
2910 if (operation == OP_DELETE)
2912 if (S_ISDIR (src_stat.st_mode))
2913 value = erase_dir (tctx, ctx, source_with_path);
2914 else
2915 value = erase_file (tctx, ctx, source_with_path);
2917 else
2919 temp = transform_source (ctx, source_with_path);
2921 if (temp == NULL)
2922 value = transform_error;
2923 else
2925 char *temp2, *temp3, *repl_dest;
2927 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2928 temp2 = concat_dir_and_file (repl_dest, temp);
2929 g_free (temp);
2930 g_free (repl_dest);
2931 temp3 = source_with_path;
2932 source_with_path = strutils_shell_unescape (source_with_path);
2933 g_free (temp3);
2934 temp3 = temp2;
2935 temp2 = strutils_shell_unescape (temp2);
2936 g_free (temp3);
2938 switch (operation)
2940 case OP_COPY:
2941 /* we use file_mask_op_follow_links only with OP_COPY */
2943 vfs_path_t *vpath;
2945 vpath = vfs_path_from_str (source_with_path);
2946 ctx->stat_func (vpath, &src_stat);
2947 vfs_path_free (vpath);
2949 if (S_ISDIR (src_stat.st_mode))
2950 value = copy_dir_dir (tctx, ctx, source_with_path, temp2,
2951 TRUE, FALSE, FALSE, NULL);
2952 else
2953 value = copy_file_file (tctx, ctx, source_with_path, temp2);
2954 free_linklist (&dest_dirs);
2955 break;
2957 case OP_MOVE:
2958 if (S_ISDIR (src_stat.st_mode))
2959 value = move_dir_dir (tctx, ctx, source_with_path, temp2);
2960 else
2961 value = move_file_file (tctx, ctx, source_with_path, temp2);
2962 break;
2964 default:
2965 /* Unknown file operation */
2966 abort ();
2969 g_free (temp2);
2971 } /* Copy or move operation */
2973 if (value == FILE_ABORT)
2974 break;
2976 if (value == FILE_CONT)
2977 do_file_mark (panel, i, 0);
2979 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2981 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2982 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
2985 if (operation != OP_DELETE)
2986 file_progress_show (ctx, 0, 0, "", FALSE);
2988 if (check_progress_buttons (ctx) == FILE_ABORT)
2989 break;
2991 mc_refresh ();
2992 } /* Loop for every file */
2994 } /* Many entries */
2996 clean_up:
2997 /* Clean up */
2998 if (save_cwd != NULL)
3000 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
3001 g_free (save_cwd);
3004 if (save_dest != NULL)
3006 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
3007 g_free (save_dest);
3010 free_linklist (&linklist);
3011 free_linklist (&dest_dirs);
3012 #ifdef WITH_FULL_PATHS
3013 g_free (source_with_path);
3014 #endif /* WITH_FULL_PATHS */
3015 g_free (dest);
3016 g_free (ctx->dest_mask);
3017 ctx->dest_mask = NULL;
3019 #ifdef ENABLE_BACKGROUND
3020 /* Let our parent know we are saying bye bye */
3021 if (mc_global.we_are_background)
3023 int cur_pid = getpid ();
3024 /* Send pid to parent with child context, it is fork and
3025 don't modify real parent ctx */
3026 ctx->pid = cur_pid;
3027 parent_call ((void *) end_bg_process, ctx, 0);
3029 vfs_shut ();
3030 _exit (0);
3032 #endif /* ENABLE_BACKGROUND */
3034 file_op_total_context_destroy (tctx);
3035 ret_fast:
3036 file_op_context_destroy (ctx);
3038 return ret_val;
3041 /* }}} */
3043 /* --------------------------------------------------------------------------------------------- */
3044 /* {{{ Query/status report routines */
3045 /** Report error with one file */
3046 FileProgressStatus
3047 file_error (const char *format, const char *file)
3049 char buf[BUF_MEDIUM];
3051 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3053 return do_file_error (buf);
3056 /* --------------------------------------------------------------------------------------------- */
3059 Cause emacs to enter folding mode for this file:
3060 Local variables:
3061 end: