Changed src/filemanager/file.c to handle vfs_path_t objects.
[midnight-commander.git] / src / filemanager / file.c
blob7eb4f07d11e109da5ec78a169ed1ddf718b7e1b5
1 /*
2 File management.
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Janne Kukonlehto, 1994, 1995
10 Fred Leeflang, 1994, 1995
11 Miguel de Icaza, 1994, 1995, 1996
12 Jakub Jelinek, 1995, 1996
13 Norbert Warmuth, 1997
14 Pavel Machek, 1998
16 The copy code was based in GNU's cp, and was written by:
17 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
19 The move code was based in GNU's mv, and was written by:
20 Mike Parker and David MacKenzie.
22 Janne Kukonlehto added much error recovery to them for being used
23 in an interactive program.
25 This file is part of the Midnight Commander.
27 The Midnight Commander is free software: you can redistribute it
28 and/or modify it under the terms of the GNU General Public License as
29 published by the Free Software Foundation, either version 3 of the License,
30 or (at your option) any later version.
32 The Midnight Commander is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program. If not, see <http://www.gnu.org/licenses/>.
42 * Please note that all dialogs used here must be safe for background
43 * operations.
46 /** \file file.c
47 * \brief Source: file management
50 /* {{{ Include files */
52 #include <config.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
62 #include <fcntl.h>
64 #include "lib/global.h"
65 #include "lib/tty/tty.h"
66 #include "lib/tty/key.h"
67 #include "lib/search.h"
68 #include "lib/strescape.h"
69 #include "lib/strutil.h"
70 #include "lib/util.h"
71 #include "lib/vfs/vfs.h"
72 #include "lib/widget.h"
74 #include "src/setup.h"
75 #ifdef ENABLE_BACKGROUND
76 #include "src/background.h" /* do_background() */
77 #endif
79 #include "layout.h" /* rotate_dash() */
81 /* Needed for current_panel, other_panel and WTree */
82 #include "dir.h"
83 #include "filegui.h"
84 #include "filenot.h"
85 #include "tree.h"
86 #include "midnight.h" /* current_panel */
88 #include "file.h"
90 /* }}} */
92 /*** global variables ****************************************************************************/
94 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
95 const char *op_names[3] = {
96 N_("DialogTitle|Copy"),
97 N_("DialogTitle|Move"),
98 N_("DialogTitle|Delete")
101 /*** file scope macro definitions ****************************************************************/
103 /* Hack: the vfs code should not rely on this */
104 #define WITH_FULL_PATHS 1
106 #define FILEOP_UPDATE_INTERVAL 2
107 #define FILEOP_STALLING_INTERVAL 4
109 /*** file scope type declarations ****************************************************************/
111 /* This is a hard link cache */
112 struct link
114 struct link *next;
115 struct vfs_class *vfs;
116 dev_t dev;
117 ino_t ino;
118 short linkcount;
119 mode_t st_mode;
120 vfs_path_t *src_vpath;
121 vfs_path_t *dst_vpath;
124 /* Status of the destination file */
125 typedef enum
127 DEST_NONE = 0, /* Not created */
128 DEST_SHORT = 1, /* Created, not fully copied */
129 DEST_FULL = 2 /* Created, fully copied */
130 } dest_status_t;
133 * This array introduced to avoid translation problems. The former (op_names)
134 * is assumed to be nouns, suitable in dialog box titles; this one should
135 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
136 * (I don't use spaces around the words, because someday they could be
137 * dropped, when widgets get smarter)
140 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
141 static const char *op_names1[] = {
142 N_("FileOperation|Copy"),
143 N_("FileOperation|Move"),
144 N_("FileOperation|Delete")
148 * These are formats for building a prompt. Parts encoded as follows:
149 * %o - operation from op_names1
150 * %f - file/files or files/directories, as appropriate
151 * %m - "with source mask" or question mark for delete
152 * %s - source name (truncated)
153 * %d - number of marked files
154 * %e - "to:" or question mark for delete
156 * xgettext:no-c-format */
157 static const char *one_format = N_("%o %f \"%s\"%m");
158 /* xgettext:no-c-format */
159 static const char *many_format = N_("%o %d %f%m");
161 static const char *prompt_parts[] = {
162 N_("file"),
163 N_("files"),
164 N_("directory"),
165 N_("directories"),
166 N_("files/directories"),
167 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
168 N_(" with source mask:"),
169 N_("to:")
172 static const char *question_format = N_("%s?");
174 /*** file scope variables ************************************************************************/
176 /* the hard link cache */
177 static struct link *linklist = NULL;
179 /* the files-to-be-erased list */
180 static struct link *erase_list;
183 * In copy_dir_dir we use two additional single linked lists: The first -
184 * variable name `parent_dirs' - holds information about already copied
185 * directories and is used to detect cyclic symbolic links.
186 * The second (`dest_dirs' below) holds information about just created
187 * target directories and is used to detect when an directory is copied
188 * into itself (we don't want to copy infinitly).
189 * Both lists don't use the linkcount and name structure members of struct
190 * link.
192 static struct link *dest_dirs = NULL;
194 static FileProgressStatus transform_error = FILE_CONT;
196 /*** file scope functions ************************************************************************/
197 /* --------------------------------------------------------------------------------------------- */
199 static char *
200 transform_source (FileOpContext * ctx, const char *source)
202 char *s, *q;
203 char *fnsource;
205 s = g_strdup (source);
207 /* We remove \n from the filename since regex routines would use \n as an anchor */
208 /* this is just to be allowed to maniupulate file names with \n on it */
209 for (q = s; *q != '\0'; q++)
210 if (*q == '\n')
211 *q = ' ';
213 fnsource = (char *) x_basename (s);
215 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
216 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
217 else
219 q = NULL;
220 transform_error = FILE_SKIP;
223 g_free (s);
224 return q;
227 /* --------------------------------------------------------------------------------------------- */
229 static void
230 free_linklist (struct link **lc_linklist)
232 struct link *lp, *lp2;
234 for (lp = *lc_linklist; lp != NULL; lp = lp2)
236 lp2 = lp->next;
237 vfs_path_free (lp->src_vpath);
238 vfs_path_free (lp->dst_vpath);
239 g_free (lp);
241 *lc_linklist = NULL;
244 /* --------------------------------------------------------------------------------------------- */
246 static int
247 is_in_linklist (struct link *lp, const vfs_path_t * vpath, struct stat *sb)
249 struct vfs_class *class;
250 ino_t ino = sb->st_ino;
251 dev_t dev = sb->st_dev;
253 class = vfs_path_get_last_path_vfs (vpath);
255 while (lp != NULL)
257 if (lp->vfs == class)
258 if (lp->ino == ino && lp->dev == dev)
259 return 1;
260 lp = lp->next;
262 return 0;
265 /* --------------------------------------------------------------------------------------------- */
267 * Check and made hardlink
269 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
270 * and a hardlink was succesfully made
273 static gboolean
274 check_hardlinks (const vfs_path_t * src_vpath, const vfs_path_t * dst_vpath, struct stat *pstat)
276 struct link *lp;
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;
283 if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
284 return FALSE;
286 my_vfs = vfs_path_get_by_index (src_vpath, -1)->class;
288 for (lp = linklist; lp != NULL; lp = lp->next)
289 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev)
291 struct vfs_class *lp_name_class;
292 int stat_result;
294 lp_name_class = vfs_path_get_last_path_vfs (lp->src_vpath);
295 stat_result = mc_stat (lp->src_vpath, &link_stat);
297 if (stat_result == 0 && link_stat.st_ino == ino
298 && link_stat.st_dev == dev && lp_name_class == my_vfs)
300 struct vfs_class *p_class, *dst_name_class;
302 dst_name_class = vfs_path_get_last_path_vfs (dst_vpath);
303 p_class = vfs_path_get_last_path_vfs (lp->dst_vpath);
305 if (dst_name_class == p_class &&
306 mc_stat (lp->dst_vpath, &link_stat) == 0 &&
307 mc_link (lp->dst_vpath, dst_vpath) == 0)
308 return TRUE;
311 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
312 return FALSE;
315 lp = g_new0 (struct link, 1);
317 if (lp != NULL)
319 lp->vfs = my_vfs;
320 lp->ino = ino;
321 lp->dev = dev;
322 lp->src_vpath = vfs_path_clone (src_vpath);
323 lp->dst_vpath = vfs_path_clone (dst_vpath);
324 lp->next = linklist;
325 linklist = lp;
327 return FALSE;
330 /* --------------------------------------------------------------------------------------------- */
332 * Duplicate the contents of the symbolic link src_path in dst_path.
333 * Try to make a stable symlink if the option "stable symlink" was
334 * set in the file mask dialog.
335 * If dst_path is an existing symlink it will be deleted silently
336 * (upper levels take already care of existing files at dst_path).
339 static FileProgressStatus
340 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
342 char link_target[MC_MAXPATHLEN];
343 int len;
344 FileProgressStatus return_status;
345 struct stat sb;
346 vfs_path_t *src_vpath;
347 vfs_path_t *dst_vpath;
348 gboolean dst_is_symlink;
349 vfs_path_t *link_target_vpath = NULL;
351 src_vpath = vfs_path_from_str (src_path);
352 dst_vpath = vfs_path_from_str (dst_path);
353 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
355 retry_src_readlink:
356 len = mc_readlink (src_vpath, link_target, MC_MAXPATHLEN - 1);
357 if (len < 0)
359 if (ctx->skip_all)
360 return_status = FILE_SKIPALL;
361 else
363 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
364 if (return_status == FILE_SKIPALL)
365 ctx->skip_all = TRUE;
366 if (return_status == FILE_RETRY)
367 goto retry_src_readlink;
369 goto ret;
371 link_target[len] = 0;
373 if (ctx->stable_symlinks)
376 if (!vfs_file_is_local (src_vpath) || !vfs_file_is_local (dst_vpath))
378 message (D_ERROR, MSG_ERROR,
379 _("Cannot make stable symlinks across"
380 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
381 ctx->stable_symlinks = FALSE;
385 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
387 char *p, *s;
388 vfs_path_t *q;
390 const char *r = strrchr (src_path, PATH_SEP);
392 if (r)
394 p = g_strndup (src_path, r - src_path + 1);
395 if (g_path_is_absolute (dst_path))
396 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
397 else
398 q = vfs_path_build_filename (p, dst_path, (char *) NULL);
400 if (vfs_path_tokens_count (q) > 1)
402 vfs_path_t *tmp_vpath1, *tmp_vpath2;
404 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
405 s = g_strconcat (p, link_target, (char *) NULL);
406 g_free (p);
407 g_strlcpy (link_target, s, sizeof (link_target));
408 g_free (s);
409 tmp_vpath2 = vfs_path_from_str (link_target);
410 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
411 vfs_path_free (tmp_vpath1);
412 vfs_path_free (tmp_vpath2);
413 if (s)
415 g_strlcpy (link_target, s, sizeof (link_target));
416 g_free (s);
419 else
420 g_free (p);
421 vfs_path_free (q);
424 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
426 retry_dst_symlink:
427 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
429 /* Success */
430 return_status = FILE_CONT;
431 goto ret;
434 * if dst_exists, it is obvious that this had failed.
435 * We can delete the old symlink and try again...
437 if (dst_is_symlink)
439 if (mc_unlink (dst_vpath) == 0)
440 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
442 /* Success */
443 return_status = FILE_CONT;
444 goto ret;
447 if (ctx->skip_all)
448 return_status = FILE_SKIPALL;
449 else
451 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
452 if (return_status == FILE_SKIPALL)
453 ctx->skip_all = TRUE;
454 if (return_status == FILE_RETRY)
455 goto retry_dst_symlink;
458 ret:
459 vfs_path_free (src_vpath);
460 vfs_path_free (dst_vpath);
461 vfs_path_free (link_target_vpath);
462 return return_status;
465 /* --------------------------------------------------------------------------------------------- */
467 static FileProgressStatus
468 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add)
470 struct timeval tv_current;
471 static struct timeval tv_start = { };
473 tctx->progress_count++;
474 tctx->progress_bytes += (uintmax_t) add;
476 if (tv_start.tv_sec == 0)
478 gettimeofday (&tv_start, (struct timezone *) NULL);
480 gettimeofday (&tv_current, (struct timezone *) NULL);
481 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
483 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
485 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
486 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
488 tv_start.tv_sec = tv_current.tv_sec;
491 return check_progress_buttons (ctx);
494 /* --------------------------------------------------------------------------------------------- */
496 static FileProgressStatus
497 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
499 char *msg;
500 int result = 0;
501 const char *head_msg;
503 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
505 msg = g_strdup_printf (fmt, a, b);
506 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
507 g_free (msg);
508 do_refresh ();
510 return (result == 1) ? FILE_ABORT : FILE_SKIP;
513 /* --------------------------------------------------------------------------------------------- */
515 static FileProgressStatus
516 warn_same_file (const char *fmt, const char *a, const char *b)
518 #ifdef ENABLE_BACKGROUND
519 /* *INDENT-OFF* */
520 union
522 void *p;
523 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
524 const char *a, const char *b);
525 } pntr;
526 /* *INDENT-ON* */
528 pntr.f = real_warn_same_file;
530 if (mc_global.we_are_background)
531 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
532 #endif
533 return real_warn_same_file (Foreground, fmt, a, b);
536 /* --------------------------------------------------------------------------------------------- */
537 /* {{{ Query/status report routines */
539 static FileProgressStatus
540 real_do_file_error (enum OperationMode mode, const char *error)
542 int result;
543 const char *msg;
545 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
546 result =
547 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
549 switch (result)
551 case 0:
552 do_refresh ();
553 return FILE_SKIP;
555 case 1:
556 do_refresh ();
557 return FILE_SKIPALL;
559 case 2:
560 do_refresh ();
561 return FILE_RETRY;
563 case 3:
564 default:
565 return FILE_ABORT;
569 /* --------------------------------------------------------------------------------------------- */
571 static FileProgressStatus
572 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
574 gchar *text;
576 if (ctx->recursive_result < RECURSIVE_ALWAYS)
578 const char *msg = mode == Foreground
579 ? _("\nDirectory not empty.\nDelete it recursively?")
580 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
581 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
583 if (safe_delete)
584 query_set_sel (1);
586 ctx->recursive_result =
587 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
588 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
590 if (ctx->recursive_result != RECURSIVE_ABORT)
591 do_refresh ();
592 g_free (text);
595 switch (ctx->recursive_result)
597 case RECURSIVE_YES:
598 case RECURSIVE_ALWAYS:
599 return FILE_CONT;
601 case RECURSIVE_NO:
602 case RECURSIVE_NEVER:
603 return FILE_SKIP;
605 case RECURSIVE_ABORT:
606 default:
607 return FILE_ABORT;
611 /* --------------------------------------------------------------------------------------------- */
613 #ifdef ENABLE_BACKGROUND
614 static FileProgressStatus
615 do_file_error (const char *str)
617 union
619 void *p;
620 FileProgressStatus (*f) (enum OperationMode, const char *);
621 } pntr;
622 pntr.f = real_do_file_error;
624 if (mc_global.we_are_background)
625 return parent_call (pntr.p, NULL, 1, strlen (str), str);
626 else
627 return real_do_file_error (Foreground, str);
630 /* --------------------------------------------------------------------------------------------- */
632 static FileProgressStatus
633 query_recursive (FileOpContext * ctx, const char *s)
635 union
637 void *p;
638 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
639 } pntr;
640 pntr.f = real_query_recursive;
642 if (mc_global.we_are_background)
643 return parent_call (pntr.p, ctx, 1, strlen (s), s);
644 else
645 return real_query_recursive (ctx, Foreground, s);
648 /* --------------------------------------------------------------------------------------------- */
650 static FileProgressStatus
651 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
652 struct stat *_d_stat)
654 union
656 void *p;
657 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
658 struct stat *, struct stat *);
659 } pntr;
660 pntr.f = file_progress_real_query_replace;
662 if (mc_global.we_are_background)
663 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
664 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
665 else
666 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
669 #else
670 /* --------------------------------------------------------------------------------------------- */
672 static FileProgressStatus
673 do_file_error (const char *str)
675 return real_do_file_error (Foreground, str);
678 /* --------------------------------------------------------------------------------------------- */
680 static FileProgressStatus
681 query_recursive (FileOpContext * ctx, const char *s)
683 return real_query_recursive (ctx, Foreground, s);
686 /* --------------------------------------------------------------------------------------------- */
688 static FileProgressStatus
689 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
690 struct stat *_d_stat)
692 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
695 #endif /* !ENABLE_BACKGROUND */
697 /* --------------------------------------------------------------------------------------------- */
698 /** Report error with two files */
700 static FileProgressStatus
701 files_error (const char *format, const char *file1, const char *file2)
703 char buf[BUF_MEDIUM];
704 char *nfile1 = g_strdup (path_trunc (file1, 15));
705 char *nfile2 = g_strdup (path_trunc (file2, 15));
707 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
709 g_free (nfile1);
710 g_free (nfile2);
712 return do_file_error (buf);
715 /* }}} */
717 /* --------------------------------------------------------------------------------------------- */
719 static void
720 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
721 struct timeval tv_current, struct timeval tv_transfer_start,
722 off_t file_size, off_t n_read_total)
724 long dt;
726 /* 1. Update rotating dash after some time */
727 rotate_dash ();
729 /* 3. Compute ETA */
730 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
732 if (n_read_total == 0)
733 ctx->eta_secs = 0.0;
734 else
736 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
737 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
740 /* 4. Compute BPS rate */
741 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
742 if (ctx->bps_time < 1)
743 ctx->bps_time = 1;
744 ctx->bps = n_read_total / ctx->bps_time;
746 /* 5. Compute total ETA and BPS */
747 if (ctx->progress_bytes != 0)
749 uintmax_t remain_bytes;
751 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
752 #if 1
754 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
756 if (total_secs < 1)
757 total_secs = 1;
759 tctx->bps = tctx->copied_bytes / total_secs;
760 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
762 #else
763 /* broken on lot of little files */
764 tctx->bps_count++;
765 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
766 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
767 #endif
771 /* --------------------------------------------------------------------------------------------- */
773 /* {{{ Move routines */
774 static FileProgressStatus
775 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
777 struct stat src_stats, dst_stats;
778 FileProgressStatus return_status = FILE_CONT;
779 gboolean copy_done = FALSE;
780 gboolean old_ask_overwrite;
781 vfs_path_t *src_vpath, *dst_vpath;
783 src_vpath = vfs_path_from_str (s);
784 dst_vpath = vfs_path_from_str (d);
786 file_progress_show_source (ctx, src_vpath);
787 file_progress_show_target (ctx, dst_vpath);
789 if (check_progress_buttons (ctx) == FILE_ABORT)
791 return_status = FILE_ABORT;
792 goto ret;
795 mc_refresh ();
797 while (mc_lstat (src_vpath, &src_stats) != 0)
799 /* Source doesn't exist */
800 if (ctx->skip_all)
801 return_status = FILE_SKIPALL;
802 else
804 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
805 if (return_status == FILE_SKIPALL)
806 ctx->skip_all = TRUE;
809 if (return_status != FILE_RETRY)
810 goto ret;
813 if (mc_lstat (dst_vpath, &dst_stats) == 0)
815 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
817 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
818 goto ret;
821 if (S_ISDIR (dst_stats.st_mode))
823 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
824 do_refresh ();
825 return_status = FILE_SKIP;
826 goto ret;
829 if (confirm_overwrite)
831 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
832 if (return_status != FILE_CONT)
833 goto ret;
835 /* Ok to overwrite */
838 if (!ctx->do_append)
840 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
842 return_status = make_symlink (ctx, s, d);
843 if (return_status == FILE_CONT)
844 goto retry_src_remove;
845 goto ret;
848 if (mc_rename (src_vpath, dst_vpath) == 0)
850 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
851 goto ret;
854 #if 0
855 /* Comparison to EXDEV seems not to work in nfs if you're moving from
856 one nfs to the same, but on the server it is on two different
857 filesystems. Then nfs returns EIO instead of EXDEV.
858 Hope it will not hurt if we always in case of error try to copy/delete. */
859 else
860 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
862 if (errno != EXDEV)
864 if (ctx->skip_all)
865 return_status = FILE_SKIPALL;
866 else
868 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
869 if (return_status == FILE_SKIPALL)
870 ctx->skip_all = TRUE;
871 if (return_status == FILE_RETRY)
872 goto retry_rename;
875 goto ret;
877 #endif
879 /* Failed because filesystem boundary -> copy the file instead */
880 old_ask_overwrite = tctx->ask_overwrite;
881 tctx->ask_overwrite = FALSE;
882 return_status = copy_file_file (tctx, ctx, s, d);
883 tctx->ask_overwrite = old_ask_overwrite;
884 if (return_status != FILE_CONT)
885 goto ret;
887 copy_done = TRUE;
889 file_progress_show_source (ctx, NULL);
890 file_progress_show (ctx, 0, 0, "", FALSE);
892 return_status = check_progress_buttons (ctx);
893 if (return_status != FILE_CONT)
894 goto ret;
895 mc_refresh ();
897 retry_src_remove:
898 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
900 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
901 if (return_status == FILE_RETRY)
902 goto retry_src_remove;
903 if (return_status == FILE_SKIPALL)
904 ctx->skip_all = TRUE;
905 goto ret;
908 if (!copy_done)
909 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
911 ret:
912 vfs_path_free (src_vpath);
913 vfs_path_free (dst_vpath);
915 return return_status;
918 /* }}} */
920 /* --------------------------------------------------------------------------------------------- */
921 /* {{{ Erase routines */
922 /** Don't update progress status if progress_count==NULL */
924 static FileProgressStatus
925 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * vpath)
927 int return_status;
928 struct stat buf;
929 char *s;
931 s = vfs_path_to_str (vpath);
932 file_progress_show_deleting (ctx, s);
933 if (check_progress_buttons (ctx) == FILE_ABORT)
935 g_free (s);
936 return FILE_ABORT;
938 mc_refresh ();
940 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
942 /* ignore, most likely the mc_unlink fails, too */
943 buf.st_size = 0;
946 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
948 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
949 if (return_status == FILE_ABORT)
951 g_free (s);
952 return return_status;
954 if (return_status == FILE_RETRY)
955 continue;
956 if (return_status == FILE_SKIPALL)
957 ctx->skip_all = TRUE;
958 break;
960 g_free (s);
961 if (tctx->progress_count == 0)
962 return FILE_CONT;
963 return progress_update_one (tctx, ctx, buf.st_size);
966 /* --------------------------------------------------------------------------------------------- */
969 Recursive remove of files
970 abort->cancel stack
971 skip ->warn every level, gets default
972 skipall->remove as much as possible
974 static FileProgressStatus
975 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
977 struct dirent *next;
978 struct stat buf;
979 DIR *reading;
980 char *path;
981 FileProgressStatus return_status = FILE_CONT;
982 vfs_path_t *vpath;
984 if (strcmp (s, "..") == 0)
985 return FILE_RETRY;
987 vpath = vfs_path_from_str (s);
988 reading = mc_opendir (vpath);
990 if (reading == NULL)
992 return_status = FILE_RETRY;
993 goto ret;
996 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
998 vfs_path_t *tmp_vpath;
1000 if (!strcmp (next->d_name, "."))
1001 continue;
1002 if (!strcmp (next->d_name, ".."))
1003 continue;
1004 path = mc_build_filename (s, next->d_name, NULL);
1005 tmp_vpath = vfs_path_from_str (path);
1006 if (mc_lstat (tmp_vpath, &buf) != 0)
1008 g_free (path);
1009 mc_closedir (reading);
1010 vfs_path_free (tmp_vpath);
1011 return_status = FILE_RETRY;
1012 goto ret;
1014 if (S_ISDIR (buf.st_mode))
1015 return_status = recursive_erase (tctx, ctx, path);
1016 else
1017 return_status = erase_file (tctx, ctx, tmp_vpath);
1018 vfs_path_free (tmp_vpath);
1019 g_free (path);
1021 mc_closedir (reading);
1022 if (return_status == FILE_ABORT)
1023 goto ret;
1025 file_progress_show_deleting (ctx, s);
1026 if (check_progress_buttons (ctx) == FILE_ABORT)
1028 return_status = FILE_ABORT;
1029 goto ret;
1031 mc_refresh ();
1033 while (my_rmdir (s) != 0 && !ctx->skip_all)
1035 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1036 if (return_status == FILE_RETRY)
1037 continue;
1038 if (return_status == FILE_ABORT)
1039 goto ret;
1040 if (return_status == FILE_SKIPALL)
1041 ctx->skip_all = TRUE;
1042 break;
1045 ret:
1046 vfs_path_free (vpath);
1047 return return_status;
1050 /* --------------------------------------------------------------------------------------------- */
1051 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1052 in the directory path points to, 0 else. */
1054 static int
1055 check_dir_is_empty (const vfs_path_t * vpath)
1057 DIR *dir;
1058 struct dirent *d;
1059 int i;
1061 dir = mc_opendir (vpath);
1062 if (!dir)
1063 return -1;
1065 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1067 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1068 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1069 continue; /* "." or ".." */
1070 i = 0;
1071 break;
1074 mc_closedir (dir);
1075 return i;
1078 /* --------------------------------------------------------------------------------------------- */
1080 static FileProgressStatus
1081 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1083 FileProgressStatus error;
1084 vfs_path_t *s_vpath;
1086 if (strcmp (s, "..") == 0)
1087 return FILE_SKIP;
1089 if (strcmp (s, ".") == 0)
1090 return FILE_SKIP;
1092 file_progress_show_deleting (ctx, s);
1093 if (check_progress_buttons (ctx) == FILE_ABORT)
1094 return FILE_ABORT;
1096 mc_refresh ();
1098 s_vpath = vfs_path_from_str (s);
1100 if (check_dir_is_empty (s_vpath) == 1) /* not empty or error */
1102 while (my_rmdir (s) != 0 && !ctx->skip_all)
1104 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1105 if (error == FILE_SKIPALL)
1106 ctx->skip_all = TRUE;
1107 if (error != FILE_RETRY)
1109 vfs_path_free (s_vpath);
1110 return error;
1115 vfs_path_free (s_vpath);
1116 return FILE_CONT;
1119 /* }}} */
1121 /* --------------------------------------------------------------------------------------------- */
1122 /* {{{ Panel operate routines */
1125 * Return currently selected entry name or the name of the first marked
1126 * entry if there is one.
1129 static char *
1130 panel_get_file (WPanel * panel)
1132 if (get_current_type () == view_tree)
1134 WTree *tree;
1136 tree = (WTree *) get_panel_widget (get_current_index ());
1137 return vfs_path_to_str (tree_selected_name (tree));
1140 if (panel->marked != 0)
1142 int i;
1144 for (i = 0; i < panel->count; i++)
1145 if (panel->dir.list[i].f.marked)
1146 return g_strdup (panel->dir.list[i].fname);
1148 return g_strdup (panel->dir.list[panel->selected].fname);
1151 /* --------------------------------------------------------------------------------------------- */
1153 * panel_compute_totals:
1155 * compute the number of files and the number of bytes
1156 * used up by the whole selection, recursing directories
1157 * as required. In addition, it checks to see if it will
1158 * overwrite any files by doing the copy.
1161 static FileProgressStatus
1162 panel_compute_totals (const WPanel * panel, const void *ui,
1163 compute_dir_size_callback cback,
1164 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1166 int i;
1168 *ret_marked = 0;
1169 *ret_total = 0;
1171 for (i = 0; i < panel->count; i++)
1173 struct stat *s;
1175 if (!panel->dir.list[i].f.marked)
1176 continue;
1178 s = &panel->dir.list[i].st;
1180 if (S_ISDIR (s->st_mode))
1182 vfs_path_t *p;
1183 size_t subdir_count = 0;
1184 uintmax_t subdir_bytes = 0;
1185 FileProgressStatus status;
1187 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, NULL);
1188 status =
1189 compute_dir_size (p, ui, cback, &subdir_count, &subdir_bytes, compute_symlinks);
1190 vfs_path_free (p);
1192 if (status != FILE_CONT)
1193 return FILE_ABORT;
1195 *ret_marked += subdir_count;
1196 *ret_total += subdir_bytes;
1198 else
1200 (*ret_marked)++;
1201 *ret_total += (uintmax_t) s->st_size;
1205 return FILE_CONT;
1208 /* --------------------------------------------------------------------------------------------- */
1210 /** Initialize variables for progress bars */
1211 static FileProgressStatus
1212 panel_operate_init_totals (FileOperation operation,
1213 const WPanel * panel, const char *source, FileOpContext * ctx)
1215 FileProgressStatus status;
1217 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1219 ComputeDirSizeUI *ui;
1221 ui = compute_dir_size_create_ui ();
1223 if (source == NULL)
1224 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1225 &ctx->progress_count, &ctx->progress_bytes,
1226 ctx->follow_links);
1227 else
1229 vfs_path_t *p;
1231 p = vfs_path_from_str (source);
1232 status = compute_dir_size (p, ui, compute_dir_size_update_ui,
1233 &ctx->progress_count, &ctx->progress_bytes,
1234 ctx->follow_links);
1235 vfs_path_free (p);
1238 compute_dir_size_destroy_ui (ui);
1240 ctx->progress_totals_computed = (status == FILE_CONT);
1242 else
1244 status = FILE_CONT;
1245 ctx->progress_count = panel->marked;
1246 ctx->progress_bytes = panel->total;
1247 ctx->progress_totals_computed = FALSE;
1250 return status;
1253 /* --------------------------------------------------------------------------------------------- */
1255 * Generate user prompt for panel operation.
1256 * single_source is the name if the source entry or NULL for multiple
1257 * entries.
1258 * src_stat is only used when single_source is not NULL.
1261 static char *
1262 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1263 gboolean single_source, const struct stat *src_stat)
1265 const char *sp, *cp;
1266 char format_string[BUF_MEDIUM];
1267 char *dp = format_string;
1268 gboolean build_question = FALSE;
1270 static gboolean i18n_flag = FALSE;
1271 if (!i18n_flag)
1273 size_t i;
1275 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1276 op_names1[i] = Q_ (op_names1[i]);
1278 #ifdef ENABLE_NLS
1279 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1280 prompt_parts[i] = _(prompt_parts[i]);
1282 one_format = _(one_format);
1283 many_format = _(many_format);
1284 question_format = _(question_format);
1285 #endif /* ENABLE_NLS */
1286 i18n_flag = TRUE;
1289 sp = single_source ? one_format : many_format;
1291 while (*sp != '\0')
1293 switch (*sp)
1295 case '%':
1296 cp = NULL;
1297 switch (sp[1])
1299 case 'o':
1300 cp = op_names1[operation];
1301 break;
1302 case 'm':
1303 if (operation == OP_DELETE)
1305 cp = "";
1306 build_question = TRUE;
1308 else
1309 cp = prompt_parts[5];
1310 break;
1311 case 'e':
1312 if (operation == OP_DELETE)
1314 cp = "";
1315 build_question = TRUE;
1317 else
1318 cp = prompt_parts[6];
1319 break;
1320 case 'f':
1321 if (single_source)
1322 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1323 else
1324 cp = (panel->marked == panel->dirs_marked)
1325 ? prompt_parts[3]
1326 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1327 break;
1328 default:
1329 *dp++ = *sp++;
1332 if (cp != NULL)
1334 sp += 2;
1335 while (*cp != '\0')
1336 *dp++ = *cp++;
1338 break;
1339 default:
1340 *dp++ = *sp++;
1343 *dp = '\0';
1345 if (build_question)
1347 char tmp[BUF_MEDIUM];
1349 memmove (tmp, format_string, sizeof (tmp));
1350 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1353 return g_strdup (format_string);
1356 /* --------------------------------------------------------------------------------------------- */
1358 #ifdef ENABLE_BACKGROUND
1359 static int
1360 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1362 int pid = ctx->pid;
1364 (void) mode;
1365 ctx->pid = 0;
1367 unregister_task_with_pid (pid);
1368 /* file_op_context_destroy(ctx); */
1369 return 1;
1371 #endif
1372 /* }}} */
1374 /* --------------------------------------------------------------------------------------------- */
1375 /*** public functions ****************************************************************************/
1376 /* --------------------------------------------------------------------------------------------- */
1378 FileProgressStatus
1379 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1380 const char *src_path, const char *dst_path)
1382 uid_t src_uid = (uid_t) (-1);
1383 gid_t src_gid = (gid_t) (-1);
1385 int src_desc, dest_desc = -1;
1386 int n_read, n_written;
1387 mode_t src_mode = 0; /* The mode of the source file */
1388 struct stat sb, sb2;
1389 struct utimbuf utb;
1390 gboolean dst_exists = FALSE, appending = FALSE;
1391 off_t file_size = -1;
1392 FileProgressStatus return_status, temp_status;
1393 struct timeval tv_transfer_start;
1394 dest_status_t dst_status = DEST_NONE;
1395 int open_flags;
1396 gboolean is_first_time = TRUE;
1397 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1399 /* FIXME: We should not be using global variables! */
1400 ctx->do_reget = 0;
1401 return_status = FILE_RETRY;
1403 dst_vpath = vfs_path_from_str (dst_path);
1404 src_vpath = vfs_path_from_str (src_path);
1406 file_progress_show_source (ctx, src_vpath);
1407 file_progress_show_target (ctx, dst_vpath);
1409 if (check_progress_buttons (ctx) == FILE_ABORT)
1411 return_status = FILE_ABORT;
1412 goto ret_fast;
1415 mc_refresh ();
1417 while (mc_stat (dst_vpath, &sb2) == 0)
1419 if (S_ISDIR (sb2.st_mode))
1421 if (ctx->skip_all)
1422 return_status = FILE_SKIPALL;
1423 else
1425 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1426 if (return_status == FILE_SKIPALL)
1427 ctx->skip_all = TRUE;
1428 if (return_status == FILE_RETRY)
1429 continue;
1431 goto ret_fast;
1434 dst_exists = TRUE;
1435 break;
1438 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1440 if (ctx->skip_all)
1441 return_status = FILE_SKIPALL;
1442 else
1444 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1445 if (return_status == FILE_SKIPALL)
1446 ctx->skip_all = TRUE;
1449 if (return_status != FILE_RETRY)
1450 goto ret_fast;
1453 if (dst_exists)
1455 /* Destination already exists */
1456 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1458 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1459 src_path, dst_path);
1460 goto ret_fast;
1463 /* Should we replace destination? */
1464 if (tctx->ask_overwrite)
1466 ctx->do_reget = 0;
1467 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1468 if (return_status != FILE_CONT)
1469 goto ret_fast;
1473 if (!ctx->do_append)
1475 /* Check the hardlinks */
1476 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &sb))
1478 /* We have made a hardlink - no more processing is necessary */
1479 return_status = FILE_CONT;
1480 goto ret_fast;
1483 if (S_ISLNK (sb.st_mode))
1485 return_status = make_symlink (ctx, src_path, dst_path);
1486 goto ret_fast;
1489 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1490 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1492 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1493 && !ctx->skip_all)
1495 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1496 if (return_status == FILE_RETRY)
1497 continue;
1498 if (return_status == FILE_SKIPALL)
1499 ctx->skip_all = TRUE;
1500 goto ret_fast;
1502 /* Success */
1504 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1505 && !ctx->skip_all)
1507 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1508 if (temp_status == FILE_SKIP)
1509 break;
1510 if (temp_status == FILE_SKIPALL)
1511 ctx->skip_all = TRUE;
1512 if (temp_status != FILE_RETRY)
1514 return_status = temp_status;
1515 goto ret_fast;
1519 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1520 && !ctx->skip_all)
1522 temp_status = file_error (_("Cannot chmod 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 return_status = FILE_CONT;
1535 goto ret_fast;
1539 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1541 while ((src_desc = mc_open (src_vpath, 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 goto ret_fast;
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;
1580 src_mode = sb.st_mode;
1581 src_uid = sb.st_uid;
1582 src_gid = sb.st_gid;
1583 utb.actime = sb.st_atime;
1584 utb.modtime = sb.st_mtime;
1585 file_size = sb.st_size;
1587 open_flags = O_WRONLY;
1588 if (dst_exists)
1590 if (ctx->do_append != 0)
1591 open_flags |= O_APPEND;
1592 else
1593 open_flags |= O_CREAT | O_TRUNC;
1595 else
1597 open_flags |= O_CREAT | O_EXCL;
1600 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
1602 if (errno != EEXIST)
1604 if (ctx->skip_all)
1605 return_status = FILE_SKIPALL;
1606 else
1608 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1609 if (return_status == FILE_RETRY)
1610 continue;
1611 if (return_status == FILE_SKIPALL)
1612 ctx->skip_all = TRUE;
1613 ctx->do_append = FALSE;
1616 goto ret;
1618 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1620 appending = ctx->do_append;
1621 ctx->do_append = FALSE;
1623 /* Find out the optimal buffer size. */
1624 while (mc_fstat (dest_desc, &sb) != 0)
1626 if (ctx->skip_all)
1627 return_status = FILE_SKIPALL;
1628 else
1630 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1631 if (return_status == FILE_RETRY)
1632 continue;
1633 if (return_status == FILE_SKIPALL)
1634 ctx->skip_all = TRUE;
1636 goto ret;
1639 while (TRUE)
1641 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1642 if (errno == 0)
1643 break;
1645 if (ctx->skip_all)
1646 return_status = FILE_SKIPALL;
1647 else
1649 return_status =
1650 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1651 if (return_status == FILE_RETRY)
1652 continue;
1653 if (return_status == FILE_SKIPALL)
1654 ctx->skip_all = TRUE;
1656 mc_close (dest_desc);
1657 dest_desc = -1;
1658 mc_unlink (dst_vpath);
1659 dst_status = DEST_NONE;
1660 goto ret;
1663 ctx->eta_secs = 0.0;
1664 ctx->bps = 0;
1666 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1667 file_progress_show (ctx, 0, file_size, "", TRUE);
1668 else
1669 file_progress_show (ctx, 1, 1, "", TRUE);
1670 return_status = check_progress_buttons (ctx);
1671 mc_refresh ();
1673 if (return_status != FILE_CONT)
1674 goto ret;
1677 off_t n_read_total = 0;
1678 struct timeval tv_current, tv_last_update, tv_last_input;
1679 int secs, update_secs;
1680 const char *stalled_msg = "";
1682 tv_last_update = tv_transfer_start;
1684 while (TRUE)
1686 char buf[BUF_8K];
1688 /* src_read */
1689 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1690 n_read = -1;
1691 else
1692 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1694 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1695 if (return_status == FILE_RETRY)
1696 continue;
1697 if (return_status == FILE_SKIPALL)
1698 ctx->skip_all = TRUE;
1699 goto ret;
1701 if (n_read == 0)
1702 break;
1704 gettimeofday (&tv_current, NULL);
1706 if (n_read > 0)
1708 char *t = buf;
1709 n_read_total += n_read;
1711 /* Windows NT ftp servers report that files have no
1712 * permissions: -------, so if we happen to have actually
1713 * read something, we should fix the permissions.
1715 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1716 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1717 gettimeofday (&tv_last_input, NULL);
1719 /* dst_write */
1720 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read && !ctx->skip_all)
1722 if (n_written > 0)
1724 n_read -= n_written;
1725 t += n_written;
1726 continue;
1728 return_status = file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1729 if (return_status == FILE_SKIP)
1730 break;
1731 if (return_status == FILE_SKIPALL)
1732 ctx->skip_all = TRUE;
1733 if (return_status != FILE_RETRY)
1734 goto ret;
1738 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1740 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1741 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1743 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1745 copy_file_file_display_progress (tctx, ctx,
1746 tv_current,
1747 tv_transfer_start, file_size, n_read_total);
1748 tv_last_update = tv_current;
1750 is_first_time = FALSE;
1752 if (update_secs > FILEOP_STALLING_INTERVAL)
1754 stalled_msg = _("(stalled)");
1758 gboolean force_update;
1760 force_update =
1761 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1763 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1765 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1766 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1769 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1770 force_update);
1772 mc_refresh ();
1774 return_status = check_progress_buttons (ctx);
1776 if (return_status != FILE_CONT)
1778 mc_refresh ();
1779 goto ret;
1784 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1786 ret:
1787 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1789 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1790 if (temp_status == FILE_RETRY)
1791 continue;
1792 if (temp_status == FILE_ABORT)
1793 return_status = temp_status;
1794 if (temp_status == FILE_SKIPALL)
1795 ctx->skip_all = TRUE;
1796 break;
1799 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1801 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1802 if (temp_status == FILE_RETRY)
1803 continue;
1804 if (temp_status == FILE_SKIPALL)
1805 ctx->skip_all = TRUE;
1806 return_status = temp_status;
1807 break;
1810 if (dst_status == DEST_SHORT)
1812 /* Remove short file */
1813 int result;
1815 result = query_dialog (Q_ ("DialogTitle|Copy"),
1816 _("Incomplete file was retrieved. Keep it?"),
1817 D_ERROR, 2, _("&Delete"), _("&Keep"));
1818 if (result == 0)
1819 mc_unlink (dst_vpath);
1821 else if (dst_status == DEST_FULL)
1823 /* Copy has succeeded */
1824 if (!appending && ctx->preserve_uidgid)
1826 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1828 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1829 if (temp_status == FILE_RETRY)
1830 continue;
1831 if (temp_status == FILE_SKIPALL)
1833 ctx->skip_all = TRUE;
1834 return_status = FILE_CONT;
1836 if (temp_status == FILE_SKIP)
1837 return_status = FILE_CONT;
1838 break;
1842 if (!appending)
1844 if (ctx->preserve)
1846 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1848 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1849 if (temp_status == FILE_RETRY)
1850 continue;
1851 if (temp_status == FILE_SKIPALL)
1853 ctx->skip_all = TRUE;
1854 return_status = FILE_CONT;
1856 if (temp_status == FILE_SKIP)
1857 return_status = FILE_CONT;
1858 break;
1861 else if (!dst_exists)
1863 src_mode = umask (-1);
1864 umask (src_mode);
1865 src_mode = 0100666 & ~src_mode;
1866 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
1868 mc_utime (dst_vpath, &utb);
1872 if (return_status == FILE_CONT)
1873 return_status = progress_update_one (tctx, ctx, file_size);
1875 ret_fast:
1876 vfs_path_free (src_vpath);
1877 vfs_path_free (dst_vpath);
1878 return return_status;
1881 /* --------------------------------------------------------------------------------------------- */
1883 * I think these copy_*_* functions should have a return type.
1884 * anyway, this function *must* have two directories as arguments.
1886 /* FIXME: This function needs to check the return values of the
1887 function calls */
1889 FileProgressStatus
1890 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1891 gboolean toplevel, gboolean move_over, gboolean do_delete, struct link * parent_dirs)
1893 struct dirent *next;
1894 struct stat buf, cbuf;
1895 DIR *reading;
1896 char *dest_dir = NULL;
1897 FileProgressStatus return_status = FILE_CONT;
1898 struct utimbuf utb;
1899 struct link *lp;
1900 char *d;
1901 vfs_path_t *src_vpath, *dst_vpath, *dest_dir_vpath = NULL;
1903 d = g_strdup (_d);
1905 src_vpath = vfs_path_from_str (s);
1906 dst_vpath = vfs_path_from_str (_d);
1908 /* First get the mode of the source dir */
1910 retry_src_stat:
1911 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
1913 if (ctx->skip_all)
1914 return_status = FILE_SKIPALL;
1915 else
1917 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1918 if (return_status == FILE_RETRY)
1919 goto retry_src_stat;
1920 if (return_status == FILE_SKIPALL)
1921 ctx->skip_all = TRUE;
1923 goto ret_fast;
1926 if (is_in_linklist (dest_dirs, src_vpath, &cbuf))
1928 /* Don't copy a directory we created before (we don't want to copy
1929 infinitely if a directory is copied into itself) */
1930 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1931 return_status = FILE_CONT;
1932 goto ret_fast;
1935 /* Hmm, hardlink to directory??? - Norbert */
1936 /* FIXME: In this step we should do something
1937 in case the destination already exist */
1938 /* Check the hardlinks */
1939 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &cbuf))
1941 /* We have made a hardlink - no more processing is necessary */
1942 goto ret_fast;
1945 if (!S_ISDIR (cbuf.st_mode))
1947 if (ctx->skip_all)
1948 return_status = FILE_SKIPALL;
1949 else
1951 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1952 if (return_status == FILE_RETRY)
1953 goto retry_src_stat;
1954 if (return_status == FILE_SKIPALL)
1955 ctx->skip_all = TRUE;
1957 goto ret_fast;
1960 if (is_in_linklist (parent_dirs, src_vpath, &cbuf))
1962 /* we found a cyclic symbolic link */
1963 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
1964 return_status = FILE_SKIP;
1965 goto ret_fast;
1968 lp = g_new0 (struct link, 1);
1969 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
1970 lp->ino = cbuf.st_ino;
1971 lp->dev = cbuf.st_dev;
1972 lp->next = parent_dirs;
1973 parent_dirs = lp;
1975 retry_dst_stat:
1976 /* Now, check if the dest dir exists, if not, create it. */
1977 if (mc_stat (dst_vpath, &buf) != 0)
1979 /* Here the dir doesn't exist : make it ! */
1980 if (move_over)
1982 if (mc_rename (src_vpath, dst_vpath) == 0)
1984 return_status = FILE_CONT;
1985 goto ret;
1988 dest_dir = d;
1989 d = NULL;
1991 else
1994 * If the destination directory exists, we want to copy the whole
1995 * directory, but we only want this to happen once.
1997 * Escape sequences added to the * to compiler warnings.
1998 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
1999 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2001 if (!S_ISDIR (buf.st_mode))
2003 if (ctx->skip_all)
2004 return_status = FILE_SKIPALL;
2005 else
2007 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2008 if (return_status == FILE_SKIPALL)
2009 ctx->skip_all = TRUE;
2010 if (return_status == FILE_RETRY)
2011 goto retry_dst_stat;
2013 goto ret;
2015 /* Dive into subdir if exists */
2016 if (toplevel && ctx->dive_into_subdirs)
2018 dest_dir = mc_build_filename (d, x_basename (s), NULL);
2020 else
2022 dest_dir = d;
2023 d = NULL;
2024 goto dont_mkdir;
2027 dest_dir_vpath = vfs_path_from_str (dest_dir);
2028 while (my_mkdir (dest_dir_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2030 if (ctx->skip_all)
2031 return_status = FILE_SKIPALL;
2032 else
2034 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
2035 if (return_status == FILE_SKIPALL)
2036 ctx->skip_all = TRUE;
2038 if (return_status != FILE_RETRY)
2039 goto ret;
2042 lp = g_new0 (struct link, 1);
2043 mc_stat (dest_dir_vpath, &buf);
2044 lp->vfs = vfs_path_get_by_index (dest_dir_vpath, -1)->class;
2045 lp->ino = buf.st_ino;
2046 lp->dev = buf.st_dev;
2047 lp->next = dest_dirs;
2048 dest_dirs = lp;
2050 if (ctx->preserve_uidgid)
2052 while (mc_chown (dest_dir_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2054 if (ctx->skip_all)
2055 return_status = FILE_SKIPALL;
2056 else
2058 return_status =
2059 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
2060 if (return_status == FILE_SKIPALL)
2061 ctx->skip_all = TRUE;
2063 if (return_status != FILE_RETRY)
2064 goto ret;
2068 dont_mkdir:
2069 /* open the source dir for reading */
2070 reading = mc_opendir (src_vpath);
2071 if (reading == NULL)
2072 goto ret;
2074 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2076 char *path;
2077 vfs_path_t *tmp_vpath;
2079 * Now, we don't want '.' and '..' to be created / copied at any time
2081 if (!strcmp (next->d_name, "."))
2082 continue;
2083 if (!strcmp (next->d_name, ".."))
2084 continue;
2086 /* get the filename and add it to the src directory */
2087 path = mc_build_filename (s, next->d_name, NULL);
2088 tmp_vpath = vfs_path_from_str (path);
2090 (*ctx->stat_func) (tmp_vpath, &buf);
2091 if (S_ISDIR (buf.st_mode))
2093 char *mdpath;
2095 mdpath = mc_build_filename (dest_dir, next->d_name, NULL);
2097 * From here, we just intend to recursively copy subdirs, not
2098 * the double functionality of copying different when the target
2099 * dir already exists. So, we give the recursive call the flag 0
2100 * meaning no toplevel.
2102 return_status =
2103 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2104 g_free (mdpath);
2106 else
2108 char *dest_file;
2110 dest_file = mc_build_filename (dest_dir, x_basename (path), NULL);
2111 return_status = copy_file_file (tctx, ctx, path, dest_file);
2112 g_free (dest_file);
2114 if (do_delete && return_status == FILE_CONT)
2116 if (ctx->erase_at_end)
2118 static struct link *tail;
2120 lp = g_new0 (struct link, 1);
2121 lp->src_vpath = vfs_path_clone (tmp_vpath);
2122 lp->st_mode = buf.st_mode;
2123 lp->next = NULL;
2125 if (erase_list != NULL)
2127 tail->next = lp;
2128 tail = lp;
2130 else
2131 erase_list = tail = lp;
2133 else
2135 if (S_ISDIR (buf.st_mode))
2137 return_status = erase_dir_iff_empty (ctx, path);
2139 else
2140 return_status = erase_file (tctx, ctx, tmp_vpath);
2143 g_free (path);
2144 vfs_path_free (tmp_vpath);
2146 mc_closedir (reading);
2148 if (ctx->preserve)
2150 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2151 utb.actime = cbuf.st_atime;
2152 utb.modtime = cbuf.st_mtime;
2153 mc_utime (dest_dir_vpath, &utb);
2155 else
2157 cbuf.st_mode = umask (-1);
2158 umask (cbuf.st_mode);
2159 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2160 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2163 ret:
2164 g_free (dest_dir);
2165 vfs_path_free (dest_dir_vpath);
2166 g_free (parent_dirs);
2167 ret_fast:
2168 g_free (d);
2169 vfs_path_free (src_vpath);
2170 vfs_path_free (dst_vpath);
2171 return return_status;
2174 /* }}} */
2176 /* --------------------------------------------------------------------------------------------- */
2177 /* {{{ Move routines */
2179 FileProgressStatus
2180 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2182 struct stat sbuf, dbuf, destbuf;
2183 struct link *lp;
2184 char *destdir;
2185 FileProgressStatus return_status;
2186 gboolean move_over = FALSE;
2187 gboolean dstat_ok;
2188 vfs_path_t *src_vpath, *dst_vpath, *destdir_vpath;
2190 src_vpath = vfs_path_from_str (s);
2191 dst_vpath = vfs_path_from_str (d);
2193 file_progress_show_source (ctx, src_vpath);
2194 file_progress_show_target (ctx, dst_vpath);
2196 if (check_progress_buttons (ctx) == FILE_ABORT)
2198 return_status = FILE_ABORT;
2199 goto ret_fast;
2202 mc_refresh ();
2204 mc_stat (src_vpath, &sbuf);
2206 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2207 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2209 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2210 goto ret_fast;
2213 if (!dstat_ok)
2214 destdir = g_strdup (d); /* destination doesn't exist */
2215 else if (!ctx->dive_into_subdirs)
2217 destdir = g_strdup (d);
2218 move_over = TRUE;
2220 else
2221 destdir = mc_build_filename (d, x_basename (s), NULL);
2223 destdir_vpath = vfs_path_from_str (destdir);
2225 /* Check if the user inputted an existing dir */
2226 retry_dst_stat:
2227 if (mc_stat (destdir_vpath, &destbuf) == 0)
2229 if (move_over)
2231 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2233 if (return_status != FILE_CONT)
2234 goto ret;
2235 goto oktoret;
2237 else if (ctx->skip_all)
2238 return_status = FILE_SKIPALL;
2239 else
2241 if (S_ISDIR (destbuf.st_mode))
2242 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2243 else
2244 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2245 if (return_status == FILE_SKIPALL)
2246 ctx->skip_all = TRUE;
2247 if (return_status == FILE_RETRY)
2248 goto retry_dst_stat;
2251 g_free (destdir);
2252 vfs_path_free (destdir_vpath);
2253 goto ret_fast;
2256 retry_rename:
2257 if (mc_rename (src_vpath, destdir_vpath) == 0)
2259 return_status = FILE_CONT;
2260 goto ret;
2263 if (errno != EXDEV)
2265 if (!ctx->skip_all)
2267 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2268 if (return_status == FILE_SKIPALL)
2269 ctx->skip_all = TRUE;
2270 if (return_status == FILE_RETRY)
2271 goto retry_rename;
2273 goto ret;
2275 /* Failed because of filesystem boundary -> copy dir instead */
2276 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2278 if (return_status != FILE_CONT)
2279 goto ret;
2280 oktoret:
2281 file_progress_show_source (ctx, NULL);
2282 file_progress_show (ctx, 0, 0, "", FALSE);
2284 return_status = check_progress_buttons (ctx);
2285 if (return_status != FILE_CONT)
2286 goto ret;
2288 mc_refresh ();
2289 if (ctx->erase_at_end)
2291 for (; erase_list && return_status != FILE_ABORT;)
2293 if (S_ISDIR (erase_list->st_mode))
2295 char *src_path;
2297 src_path = vfs_path_to_str (erase_list->src_vpath);
2298 return_status = erase_dir_iff_empty (ctx, src_path);
2299 g_free (src_path);
2301 else
2302 return_status = erase_file (tctx, ctx, erase_list->src_vpath);
2303 lp = erase_list;
2304 erase_list = erase_list->next;
2305 g_free (lp);
2308 erase_dir_iff_empty (ctx, s);
2310 ret:
2311 g_free (destdir);
2312 vfs_path_free (destdir_vpath);
2313 while (erase_list)
2315 lp = erase_list;
2316 erase_list = erase_list->next;
2317 g_free (lp);
2319 ret_fast:
2320 vfs_path_free (src_vpath);
2321 vfs_path_free (dst_vpath);
2322 return return_status;
2325 /* }}} */
2327 /* --------------------------------------------------------------------------------------------- */
2328 /* {{{ Erase routines */
2330 FileProgressStatus
2331 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * s_vpath)
2333 FileProgressStatus error;
2334 char *s;
2336 s = vfs_path_to_str (s_vpath);
2339 if (strcmp (s, "..") == 0)
2340 return FILE_SKIP;
2342 if (strcmp (s, ".") == 0)
2343 return FILE_SKIP;
2346 file_progress_show_deleting (ctx, s);
2347 if (check_progress_buttons (ctx) == FILE_ABORT)
2349 g_free (s);
2350 return FILE_ABORT;
2352 mc_refresh ();
2354 /* The old way to detect a non empty directory was:
2355 error = my_rmdir (s);
2356 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2357 For the linux user space nfs server (nfs-server-2.2beta29-2)
2358 we would have to check also for EIO. I hope the new way is
2359 fool proof. (Norbert)
2361 error = check_dir_is_empty (s_vpath);
2362 if (error == 0)
2363 { /* not empty */
2364 error = query_recursive (ctx, s);
2365 if (error == FILE_CONT)
2366 error = recursive_erase (tctx, ctx, s);
2367 g_free (s);
2368 return error;
2371 while (my_rmdir (s) == -1 && !ctx->skip_all)
2373 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2374 if (error != FILE_RETRY)
2376 g_free (s);
2377 return error;
2381 g_free (s);
2382 return FILE_CONT;
2385 /* }}} */
2387 /* --------------------------------------------------------------------------------------------- */
2388 /* {{{ Panel operate routines */
2390 ComputeDirSizeUI *
2391 compute_dir_size_create_ui (void)
2393 ComputeDirSizeUI *ui;
2395 const char *b_name = N_("&Abort");
2397 #ifdef ENABLE_NLS
2398 b_name = _(b_name);
2399 #endif
2401 ui = g_new (ComputeDirSizeUI, 1);
2403 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
2404 NULL, _("Directory scanning"), DLG_CENTER);
2405 ui->dirname = label_new (3, 3, "");
2406 add_widget (ui->dlg, ui->dirname);
2408 add_widget (ui->dlg,
2409 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
2410 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2412 /* We will manage the dialog without any help,
2413 that's why we have to call init_dlg */
2414 init_dlg (ui->dlg);
2416 return ui;
2419 /* --------------------------------------------------------------------------------------------- */
2421 void
2422 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2424 if (ui != NULL)
2426 /* schedule to update passive panel */
2427 other_panel->dirty = 1;
2429 /* close and destroy dialog */
2430 dlg_run_done (ui->dlg);
2431 destroy_dlg (ui->dlg);
2432 g_free (ui);
2436 /* --------------------------------------------------------------------------------------------- */
2438 FileProgressStatus
2439 compute_dir_size_update_ui (const void *ui, const vfs_path_t * dirname_vpath)
2441 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2442 int c;
2443 Gpm_Event event;
2444 char *dirname;
2446 if (ui == NULL)
2447 return FILE_CONT;
2449 dirname = vfs_path_to_str (dirname_vpath);
2450 label_set_text (this->dirname, str_trunc (dirname, this->dlg->cols - 6));
2451 g_free (dirname);
2453 event.x = -1; /* Don't show the GPM cursor */
2454 c = tty_get_event (&event, FALSE, FALSE);
2455 if (c == EV_NONE)
2456 return FILE_CONT;
2458 /* Reinitialize to avoid old values after events other than
2459 selecting a button */
2460 this->dlg->ret_value = FILE_CONT;
2462 dlg_process_event (this->dlg, c, &event);
2464 switch (this->dlg->ret_value)
2466 case B_CANCEL:
2467 case FILE_ABORT:
2468 return FILE_ABORT;
2469 default:
2470 return FILE_CONT;
2474 /* --------------------------------------------------------------------------------------------- */
2476 * compute_dir_size:
2478 * Computes the number of bytes used by the files in a directory
2481 FileProgressStatus
2482 compute_dir_size (const vfs_path_t * dirname_vpath, const void *ui,
2483 compute_dir_size_callback cback,
2484 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2486 int res;
2487 struct stat s;
2488 DIR *dir;
2489 struct dirent *dirent;
2490 FileProgressStatus ret = FILE_CONT;
2492 if (!compute_symlinks)
2494 res = mc_lstat (dirname_vpath, &s);
2495 if (res != 0)
2496 return ret;
2498 /* don't scan symlink to directory */
2499 if (S_ISLNK (s.st_mode))
2501 (*ret_marked)++;
2502 *ret_total += (uintmax_t) s.st_size;
2503 return ret;
2507 dir = mc_opendir (dirname_vpath);
2509 if (dir == NULL)
2510 return ret;
2512 while ((dirent = mc_readdir (dir)) != NULL)
2514 vfs_path_t *tmp_vpath;
2516 ret = (cback != NULL) ? cback (ui, dirname_vpath) : FILE_CONT;
2518 if (ret != FILE_CONT)
2519 break;
2521 if (strcmp (dirent->d_name, ".") == 0)
2522 continue;
2523 if (strcmp (dirent->d_name, "..") == 0)
2524 continue;
2526 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, NULL);
2527 res = mc_lstat (tmp_vpath, &s);
2528 if (res == 0)
2530 if (S_ISDIR (s.st_mode))
2532 size_t subdir_count = 0;
2533 uintmax_t subdir_bytes = 0;
2535 ret =
2536 compute_dir_size (tmp_vpath, ui, cback, &subdir_count, &subdir_bytes,
2537 compute_symlinks);
2539 if (ret != FILE_CONT)
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 vfs_path_free (tmp_vpath);
2557 mc_closedir (dir);
2558 return ret;
2561 /* --------------------------------------------------------------------------------------------- */
2563 * panel_operate:
2565 * Performs one of the operations on the selection on the source_panel
2566 * (copy, delete, move).
2568 * Returns TRUE if did change the directory
2569 * structure, Returns FALSE if user aborted
2571 * force_single forces operation on the current entry and affects
2572 * default destination. Current filename is used as default.
2575 gboolean
2576 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2578 WPanel *panel = (WPanel *) source_panel;
2579 const gboolean single_entry = force_single || (panel->marked <= 1)
2580 || (get_current_type () == view_tree);
2582 char *source = NULL;
2583 #ifdef WITH_FULL_PATHS
2584 vfs_path_t *source_with_vpath = NULL;
2585 char *source_with_path_str = NULL;
2586 #else
2587 #define source_with_path source
2588 #endif /* !WITH_FULL_PATHS */
2589 char *dest = NULL;
2590 vfs_path_t *dest_vpath = NULL;
2591 char *temp = NULL;
2592 char *save_cwd = NULL, *save_dest = NULL;
2593 struct stat src_stat;
2594 gboolean ret_val = TRUE;
2595 int i;
2596 FileProgressStatus value;
2597 FileOpContext *ctx;
2598 FileOpTotalContext *tctx;
2599 vfs_path_t *tmp_vpath;
2601 gboolean do_bg = FALSE; /* do background operation? */
2603 static gboolean i18n_flag = FALSE;
2604 if (!i18n_flag)
2606 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2607 op_names[i] = Q_ (op_names[i]);
2608 i18n_flag = TRUE;
2611 free_linklist (&linklist);
2612 free_linklist (&dest_dirs);
2614 if (single_entry)
2616 vfs_path_t *source_vpath;
2618 if (force_single)
2619 source = g_strdup (selection (panel)->fname);
2620 else
2621 source = panel_get_file (panel);
2623 if (strcmp (source, "..") == 0)
2625 g_free (source);
2626 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2627 return FALSE;
2630 source_vpath = vfs_path_from_str (source);
2631 /* Update stat to get actual info */
2632 if (mc_lstat (source_vpath, &src_stat) != 0)
2634 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2635 path_trunc (source, 30), unix_error_string (errno));
2637 /* Directory was changed outside MC. Reload it forced */
2638 if (!panel->is_panelized)
2640 panel_update_flags_t flags = UP_RELOAD;
2642 /* don't update panelized panel */
2643 if (get_other_type () == view_listing && other_panel->is_panelized)
2644 flags |= UP_ONLY_CURRENT;
2646 update_panels (flags, UP_KEEPSEL);
2648 vfs_path_free (source_vpath);
2649 return FALSE;
2651 vfs_path_free (source_vpath);
2654 ctx = file_op_context_new (operation);
2656 /* Show confirmation dialog */
2657 if (operation != OP_DELETE)
2659 char *tmp_dest_dir, *dest_dir;
2660 char *format;
2662 /* Forced single operations default to the original name */
2663 if (force_single)
2664 tmp_dest_dir = g_strdup (source);
2665 else if (get_other_type () == view_listing)
2666 tmp_dest_dir = vfs_path_to_str (other_panel->cwd_vpath);
2667 else
2668 tmp_dest_dir = vfs_path_to_str (panel->cwd_vpath);
2670 * Add trailing backslash only when do non-local ops.
2671 * It saves user from occasional file renames (when destination
2672 * dir is deleted)
2674 if (!force_single && tmp_dest_dir[0] != '\0'
2675 && tmp_dest_dir[strlen (tmp_dest_dir) - 1] != PATH_SEP)
2677 /* add trailing separator */
2678 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
2679 g_free (tmp_dest_dir);
2681 else
2683 /* just copy */
2684 dest_dir = tmp_dest_dir;
2686 if (dest_dir == NULL)
2688 ret_val = FALSE;
2689 goto ret_fast;
2692 /* Generate confirmation prompt */
2693 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2695 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2696 source != NULL ? (void *) source
2697 : (void *) &panel->marked, dest_dir, &do_bg);
2699 g_free (format);
2700 g_free (dest_dir);
2702 if (dest == NULL || dest[0] == '\0')
2704 g_free (dest);
2705 ret_val = FALSE;
2706 goto ret_fast;
2708 dest_vpath = vfs_path_from_str (dest);
2710 else if (confirm_delete)
2712 char *format;
2713 char fmd_buf[BUF_MEDIUM];
2715 /* Generate confirmation prompt */
2716 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2718 if (source == NULL)
2719 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2720 else
2722 const int fmd_xlen = 64;
2723 i = fmd_xlen - str_term_width1 (format) - 4;
2724 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2727 g_free (format);
2729 if (safe_delete)
2730 query_set_sel (1);
2732 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2734 if (i != 0)
2736 ret_val = FALSE;
2737 goto ret_fast;
2741 tctx = file_op_total_context_new ();
2742 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2745 filegui_dialog_type_t dialog_type;
2747 if (operation == OP_DELETE)
2748 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2749 else
2751 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2752 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2754 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2755 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2758 /* Background also need ctx->ui, but not full */
2759 if (do_bg)
2760 file_op_context_create_ui_without_init (ctx, TRUE, dialog_type);
2761 else
2762 file_op_context_create_ui (ctx, TRUE, dialog_type);
2765 #ifdef ENABLE_BACKGROUND
2766 /* Did the user select to do a background operation? */
2767 if (do_bg)
2769 int v;
2770 char *cwd_str;
2772 cwd_str = vfs_path_to_str (panel->cwd_vpath);
2773 v = do_background (ctx, g_strconcat (op_names[operation], ": ", cwd_str, (char *) NULL));
2774 g_free (cwd_str);
2775 if (v == -1)
2776 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2778 /* If we are the parent */
2779 if (v == 1)
2781 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
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 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
2800 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2801 save_cwd = vfs_path_to_str (panel->cwd_vpath);
2803 /* Now, let's do the job */
2805 /* This code is only called by the tree and panel code */
2806 if (single_entry)
2808 /* We now have ETA in all cases */
2810 /* One file: FIXME mc_chdir will take user out of any vfs */
2811 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2813 vfs_path_t *vpath;
2814 int chdir_retcode;
2816 vpath = vfs_path_from_str (PATH_SEP_STR);
2817 chdir_retcode = mc_chdir (vpath);
2818 vfs_path_free (vpath);
2819 if (chdir_retcode < 0)
2821 ret_val = FALSE;
2822 goto clean_up;
2826 /* The source and src_stat variables have been initialized before */
2827 #ifdef WITH_FULL_PATHS
2828 if (g_path_is_absolute (source))
2829 source_with_vpath = vfs_path_from_str (source);
2830 else
2831 source_with_vpath = vfs_path_append_new (panel->cwd_vpath, source, (char *) NULL);
2832 source_with_path_str = vfs_path_to_str (source_with_vpath);
2833 #endif /* WITH_FULL_PATHS */
2834 if (panel_operate_init_totals (operation, panel, source_with_path_str, ctx) == FILE_CONT)
2836 if (operation == OP_DELETE)
2838 if (S_ISDIR (src_stat.st_mode))
2839 value = erase_dir (tctx, ctx, source_with_vpath);
2840 else
2841 value = erase_file (tctx, ctx, source_with_vpath);
2843 else
2845 temp = transform_source (ctx, source_with_path_str);
2846 if (temp == NULL)
2847 value = transform_error;
2848 else
2850 char *repl_dest, *temp2;
2852 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2853 temp2 = mc_build_filename (repl_dest, temp, NULL);
2854 g_free (temp);
2855 g_free (repl_dest);
2856 g_free (dest);
2857 vfs_path_free (dest_vpath);
2858 dest = temp2;
2859 dest_vpath = vfs_path_from_str (dest);
2861 switch (operation)
2863 case OP_COPY:
2864 /* we use file_mask_op_follow_links only with OP_COPY */
2865 ctx->stat_func (source_with_vpath, &src_stat);
2867 if (S_ISDIR (src_stat.st_mode))
2868 value = copy_dir_dir (tctx, ctx, source_with_path_str, dest,
2869 TRUE, FALSE, FALSE, NULL);
2870 else
2871 value = copy_file_file (tctx, ctx, source_with_path_str, dest);
2872 break;
2874 case OP_MOVE:
2875 if (S_ISDIR (src_stat.st_mode))
2876 value = move_dir_dir (tctx, ctx, source_with_path_str, dest);
2877 else
2878 value = move_file_file (tctx, ctx, source_with_path_str, dest);
2879 break;
2881 default:
2882 /* Unknown file operation */
2883 abort ();
2886 } /* Copy or move operation */
2888 if ((value == FILE_CONT) && !force_single)
2889 unmark_files (panel);
2892 else
2894 /* Many files */
2896 /* Check destination for copy or move operation */
2897 while (operation != OP_DELETE)
2899 int dst_result;
2900 struct stat dst_stat;
2902 dst_result = mc_stat (dest_vpath, &dst_stat);
2904 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2905 break;
2907 if (ctx->skip_all
2908 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2909 goto clean_up;
2912 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2914 /* Loop for every file, perform the actual copy operation */
2915 for (i = 0; i < panel->count; i++)
2917 const char *source2;
2919 if (!panel->dir.list[i].f.marked)
2920 continue; /* Skip the unmarked ones */
2922 source2 = panel->dir.list[i].fname;
2923 src_stat = panel->dir.list[i].st;
2925 #ifdef WITH_FULL_PATHS
2926 g_free (source_with_path_str);
2927 vfs_path_free (source_with_vpath);
2928 if (g_path_is_absolute (source2))
2929 source_with_vpath = vfs_path_from_str (source2);
2930 else
2931 source_with_vpath =
2932 vfs_path_append_new (panel->cwd_vpath, source2, (char *) NULL);
2933 source_with_path_str = vfs_path_to_str (source_with_vpath);
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_vpath);
2940 else
2941 value = erase_file (tctx, ctx, source_with_vpath);
2943 else
2945 temp = transform_source (ctx, source_with_path_str);
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 = mc_build_filename (repl_dest, temp, NULL);
2955 g_free (temp);
2956 g_free (repl_dest);
2957 temp3 = source_with_path_str;
2958 source_with_path_str = strutils_shell_unescape (source_with_path_str);
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_str);
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_str, temp2,
2977 TRUE, FALSE, FALSE, NULL);
2978 else
2979 value = copy_file_file (tctx, ctx, source_with_path_str, 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_str, temp2);
2986 else
2987 value = move_file_file (tctx, ctx, source_with_path_str, 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_str);
3044 vfs_path_free (source_with_vpath);
3045 #endif /* WITH_FULL_PATHS */
3046 g_free (dest);
3047 vfs_path_free (dest_vpath);
3048 g_free (ctx->dest_mask);
3049 ctx->dest_mask = NULL;
3051 #ifdef ENABLE_BACKGROUND
3052 /* Let our parent know we are saying bye bye */
3053 if (mc_global.we_are_background)
3055 int cur_pid = getpid ();
3056 /* Send pid to parent with child context, it is fork and
3057 don't modify real parent ctx */
3058 ctx->pid = cur_pid;
3059 parent_call ((void *) end_bg_process, ctx, 0);
3061 vfs_shut ();
3062 _exit (0);
3064 #endif /* ENABLE_BACKGROUND */
3066 file_op_total_context_destroy (tctx);
3067 ret_fast:
3068 file_op_context_destroy (ctx);
3069 g_free (source);
3071 return ret_val;
3074 /* }}} */
3076 /* --------------------------------------------------------------------------------------------- */
3077 /* {{{ Query/status report routines */
3078 /** Report error with one file */
3079 FileProgressStatus
3080 file_error (const char *format, const char *file)
3082 char buf[BUF_MEDIUM];
3084 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3086 return do_file_error (buf);
3089 /* --------------------------------------------------------------------------------------------- */
3092 Cause emacs to enter folding mode for this file:
3093 Local variables:
3094 end: