changed interface of function mc_chdir() for handle vfs_path_t object as parameter
[midnight-commander.git] / src / filemanager / file.c
blob474c2338f81472298dc899848dde792de630dfb6
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;
299 vpath = vfs_path_from_str (lp->name);
300 lp_name_class = vfs_path_get_by_index (vpath, -1)->class;
301 vfs_path_free (vpath);
303 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
304 && link_stat.st_dev == dev && lp_name_class == my_vfs)
306 struct vfs_class *p_class, *dst_name_class;
308 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
309 was copied to */
311 vpath = vfs_path_from_str (p);
312 p_class = vfs_path_get_by_index (vpath, -1)->class;
313 vfs_path_free (vpath);
315 vpath = vfs_path_from_str (dst_name);
316 dst_name_class = vfs_path_get_by_index (vpath, -1)->class;
317 vfs_path_free (vpath);
319 if (dst_name_class == p_class)
321 if (!mc_stat (p, &link_stat))
323 if (!mc_link (p, dst_name))
324 return TRUE;
328 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
329 return FALSE;
331 lp = (struct link *) g_try_malloc (sizeof (struct link) + strlen (src_name)
332 + strlen (dst_name) + 1);
333 if (lp)
335 char *lpdstname;
336 lp->vfs = my_vfs;
337 lp->ino = ino;
338 lp->dev = dev;
339 strcpy (lp->name, src_name);
340 lpdstname = lp->name + strlen (lp->name) + 1;
341 strcpy (lpdstname, dst_name);
342 lp->next = linklist;
343 linklist = lp;
345 return FALSE;
348 /* --------------------------------------------------------------------------------------------- */
350 * Duplicate the contents of the symbolic link src_path in dst_path.
351 * Try to make a stable symlink if the option "stable symlink" was
352 * set in the file mask dialog.
353 * If dst_path is an existing symlink it will be deleted silently
354 * (upper levels take already care of existing files at dst_path).
357 static FileProgressStatus
358 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
360 char link_target[MC_MAXPATHLEN];
361 int len;
362 FileProgressStatus return_status;
363 struct stat sb;
364 gboolean dst_is_symlink;
366 dst_is_symlink = (mc_lstat (dst_path, &sb) == 0) && S_ISLNK (sb.st_mode);
368 retry_src_readlink:
369 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
370 if (len < 0)
372 if (ctx->skip_all)
373 return_status = FILE_SKIPALL;
374 else
376 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
377 if (return_status == FILE_SKIPALL)
378 ctx->skip_all = TRUE;
379 if (return_status == FILE_RETRY)
380 goto retry_src_readlink;
382 return return_status;
384 link_target[len] = 0;
386 if (ctx->stable_symlinks)
388 vfs_path_t *vpath1 = vfs_path_from_str (src_path);
389 vfs_path_t *vpath2 = vfs_path_from_str (dst_path);
391 if (!vfs_file_is_local (vpath1) || !vfs_file_is_local (vpath2))
393 message (D_ERROR, MSG_ERROR,
394 _("Cannot make stable symlinks across"
395 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
396 ctx->stable_symlinks = FALSE;
398 vfs_path_free (vpath1);
399 vfs_path_free (vpath2);
402 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
404 char *p, *q, *s;
406 const char *r = strrchr (src_path, PATH_SEP);
408 if (r)
410 p = g_strndup (src_path, r - src_path + 1);
411 if (g_path_is_absolute (dst_path))
412 q = g_strdup (dst_path);
413 else
414 q = g_strconcat (p, dst_path, (char *) NULL);
415 s = strrchr (q, PATH_SEP);
416 if (s)
418 s[1] = 0;
419 s = g_strconcat (p, link_target, (char *) NULL);
420 g_free (p);
421 g_strlcpy (link_target, s, sizeof (link_target));
422 g_free (s);
423 s = diff_two_paths (q, link_target);
424 if (s)
426 g_strlcpy (link_target, s, sizeof (link_target));
427 g_free (s);
430 else
431 g_free (p);
432 g_free (q);
435 retry_dst_symlink:
436 if (mc_symlink (link_target, dst_path) == 0)
437 /* Success */
438 return FILE_CONT;
440 * if dst_exists, it is obvious that this had failed.
441 * We can delete the old symlink and try again...
443 if (dst_is_symlink)
445 if (!mc_unlink (dst_path))
446 if (mc_symlink (link_target, dst_path) == 0)
447 /* Success */
448 return FILE_CONT;
450 if (ctx->skip_all)
451 return_status = FILE_SKIPALL;
452 else
454 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
455 if (return_status == FILE_SKIPALL)
456 ctx->skip_all = TRUE;
457 if (return_status == FILE_RETRY)
458 goto retry_dst_symlink;
460 return return_status;
463 /* --------------------------------------------------------------------------------------------- */
465 static FileProgressStatus
466 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add)
468 struct timeval tv_current;
469 static struct timeval tv_start = { };
471 tctx->progress_count++;
472 tctx->progress_bytes += (uintmax_t) add;
474 if (tv_start.tv_sec == 0)
476 gettimeofday (&tv_start, (struct timezone *) NULL);
478 gettimeofday (&tv_current, (struct timezone *) NULL);
479 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
481 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
483 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
484 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
486 tv_start.tv_sec = tv_current.tv_sec;
489 return check_progress_buttons (ctx);
492 /* --------------------------------------------------------------------------------------------- */
494 static FileProgressStatus
495 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
497 char *msg;
498 int result = 0;
499 const char *head_msg;
501 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
503 msg = g_strdup_printf (fmt, a, b);
504 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
505 g_free (msg);
506 do_refresh ();
508 return (result == 1) ? FILE_ABORT : FILE_SKIP;
511 /* --------------------------------------------------------------------------------------------- */
513 static FileProgressStatus
514 warn_same_file (const char *fmt, const char *a, const char *b)
516 #ifdef ENABLE_BACKGROUND
517 /* *INDENT-OFF* */
518 union
520 void *p;
521 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
522 const char *a, const char *b);
523 } pntr;
524 /* *INDENT-ON* */
526 pntr.f = real_warn_same_file;
528 if (mc_global.we_are_background)
529 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
530 #endif
531 return real_warn_same_file (Foreground, fmt, a, b);
534 /* --------------------------------------------------------------------------------------------- */
535 /* {{{ Query/status report routines */
537 static FileProgressStatus
538 real_do_file_error (enum OperationMode mode, const char *error)
540 int result;
541 const char *msg;
543 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
544 result =
545 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
547 switch (result)
549 case 0:
550 do_refresh ();
551 return FILE_SKIP;
553 case 1:
554 do_refresh ();
555 return FILE_SKIPALL;
557 case 2:
558 do_refresh ();
559 return FILE_RETRY;
561 case 3:
562 default:
563 return FILE_ABORT;
567 /* --------------------------------------------------------------------------------------------- */
569 static FileProgressStatus
570 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
572 gchar *text;
574 if (ctx->recursive_result < RECURSIVE_ALWAYS)
576 const char *msg = mode == Foreground
577 ? _("\nDirectory not empty.\nDelete it recursively?")
578 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
579 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
581 if (safe_delete)
582 query_set_sel (1);
584 ctx->recursive_result =
585 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
586 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
588 if (ctx->recursive_result != RECURSIVE_ABORT)
589 do_refresh ();
590 g_free (text);
593 switch (ctx->recursive_result)
595 case RECURSIVE_YES:
596 case RECURSIVE_ALWAYS:
597 return FILE_CONT;
599 case RECURSIVE_NO:
600 case RECURSIVE_NEVER:
601 return FILE_SKIP;
603 case RECURSIVE_ABORT:
604 default:
605 return FILE_ABORT;
609 /* --------------------------------------------------------------------------------------------- */
611 #ifdef ENABLE_BACKGROUND
612 static FileProgressStatus
613 do_file_error (const char *str)
615 union
617 void *p;
618 FileProgressStatus (*f) (enum OperationMode, const char *);
619 } pntr;
620 pntr.f = real_do_file_error;
622 if (mc_global.we_are_background)
623 return parent_call (pntr.p, NULL, 1, strlen (str), str);
624 else
625 return real_do_file_error (Foreground, str);
628 /* --------------------------------------------------------------------------------------------- */
630 static FileProgressStatus
631 query_recursive (FileOpContext * ctx, const char *s)
633 union
635 void *p;
636 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
637 } pntr;
638 pntr.f = real_query_recursive;
640 if (mc_global.we_are_background)
641 return parent_call (pntr.p, ctx, 1, strlen (s), s);
642 else
643 return real_query_recursive (ctx, Foreground, s);
646 /* --------------------------------------------------------------------------------------------- */
648 static FileProgressStatus
649 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
650 struct stat *_d_stat)
652 union
654 void *p;
655 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
656 struct stat *, struct stat *);
657 } pntr;
658 pntr.f = file_progress_real_query_replace;
660 if (mc_global.we_are_background)
661 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
662 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
663 else
664 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
667 #else
668 /* --------------------------------------------------------------------------------------------- */
670 static FileProgressStatus
671 do_file_error (const char *str)
673 return real_do_file_error (Foreground, str);
676 /* --------------------------------------------------------------------------------------------- */
678 static FileProgressStatus
679 query_recursive (FileOpContext * ctx, const char *s)
681 return real_query_recursive (ctx, Foreground, s);
684 /* --------------------------------------------------------------------------------------------- */
686 static FileProgressStatus
687 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
688 struct stat *_d_stat)
690 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
693 #endif /* !ENABLE_BACKGROUND */
695 /* --------------------------------------------------------------------------------------------- */
696 /** Report error with two files */
698 static FileProgressStatus
699 files_error (const char *format, const char *file1, const char *file2)
701 char buf[BUF_MEDIUM];
702 char *nfile1 = g_strdup (path_trunc (file1, 15));
703 char *nfile2 = g_strdup (path_trunc (file2, 15));
705 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
707 g_free (nfile1);
708 g_free (nfile2);
710 return do_file_error (buf);
713 /* }}} */
715 /* --------------------------------------------------------------------------------------------- */
717 static void
718 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
719 struct timeval tv_current, struct timeval tv_transfer_start,
720 off_t file_size, off_t n_read_total)
722 long dt;
724 /* 1. Update rotating dash after some time */
725 rotate_dash ();
727 /* 3. Compute ETA */
728 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
730 if (n_read_total == 0)
731 ctx->eta_secs = 0.0;
732 else
734 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
735 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
738 /* 4. Compute BPS rate */
739 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
740 if (ctx->bps_time < 1)
741 ctx->bps_time = 1;
742 ctx->bps = n_read_total / ctx->bps_time;
744 /* 5. Compute total ETA and BPS */
745 if (ctx->progress_bytes != 0)
747 uintmax_t remain_bytes;
749 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
750 #if 1
752 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
754 if (total_secs < 1)
755 total_secs = 1;
757 tctx->bps = tctx->copied_bytes / total_secs;
758 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
760 #else
761 /* broken on lot of little files */
762 tctx->bps_count++;
763 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
764 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
765 #endif
769 /* --------------------------------------------------------------------------------------------- */
771 /* {{{ Move routines */
772 static FileProgressStatus
773 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
775 struct stat src_stats, dst_stats;
776 FileProgressStatus return_status = FILE_CONT;
777 gboolean copy_done = FALSE;
778 gboolean old_ask_overwrite;
780 file_progress_show_source (ctx, s);
781 file_progress_show_target (ctx, d);
782 if (check_progress_buttons (ctx) == FILE_ABORT)
783 return FILE_ABORT;
785 mc_refresh ();
787 while (mc_lstat (s, &src_stats) != 0)
789 /* Source doesn't exist */
790 if (ctx->skip_all)
791 return_status = FILE_SKIPALL;
792 else
794 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
795 if (return_status == FILE_SKIPALL)
796 ctx->skip_all = TRUE;
798 if (return_status != FILE_RETRY)
799 return return_status;
802 if (mc_lstat (d, &dst_stats) == 0)
804 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
805 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
807 if (S_ISDIR (dst_stats.st_mode))
809 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
810 do_refresh ();
811 return FILE_SKIP;
814 if (confirm_overwrite)
816 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
817 if (return_status != FILE_CONT)
818 return return_status;
820 /* Ok to overwrite */
823 if (!ctx->do_append)
825 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
827 return_status = make_symlink (ctx, s, d);
828 if (return_status == FILE_CONT)
829 goto retry_src_remove;
830 else
831 return return_status;
834 if (mc_rename (s, d) == 0)
835 return progress_update_one (tctx, ctx, src_stats.st_size);
837 #if 0
838 /* Comparison to EXDEV seems not to work in nfs if you're moving from
839 one nfs to the same, but on the server it is on two different
840 filesystems. Then nfs returns EIO instead of EXDEV.
841 Hope it will not hurt if we always in case of error try to copy/delete. */
842 else
843 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
845 if (errno != EXDEV)
847 if (ctx->skip_all)
848 return_status = FILE_SKIPALL;
849 else
851 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
852 if (return_status == FILE_SKIPALL)
853 ctx->skip_all = TRUE;
854 if (return_status == FILE_RETRY)
855 goto retry_rename;
857 return return_status;
859 #endif
861 /* Failed because filesystem boundary -> copy the file instead */
862 old_ask_overwrite = tctx->ask_overwrite;
863 tctx->ask_overwrite = FALSE;
864 return_status = copy_file_file (tctx, ctx, s, d);
865 tctx->ask_overwrite = old_ask_overwrite;
866 if (return_status != FILE_CONT)
867 return return_status;
869 copy_done = TRUE;
871 file_progress_show_source (ctx, NULL);
872 file_progress_show (ctx, 0, 0, "", FALSE);
874 return_status = check_progress_buttons (ctx);
875 if (return_status != FILE_CONT)
876 return return_status;
878 mc_refresh ();
880 retry_src_remove:
881 if (mc_unlink (s) != 0 && !ctx->skip_all)
883 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
884 if (return_status == FILE_RETRY)
885 goto retry_src_remove;
886 if (return_status == FILE_SKIPALL)
887 ctx->skip_all = TRUE;
888 return return_status;
891 if (!copy_done)
892 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
894 return return_status;
897 /* }}} */
899 /* --------------------------------------------------------------------------------------------- */
900 /* {{{ Erase routines */
901 /** Don't update progress status if progress_count==NULL */
903 static FileProgressStatus
904 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
906 int return_status;
907 struct stat buf;
909 file_progress_show_deleting (ctx, s);
910 if (check_progress_buttons (ctx) == FILE_ABORT)
911 return FILE_ABORT;
912 mc_refresh ();
914 if (tctx->progress_count != 0 && mc_lstat (s, &buf) != 0)
916 /* ignore, most likely the mc_unlink fails, too */
917 buf.st_size = 0;
920 while (mc_unlink (s) != 0 && !ctx->skip_all)
922 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
923 if (return_status == FILE_ABORT)
924 return return_status;
925 if (return_status == FILE_RETRY)
926 continue;
927 if (return_status == FILE_SKIPALL)
928 ctx->skip_all = TRUE;
929 break;
932 if (tctx->progress_count == 0)
933 return FILE_CONT;
934 return progress_update_one (tctx, ctx, buf.st_size);
937 /* --------------------------------------------------------------------------------------------- */
940 Recursive remove of files
941 abort->cancel stack
942 skip ->warn every level, gets default
943 skipall->remove as much as possible
945 static FileProgressStatus
946 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
948 struct dirent *next;
949 struct stat buf;
950 DIR *reading;
951 char *path;
952 FileProgressStatus return_status = FILE_CONT;
954 if (!strcmp (s, ".."))
955 return FILE_RETRY;
957 reading = mc_opendir (s);
959 if (!reading)
960 return FILE_RETRY;
962 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
964 if (!strcmp (next->d_name, "."))
965 continue;
966 if (!strcmp (next->d_name, ".."))
967 continue;
968 path = concat_dir_and_file (s, next->d_name);
969 if (mc_lstat (path, &buf))
971 g_free (path);
972 mc_closedir (reading);
973 return FILE_RETRY;
975 if (S_ISDIR (buf.st_mode))
976 return_status = recursive_erase (tctx, ctx, path);
977 else
978 return_status = erase_file (tctx, ctx, path);
979 g_free (path);
981 mc_closedir (reading);
982 if (return_status == FILE_ABORT)
983 return return_status;
984 file_progress_show_deleting (ctx, s);
985 if (check_progress_buttons (ctx) == FILE_ABORT)
986 return FILE_ABORT;
987 mc_refresh ();
989 while (my_rmdir (s) != 0 && !ctx->skip_all)
991 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
992 if (return_status == FILE_RETRY)
993 continue;
994 if (return_status == FILE_ABORT)
995 return return_status;
996 if (return_status == FILE_SKIPALL)
997 ctx->skip_all = TRUE;
998 break;
1001 return FILE_CONT;
1004 /* --------------------------------------------------------------------------------------------- */
1005 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1006 in the directory path points to, 0 else. */
1008 static int
1009 check_dir_is_empty (const char *path)
1011 DIR *dir;
1012 struct dirent *d;
1013 int i;
1015 dir = mc_opendir (path);
1016 if (!dir)
1017 return -1;
1019 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1021 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1022 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1023 continue; /* "." or ".." */
1024 i = 0;
1025 break;
1028 mc_closedir (dir);
1029 return i;
1032 /* --------------------------------------------------------------------------------------------- */
1034 static FileProgressStatus
1035 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1037 FileProgressStatus error;
1039 if (strcmp (s, "..") == 0)
1040 return FILE_SKIP;
1042 if (strcmp (s, ".") == 0)
1043 return FILE_SKIP;
1045 file_progress_show_deleting (ctx, s);
1046 if (check_progress_buttons (ctx) == FILE_ABORT)
1047 return FILE_ABORT;
1048 mc_refresh ();
1050 if (1 != check_dir_is_empty (s)) /* not empty or error */
1051 return FILE_CONT;
1053 while (my_rmdir (s) != 0 && !ctx->skip_all)
1055 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1056 if (error == FILE_SKIPALL)
1057 ctx->skip_all = TRUE;
1058 if (error != FILE_RETRY)
1059 return error;
1062 return FILE_CONT;
1065 /* }}} */
1067 /* --------------------------------------------------------------------------------------------- */
1068 /* {{{ Panel operate routines */
1071 * Return currently selected entry name or the name of the first marked
1072 * entry if there is one.
1075 static char *
1076 panel_get_file (WPanel * panel)
1078 if (get_current_type () == view_tree)
1080 WTree *tree;
1082 tree = (WTree *) get_panel_widget (get_current_index ());
1083 return tree_selected_name (tree);
1086 if (panel->marked != 0)
1088 int i;
1090 for (i = 0; i < panel->count; i++)
1091 if (panel->dir.list[i].f.marked)
1092 return panel->dir.list[i].fname;
1095 return panel->dir.list[panel->selected].fname;
1098 /* --------------------------------------------------------------------------------------------- */
1100 * panel_compute_totals:
1102 * compute the number of files and the number of bytes
1103 * used up by the whole selection, recursing directories
1104 * as required. In addition, it checks to see if it will
1105 * overwrite any files by doing the copy.
1108 static FileProgressStatus
1109 panel_compute_totals (const WPanel * panel, const void *ui,
1110 compute_dir_size_callback cback,
1111 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1113 int i;
1115 *ret_marked = 0;
1116 *ret_total = 0;
1118 for (i = 0; i < panel->count; i++)
1120 struct stat *s;
1122 if (!panel->dir.list[i].f.marked)
1123 continue;
1125 s = &panel->dir.list[i].st;
1127 if (S_ISDIR (s->st_mode))
1129 char *dir_name;
1130 size_t subdir_count = 0;
1131 uintmax_t subdir_bytes = 0;
1132 FileProgressStatus status;
1134 dir_name = concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1136 status = compute_dir_size (dir_name, ui, cback,
1137 &subdir_count, &subdir_bytes, compute_symlinks);
1138 g_free (dir_name);
1140 if (status != FILE_CONT)
1141 return FILE_ABORT;
1143 *ret_marked += subdir_count;
1144 *ret_total += subdir_bytes;
1146 else
1148 (*ret_marked)++;
1149 *ret_total += (uintmax_t) s->st_size;
1153 return FILE_CONT;
1156 /* --------------------------------------------------------------------------------------------- */
1158 /** Initialize variables for progress bars */
1159 static FileProgressStatus
1160 panel_operate_init_totals (FileOperation operation,
1161 const WPanel * panel, const char *source, FileOpContext * ctx)
1163 FileProgressStatus status;
1165 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1167 ComputeDirSizeUI *ui;
1169 ui = compute_dir_size_create_ui ();
1171 if (source != NULL)
1172 status = compute_dir_size (source, ui, compute_dir_size_update_ui,
1173 &ctx->progress_count, &ctx->progress_bytes,
1174 ctx->follow_links);
1175 else
1176 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1177 &ctx->progress_count, &ctx->progress_bytes,
1178 ctx->follow_links);
1180 compute_dir_size_destroy_ui (ui);
1182 ctx->progress_totals_computed = (status == FILE_CONT);
1184 else
1186 status = FILE_CONT;
1187 ctx->progress_count = panel->marked;
1188 ctx->progress_bytes = panel->total;
1189 ctx->progress_totals_computed = FALSE;
1192 return status;
1195 /* --------------------------------------------------------------------------------------------- */
1197 * Generate user prompt for panel operation.
1198 * single_source is the name if the source entry or NULL for multiple
1199 * entries.
1200 * src_stat is only used when single_source is not NULL.
1203 static char *
1204 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1205 gboolean single_source, const struct stat *src_stat)
1207 const char *sp, *cp;
1208 char format_string[BUF_MEDIUM];
1209 char *dp = format_string;
1210 gboolean build_question = FALSE;
1212 static gboolean i18n_flag = FALSE;
1213 if (!i18n_flag)
1215 size_t i;
1217 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1218 op_names1[i] = Q_ (op_names1[i]);
1220 #ifdef ENABLE_NLS
1221 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1222 prompt_parts[i] = _(prompt_parts[i]);
1224 one_format = _(one_format);
1225 many_format = _(many_format);
1226 question_format = _(question_format);
1227 #endif /* ENABLE_NLS */
1228 i18n_flag = TRUE;
1231 sp = single_source ? one_format : many_format;
1233 while (*sp != '\0')
1235 switch (*sp)
1237 case '%':
1238 cp = NULL;
1239 switch (sp[1])
1241 case 'o':
1242 cp = op_names1[operation];
1243 break;
1244 case 'm':
1245 if (operation == OP_DELETE)
1247 cp = "";
1248 build_question = TRUE;
1250 else
1251 cp = prompt_parts[5];
1252 break;
1253 case 'e':
1254 if (operation == OP_DELETE)
1256 cp = "";
1257 build_question = TRUE;
1259 else
1260 cp = prompt_parts[6];
1261 break;
1262 case 'f':
1263 if (single_source)
1264 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1265 else
1266 cp = (panel->marked == panel->dirs_marked)
1267 ? prompt_parts[3]
1268 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1269 break;
1270 default:
1271 *dp++ = *sp++;
1274 if (cp != NULL)
1276 sp += 2;
1277 while (*cp != '\0')
1278 *dp++ = *cp++;
1280 break;
1281 default:
1282 *dp++ = *sp++;
1285 *dp = '\0';
1287 if (build_question)
1289 char tmp[BUF_MEDIUM];
1291 memmove (tmp, format_string, sizeof (tmp));
1292 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1295 return g_strdup (format_string);
1298 /* --------------------------------------------------------------------------------------------- */
1300 #ifdef ENABLE_BACKGROUND
1301 static int
1302 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1304 int pid = ctx->pid;
1306 (void) mode;
1307 ctx->pid = 0;
1309 unregister_task_with_pid (pid);
1310 /* file_op_context_destroy(ctx); */
1311 return 1;
1313 #endif
1314 /* }}} */
1316 /* --------------------------------------------------------------------------------------------- */
1317 /*** public functions ****************************************************************************/
1318 /* --------------------------------------------------------------------------------------------- */
1320 FileProgressStatus
1321 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1322 const char *src_path, const char *dst_path)
1324 uid_t src_uid = (uid_t) - 1;
1325 gid_t src_gid = (gid_t) - 1;
1327 int src_desc, dest_desc = -1;
1328 int n_read, n_written;
1329 mode_t src_mode = 0; /* The mode of the source file */
1330 struct stat sb, sb2;
1331 struct utimbuf utb;
1332 gboolean dst_exists = FALSE, appending = FALSE;
1333 off_t file_size = -1;
1334 FileProgressStatus return_status, temp_status;
1335 struct timeval tv_transfer_start;
1336 dest_status_t dst_status = DEST_NONE;
1337 int open_flags;
1338 gboolean is_first_time = TRUE;
1340 /* FIXME: We should not be using global variables! */
1341 ctx->do_reget = 0;
1342 return_status = FILE_RETRY;
1344 file_progress_show_source (ctx, src_path);
1345 file_progress_show_target (ctx, dst_path);
1346 if (check_progress_buttons (ctx) == FILE_ABORT)
1347 return FILE_ABORT;
1349 mc_refresh ();
1351 while (mc_stat (dst_path, &sb2) == 0)
1353 if (S_ISDIR (sb2.st_mode))
1355 if (ctx->skip_all)
1356 return_status = FILE_SKIPALL;
1357 else
1359 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1360 if (return_status == FILE_SKIPALL)
1361 ctx->skip_all = TRUE;
1362 if (return_status == FILE_RETRY)
1363 continue;
1365 return return_status;
1367 dst_exists = TRUE;
1368 break;
1371 while ((*ctx->stat_func) (src_path, &sb) != 0)
1373 if (ctx->skip_all)
1374 return_status = FILE_SKIPALL;
1375 else
1377 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1378 if (return_status == FILE_SKIPALL)
1379 ctx->skip_all = TRUE;
1381 if (return_status != FILE_RETRY)
1382 return return_status;
1385 if (dst_exists)
1387 /* Destination already exists */
1388 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1389 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), src_path, dst_path);
1390 /* Should we replace destination? */
1391 if (tctx->ask_overwrite)
1393 ctx->do_reget = 0;
1394 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1395 if (return_status != FILE_CONT)
1396 return return_status;
1400 if (!ctx->do_append)
1402 /* Check the hardlinks */
1403 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb))
1405 /* We have made a hardlink - no more processing is necessary */
1406 return FILE_CONT;
1409 if (S_ISLNK (sb.st_mode))
1410 return make_symlink (ctx, src_path, dst_path);
1412 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1413 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1415 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1416 && !ctx->skip_all)
1418 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1419 if (return_status == FILE_RETRY)
1420 continue;
1421 if (return_status == FILE_SKIPALL)
1422 ctx->skip_all = TRUE;
1423 return return_status;
1425 /* Success */
1427 while (ctx->preserve_uidgid && mc_chown (dst_path, sb.st_uid, sb.st_gid) != 0
1428 && !ctx->skip_all)
1430 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1431 if (temp_status == FILE_SKIP)
1432 break;
1433 if (temp_status == FILE_SKIPALL)
1434 ctx->skip_all = TRUE;
1435 if (temp_status != FILE_RETRY)
1436 return temp_status;
1439 while (ctx->preserve && mc_chmod (dst_path, sb.st_mode & ctx->umask_kill) != 0
1440 && !ctx->skip_all)
1442 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1443 if (temp_status == FILE_SKIP)
1444 break;
1445 if (temp_status == FILE_SKIPALL)
1446 ctx->skip_all = TRUE;
1447 if (temp_status != FILE_RETRY)
1448 return temp_status;
1451 return FILE_CONT;
1455 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1457 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1459 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1460 if (return_status == FILE_RETRY)
1461 continue;
1462 if (return_status == FILE_SKIPALL)
1463 ctx->skip_all = TRUE;
1464 if (return_status == FILE_SKIP)
1465 break;
1466 ctx->do_append = 0;
1467 return return_status;
1470 if (ctx->do_reget != 0)
1472 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1474 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1475 ctx->do_reget = 0;
1476 ctx->do_append = FALSE;
1480 while (mc_fstat (src_desc, &sb) != 0)
1482 if (ctx->skip_all)
1483 return_status = FILE_SKIPALL;
1484 else
1486 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1487 if (return_status == FILE_RETRY)
1488 continue;
1489 if (return_status == FILE_SKIPALL)
1490 ctx->skip_all = TRUE;
1491 ctx->do_append = FALSE;
1493 goto ret;
1495 src_mode = sb.st_mode;
1496 src_uid = sb.st_uid;
1497 src_gid = sb.st_gid;
1498 utb.actime = sb.st_atime;
1499 utb.modtime = sb.st_mtime;
1500 file_size = sb.st_size;
1502 open_flags = O_WRONLY;
1503 if (dst_exists)
1505 if (ctx->do_append != 0)
1506 open_flags |= O_APPEND;
1507 else
1508 open_flags |= O_CREAT | O_TRUNC;
1510 else
1512 open_flags |= O_CREAT | O_EXCL;
1515 while ((dest_desc = mc_open (dst_path, open_flags, src_mode)) < 0)
1517 if (errno != EEXIST)
1519 if (ctx->skip_all)
1520 return_status = FILE_SKIPALL;
1521 else
1523 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1524 if (return_status == FILE_RETRY)
1525 continue;
1526 if (return_status == FILE_SKIPALL)
1527 ctx->skip_all = TRUE;
1528 ctx->do_append = FALSE;
1531 goto ret;
1533 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1535 appending = ctx->do_append;
1536 ctx->do_append = FALSE;
1538 /* Find out the optimal buffer size. */
1539 while (mc_fstat (dest_desc, &sb) != 0)
1541 if (ctx->skip_all)
1542 return_status = FILE_SKIPALL;
1543 else
1545 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1546 if (return_status == FILE_RETRY)
1547 continue;
1548 if (return_status == FILE_SKIPALL)
1549 ctx->skip_all = TRUE;
1551 goto ret;
1554 while (TRUE)
1556 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1557 if (errno == 0)
1558 break;
1560 if (ctx->skip_all)
1561 return_status = FILE_SKIPALL;
1562 else
1564 return_status =
1565 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1566 if (return_status == FILE_RETRY)
1567 continue;
1568 if (return_status == FILE_SKIPALL)
1569 ctx->skip_all = TRUE;
1571 mc_close (dest_desc);
1572 dest_desc = -1;
1573 mc_unlink (dst_path);
1574 dst_status = DEST_NONE;
1575 goto ret;
1578 ctx->eta_secs = 0.0;
1579 ctx->bps = 0;
1581 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1582 file_progress_show (ctx, 0, file_size, "", TRUE);
1583 else
1584 file_progress_show (ctx, 1, 1, "", TRUE);
1585 return_status = check_progress_buttons (ctx);
1586 mc_refresh ();
1588 if (return_status != FILE_CONT)
1589 goto ret;
1592 off_t n_read_total = 0;
1593 struct timeval tv_current, tv_last_update, tv_last_input;
1594 int secs, update_secs;
1595 const char *stalled_msg = "";
1597 tv_last_update = tv_transfer_start;
1599 while (TRUE)
1601 char buf[BUF_8K];
1603 /* src_read */
1604 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1605 n_read = -1;
1606 else
1607 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1609 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1610 if (return_status == FILE_RETRY)
1611 continue;
1612 if (return_status == FILE_SKIPALL)
1613 ctx->skip_all = TRUE;
1614 goto ret;
1616 if (n_read == 0)
1617 break;
1619 gettimeofday (&tv_current, NULL);
1621 if (n_read > 0)
1623 char *t = buf;
1624 n_read_total += n_read;
1626 /* Windows NT ftp servers report that files have no
1627 * permissions: -------, so if we happen to have actually
1628 * read something, we should fix the permissions.
1630 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1631 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1632 gettimeofday (&tv_last_input, NULL);
1634 /* dst_write */
1635 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read && !ctx->skip_all)
1637 if (n_written > 0)
1639 n_read -= n_written;
1640 t += n_written;
1641 continue;
1643 return_status = file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1644 if (return_status == FILE_SKIP)
1645 break;
1646 if (return_status == FILE_SKIPALL)
1647 ctx->skip_all = TRUE;
1648 if (return_status != FILE_RETRY)
1649 goto ret;
1653 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1655 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1656 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1658 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1660 copy_file_file_display_progress (tctx, ctx,
1661 tv_current,
1662 tv_transfer_start, file_size, n_read_total);
1663 tv_last_update = tv_current;
1665 is_first_time = FALSE;
1667 if (update_secs > FILEOP_STALLING_INTERVAL)
1669 stalled_msg = _("(stalled)");
1673 gboolean force_update;
1675 force_update =
1676 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1678 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1680 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1681 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1684 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1685 force_update);
1687 mc_refresh ();
1689 return_status = check_progress_buttons (ctx);
1691 if (return_status != FILE_CONT)
1693 mc_refresh ();
1694 goto ret;
1699 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1701 ret:
1702 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1704 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1705 if (temp_status == FILE_RETRY)
1706 continue;
1707 if (temp_status == FILE_ABORT)
1708 return_status = temp_status;
1709 if (temp_status == FILE_SKIPALL)
1710 ctx->skip_all = TRUE;
1711 break;
1714 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1716 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1717 if (temp_status == FILE_RETRY)
1718 continue;
1719 if (temp_status == FILE_SKIPALL)
1720 ctx->skip_all = TRUE;
1721 return_status = temp_status;
1722 break;
1725 if (dst_status == DEST_SHORT)
1727 /* Remove short file */
1728 int result;
1729 result = query_dialog (Q_ ("DialogTitle|Copy"),
1730 _("Incomplete file was retrieved. Keep it?"),
1731 D_ERROR, 2, _("&Delete"), _("&Keep"));
1732 if (result == 0)
1733 mc_unlink (dst_path);
1735 else if (dst_status == DEST_FULL)
1737 /* Copy has succeeded */
1738 if (!appending && ctx->preserve_uidgid)
1740 while (mc_chown (dst_path, src_uid, src_gid) != 0 && !ctx->skip_all)
1742 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1743 if (temp_status == FILE_RETRY)
1744 continue;
1745 if (temp_status == FILE_SKIPALL)
1747 ctx->skip_all = TRUE;
1748 return_status = FILE_CONT;
1750 if (temp_status == FILE_SKIP)
1751 return_status = FILE_CONT;
1752 break;
1756 if (!appending)
1758 if (ctx->preserve)
1760 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1762 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1763 if (temp_status == FILE_RETRY)
1764 continue;
1765 if (temp_status == FILE_SKIPALL)
1767 ctx->skip_all = TRUE;
1768 return_status = FILE_CONT;
1770 if (temp_status == FILE_SKIP)
1771 return_status = FILE_CONT;
1772 break;
1775 else if (!dst_exists)
1777 src_mode = umask (-1);
1778 umask (src_mode);
1779 src_mode = 0100666 & ~src_mode;
1780 mc_chmod (dst_path, (src_mode & ctx->umask_kill));
1782 mc_utime (dst_path, &utb);
1786 if (return_status == FILE_CONT)
1787 return_status = progress_update_one (tctx, ctx, file_size);
1789 return return_status;
1792 /* --------------------------------------------------------------------------------------------- */
1794 * I think these copy_*_* functions should have a return type.
1795 * anyway, this function *must* have two directories as arguments.
1797 /* FIXME: This function needs to check the return values of the
1798 function calls */
1800 FileProgressStatus
1801 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1802 gboolean toplevel, gboolean move_over, gboolean do_delete, struct link * parent_dirs)
1804 struct dirent *next;
1805 struct stat buf, cbuf;
1806 DIR *reading;
1807 char *dest_dir = NULL;
1808 FileProgressStatus return_status = FILE_CONT;
1809 struct utimbuf utb;
1810 struct link *lp;
1811 char *d;
1813 d = g_strdup (_d);
1815 /* First get the mode of the source dir */
1816 retry_src_stat:
1817 if ((*ctx->stat_func) (s, &cbuf) != 0)
1819 if (ctx->skip_all)
1820 return_status = FILE_SKIPALL;
1821 else
1823 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1824 if (return_status == FILE_RETRY)
1825 goto retry_src_stat;
1826 if (return_status == FILE_SKIPALL)
1827 ctx->skip_all = TRUE;
1829 goto ret_fast;
1832 if (is_in_linklist (dest_dirs, s, &cbuf))
1834 /* Don't copy a directory we created before (we don't want to copy
1835 infinitely if a directory is copied into itself) */
1836 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1837 return_status = FILE_CONT;
1838 goto ret_fast;
1841 /* Hmm, hardlink to directory??? - Norbert */
1842 /* FIXME: In this step we should do something
1843 in case the destination already exist */
1844 /* Check the hardlinks */
1845 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf))
1847 /* We have made a hardlink - no more processing is necessary */
1848 goto ret_fast;
1851 if (!S_ISDIR (cbuf.st_mode))
1853 if (ctx->skip_all)
1854 return_status = FILE_SKIPALL;
1855 else
1857 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1858 if (return_status == FILE_RETRY)
1859 goto retry_src_stat;
1860 if (return_status == FILE_SKIPALL)
1861 ctx->skip_all = TRUE;
1863 goto ret_fast;
1866 if (is_in_linklist (parent_dirs, s, &cbuf))
1868 /* we found a cyclic symbolic link */
1869 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
1870 return_status = FILE_SKIP;
1871 goto ret_fast;
1874 lp = g_new (struct link, 1);
1876 vfs_path_t *vpath = vfs_path_from_str (s);
1877 lp->vfs = vfs_path_get_by_index (vpath, -1)->class;
1878 vfs_path_free (vpath);
1880 lp->ino = cbuf.st_ino;
1881 lp->dev = cbuf.st_dev;
1882 lp->next = parent_dirs;
1883 parent_dirs = lp;
1885 retry_dst_stat:
1886 /* Now, check if the dest dir exists, if not, create it. */
1887 if (mc_stat (d, &buf))
1889 /* Here the dir doesn't exist : make it ! */
1890 if (move_over)
1892 if (mc_rename (s, d) == 0)
1894 return_status = FILE_CONT;
1895 goto ret;
1898 dest_dir = d;
1899 d = NULL;
1901 else
1904 * If the destination directory exists, we want to copy the whole
1905 * directory, but we only want this to happen once.
1907 * Escape sequences added to the * to compiler warnings.
1908 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
1909 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
1911 if (!S_ISDIR (buf.st_mode))
1913 if (ctx->skip_all)
1914 return_status = FILE_SKIPALL;
1915 else
1917 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
1918 if (return_status == FILE_SKIPALL)
1919 ctx->skip_all = TRUE;
1920 if (return_status == FILE_RETRY)
1921 goto retry_dst_stat;
1923 goto ret;
1925 /* Dive into subdir if exists */
1926 if (toplevel && ctx->dive_into_subdirs)
1928 dest_dir = concat_dir_and_file (d, x_basename (s));
1930 else
1932 dest_dir = d;
1933 d = NULL;
1934 goto dont_mkdir;
1937 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU))
1939 if (ctx->skip_all)
1940 return_status = FILE_SKIPALL;
1941 else
1943 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
1944 if (return_status == FILE_SKIPALL)
1945 ctx->skip_all = TRUE;
1947 if (return_status != FILE_RETRY)
1948 goto ret;
1951 lp = g_new (struct link, 1);
1952 mc_stat (dest_dir, &buf);
1954 vfs_path_t *vpath = vfs_path_from_str (dest_dir);
1955 lp->vfs = vfs_path_get_by_index (vpath, -1)->class;
1956 vfs_path_free (vpath);
1958 lp->ino = buf.st_ino;
1959 lp->dev = buf.st_dev;
1960 lp->next = dest_dirs;
1961 dest_dirs = lp;
1963 if (ctx->preserve_uidgid)
1965 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid) != 0)
1967 if (ctx->skip_all)
1968 return_status = FILE_SKIPALL;
1969 else
1971 return_status =
1972 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
1973 if (return_status == FILE_SKIPALL)
1974 ctx->skip_all = TRUE;
1976 if (return_status != FILE_RETRY)
1977 goto ret;
1981 dont_mkdir:
1982 /* open the source dir for reading */
1983 reading = mc_opendir (s);
1984 if (reading == NULL)
1985 goto ret;
1987 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1989 char *path;
1991 * Now, we don't want '.' and '..' to be created / copied at any time
1993 if (!strcmp (next->d_name, "."))
1994 continue;
1995 if (!strcmp (next->d_name, ".."))
1996 continue;
1998 /* get the filename and add it to the src directory */
1999 path = concat_dir_and_file (s, next->d_name);
2001 (*ctx->stat_func) (path, &buf);
2002 if (S_ISDIR (buf.st_mode))
2004 char *mdpath;
2006 mdpath = concat_dir_and_file (dest_dir, next->d_name);
2008 * From here, we just intend to recursively copy subdirs, not
2009 * the double functionality of copying different when the target
2010 * dir already exists. So, we give the recursive call the flag 0
2011 * meaning no toplevel.
2013 return_status =
2014 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2015 g_free (mdpath);
2017 else
2019 char *dest_file;
2021 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
2022 return_status = copy_file_file (tctx, ctx, path, dest_file);
2023 g_free (dest_file);
2025 if (do_delete && return_status == FILE_CONT)
2027 if (ctx->erase_at_end)
2029 static struct link *tail;
2030 size_t len = strlen (path);
2031 lp = g_malloc (sizeof (struct link) + len);
2032 strncpy (lp->name, path, len + 1);
2033 lp->st_mode = buf.st_mode;
2034 lp->next = NULL;
2035 if (erase_list != NULL)
2037 tail->next = lp;
2038 tail = lp;
2040 else
2041 erase_list = tail = lp;
2043 else
2045 if (S_ISDIR (buf.st_mode))
2047 return_status = erase_dir_iff_empty (ctx, path);
2049 else
2050 return_status = erase_file (tctx, ctx, path);
2053 g_free (path);
2055 mc_closedir (reading);
2057 if (ctx->preserve)
2059 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
2060 utb.actime = cbuf.st_atime;
2061 utb.modtime = cbuf.st_mtime;
2062 mc_utime (dest_dir, &utb);
2064 else
2066 cbuf.st_mode = umask (-1);
2067 umask (cbuf.st_mode);
2068 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2069 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
2072 ret:
2073 g_free (dest_dir);
2074 g_free (parent_dirs);
2075 ret_fast:
2076 g_free (d);
2077 return return_status;
2080 /* }}} */
2082 /* --------------------------------------------------------------------------------------------- */
2083 /* {{{ Move routines */
2085 FileProgressStatus
2086 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2088 struct stat sbuf, dbuf, destbuf;
2089 struct link *lp;
2090 char *destdir;
2091 FileProgressStatus return_status;
2092 gboolean move_over = FALSE;
2093 gboolean dstat_ok;
2095 file_progress_show_source (ctx, s);
2096 file_progress_show_target (ctx, d);
2097 if (check_progress_buttons (ctx) == FILE_ABORT)
2098 return FILE_ABORT;
2100 mc_refresh ();
2102 mc_stat (s, &sbuf);
2103 dstat_ok = (mc_stat (d, &dbuf) == 0);
2105 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2106 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2108 if (!dstat_ok)
2109 destdir = g_strdup (d); /* destination doesn't exist */
2110 else if (!ctx->dive_into_subdirs)
2112 destdir = g_strdup (d);
2113 move_over = TRUE;
2115 else
2116 destdir = concat_dir_and_file (d, x_basename (s));
2118 /* Check if the user inputted an existing dir */
2119 retry_dst_stat:
2120 if (mc_stat (destdir, &destbuf) == 0)
2122 if (move_over)
2124 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2126 if (return_status != FILE_CONT)
2127 goto ret;
2128 goto oktoret;
2130 else if (ctx->skip_all)
2131 return_status = FILE_SKIPALL;
2132 else
2134 if (S_ISDIR (destbuf.st_mode))
2135 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2136 else
2137 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2138 if (return_status == FILE_SKIPALL)
2139 ctx->skip_all = TRUE;
2140 if (return_status == FILE_RETRY)
2141 goto retry_dst_stat;
2143 g_free (destdir);
2144 return return_status;
2147 retry_rename:
2148 if (mc_rename (s, destdir) == 0)
2150 return_status = FILE_CONT;
2151 goto ret;
2154 if (errno != EXDEV)
2156 if (!ctx->skip_all)
2158 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2159 if (return_status == FILE_SKIPALL)
2160 ctx->skip_all = TRUE;
2161 if (return_status == FILE_RETRY)
2162 goto retry_rename;
2164 goto ret;
2166 /* Failed because of filesystem boundary -> copy dir instead */
2167 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2169 if (return_status != FILE_CONT)
2170 goto ret;
2171 oktoret:
2172 file_progress_show_source (ctx, NULL);
2173 file_progress_show (ctx, 0, 0, "", FALSE);
2175 return_status = check_progress_buttons (ctx);
2176 if (return_status != FILE_CONT)
2177 goto ret;
2179 mc_refresh ();
2180 if (ctx->erase_at_end)
2182 for (; erase_list && return_status != FILE_ABORT;)
2184 if (S_ISDIR (erase_list->st_mode))
2186 return_status = erase_dir_iff_empty (ctx, erase_list->name);
2188 else
2189 return_status = erase_file (tctx, ctx, erase_list->name);
2190 lp = erase_list;
2191 erase_list = erase_list->next;
2192 g_free (lp);
2195 erase_dir_iff_empty (ctx, s);
2197 ret:
2198 g_free (destdir);
2199 while (erase_list)
2201 lp = erase_list;
2202 erase_list = erase_list->next;
2203 g_free (lp);
2205 return return_status;
2208 /* }}} */
2210 /* --------------------------------------------------------------------------------------------- */
2211 /* {{{ Erase routines */
2213 FileProgressStatus
2214 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
2216 FileProgressStatus error;
2218 if (strcmp (s, "..") == 0)
2219 return FILE_SKIP;
2221 if (strcmp (s, ".") == 0)
2222 return FILE_SKIP;
2224 file_progress_show_deleting (ctx, s);
2225 if (check_progress_buttons (ctx) == FILE_ABORT)
2226 return FILE_ABORT;
2227 mc_refresh ();
2229 /* The old way to detect a non empty directory was:
2230 error = my_rmdir (s);
2231 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2232 For the linux user space nfs server (nfs-server-2.2beta29-2)
2233 we would have to check also for EIO. I hope the new way is
2234 fool proof. (Norbert)
2236 error = check_dir_is_empty (s);
2237 if (error == 0)
2238 { /* not empty */
2239 error = query_recursive (ctx, s);
2240 if (error == FILE_CONT)
2241 return recursive_erase (tctx, ctx, s);
2242 else
2243 return error;
2246 while (my_rmdir (s) == -1 && !ctx->skip_all)
2248 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2249 if (error != FILE_RETRY)
2250 return error;
2253 return FILE_CONT;
2256 /* }}} */
2258 /* --------------------------------------------------------------------------------------------- */
2259 /* {{{ Panel operate routines */
2261 ComputeDirSizeUI *
2262 compute_dir_size_create_ui (void)
2264 ComputeDirSizeUI *ui;
2266 const char *b_name = N_("&Abort");
2268 #ifdef ENABLE_NLS
2269 b_name = _(b_name);
2270 #endif
2272 ui = g_new (ComputeDirSizeUI, 1);
2274 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
2275 NULL, _("Directory scanning"), DLG_CENTER);
2276 ui->dirname = label_new (3, 3, "");
2277 add_widget (ui->dlg, ui->dirname);
2279 add_widget (ui->dlg,
2280 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
2281 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2283 /* We will manage the dialog without any help,
2284 that's why we have to call init_dlg */
2285 init_dlg (ui->dlg);
2287 return ui;
2290 /* --------------------------------------------------------------------------------------------- */
2292 void
2293 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2295 if (ui != NULL)
2297 /* schedule to update passive panel */
2298 other_panel->dirty = 1;
2300 /* close and destroy dialog */
2301 dlg_run_done (ui->dlg);
2302 destroy_dlg (ui->dlg);
2303 g_free (ui);
2307 /* --------------------------------------------------------------------------------------------- */
2309 FileProgressStatus
2310 compute_dir_size_update_ui (const void *ui, const char *dirname)
2312 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2313 int c;
2314 Gpm_Event event;
2316 if (ui == NULL)
2317 return FILE_CONT;
2319 label_set_text (this->dirname, str_trunc (dirname, this->dlg->cols - 6));
2321 event.x = -1; /* Don't show the GPM cursor */
2322 c = tty_get_event (&event, FALSE, FALSE);
2323 if (c == EV_NONE)
2324 return FILE_CONT;
2326 /* Reinitialize to avoid old values after events other than
2327 selecting a button */
2328 this->dlg->ret_value = FILE_CONT;
2330 dlg_process_event (this->dlg, c, &event);
2332 switch (this->dlg->ret_value)
2334 case B_CANCEL:
2335 case FILE_ABORT:
2336 return FILE_ABORT;
2337 default:
2338 return FILE_CONT;
2342 /* --------------------------------------------------------------------------------------------- */
2344 * compute_dir_size:
2346 * Computes the number of bytes used by the files in a directory
2349 FileProgressStatus
2350 compute_dir_size (const char *dirname, const void *ui,
2351 compute_dir_size_callback cback,
2352 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2354 int res;
2355 struct stat s;
2356 DIR *dir;
2357 struct dirent *dirent;
2358 FileProgressStatus ret = FILE_CONT;
2360 if (!compute_symlinks)
2362 res = mc_lstat (dirname, &s);
2363 if (res != 0)
2364 return ret;
2366 /* don't scan symlink to directory */
2367 if (S_ISLNK (s.st_mode))
2369 (*ret_marked)++;
2370 *ret_total += (uintmax_t) s.st_size;
2371 return ret;
2375 dir = mc_opendir (dirname);
2377 if (dir == NULL)
2378 return ret;
2380 while ((dirent = mc_readdir (dir)) != NULL)
2382 char *fullname;
2384 ret = (cback != NULL) ? cback (ui, dirname) : FILE_CONT;
2386 if (ret != FILE_CONT)
2387 break;
2389 if (strcmp (dirent->d_name, ".") == 0)
2390 continue;
2391 if (strcmp (dirent->d_name, "..") == 0)
2392 continue;
2394 fullname = concat_dir_and_file (dirname, dirent->d_name);
2395 res = mc_lstat (fullname, &s);
2397 if (res != 0)
2399 g_free (fullname);
2400 continue;
2403 if (S_ISDIR (s.st_mode))
2405 size_t subdir_count = 0;
2406 uintmax_t subdir_bytes = 0;
2408 ret =
2409 compute_dir_size (fullname, ui, cback, &subdir_count, &subdir_bytes,
2410 compute_symlinks);
2412 if (ret != FILE_CONT)
2414 g_free (fullname);
2415 break;
2418 *ret_marked += subdir_count;
2419 *ret_total += subdir_bytes;
2421 else
2423 (*ret_marked)++;
2424 *ret_total += (uintmax_t) s.st_size;
2427 g_free (fullname);
2430 mc_closedir (dir);
2432 return ret;
2435 /* --------------------------------------------------------------------------------------------- */
2437 * panel_operate:
2439 * Performs one of the operations on the selection on the source_panel
2440 * (copy, delete, move).
2442 * Returns TRUE if did change the directory
2443 * structure, Returns FALSE if user aborted
2445 * force_single forces operation on the current entry and affects
2446 * default destination. Current filename is used as default.
2449 gboolean
2450 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2452 WPanel *panel = (WPanel *) source_panel;
2453 const gboolean single_entry = force_single || (panel->marked <= 1)
2454 || (get_current_type () == view_tree);
2456 char *source = NULL;
2457 #ifdef WITH_FULL_PATHS
2458 char *source_with_path = NULL;
2459 #else
2460 #define source_with_path source
2461 #endif /* !WITH_FULL_PATHS */
2462 char *dest = NULL;
2463 char *temp = NULL;
2464 char *save_cwd = NULL, *save_dest = NULL;
2465 struct stat src_stat;
2466 gboolean ret_val = TRUE;
2467 int i;
2468 FileProgressStatus value;
2469 FileOpContext *ctx;
2470 FileOpTotalContext *tctx;
2472 gboolean do_bg = FALSE; /* do background operation? */
2474 static gboolean i18n_flag = FALSE;
2475 if (!i18n_flag)
2477 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2478 op_names[i] = Q_ (op_names[i]);
2479 i18n_flag = TRUE;
2482 free_linklist (&linklist);
2483 free_linklist (&dest_dirs);
2485 if (single_entry)
2487 if (force_single)
2488 source = selection (panel)->fname;
2489 else
2490 source = panel_get_file (panel);
2492 if (strcmp (source, "..") == 0)
2494 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2495 return FALSE;
2498 /* Update stat to get actual info */
2499 if (mc_lstat (source, &src_stat) != 0)
2501 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2502 path_trunc (source, 30), unix_error_string (errno));
2504 /* Directory was changed outside MC. Reload it forced */
2505 if (!panel->is_panelized)
2507 panel_update_flags_t flags = UP_RELOAD;
2509 /* don't update panelized panel */
2510 if (get_other_type () == view_listing && other_panel->is_panelized)
2511 flags |= UP_ONLY_CURRENT;
2513 update_panels (flags, UP_KEEPSEL);
2516 return FALSE;
2520 ctx = file_op_context_new (operation);
2522 /* Show confirmation dialog */
2523 if (operation != OP_DELETE)
2525 char *dest_dir;
2526 char *dest_dir_;
2527 char *format;
2529 /* Forced single operations default to the original name */
2530 if (force_single)
2531 dest_dir = source;
2532 else if (get_other_type () == view_listing)
2533 dest_dir = other_panel->cwd;
2534 else
2535 dest_dir = panel->cwd;
2537 * Add trailing backslash only when do non-local ops.
2538 * It saves user from occasional file renames (when destination
2539 * dir is deleted)
2541 if (!force_single && dest_dir[0] != '\0' && dest_dir[strlen (dest_dir) - 1] != PATH_SEP)
2543 /* add trailing separator */
2544 dest_dir_ = g_strconcat (dest_dir, PATH_SEP_STR, (char *) NULL);
2546 else
2548 /* just copy */
2549 dest_dir_ = g_strdup (dest_dir);
2552 if (dest_dir_ == NULL)
2554 ret_val = FALSE;
2555 goto ret_fast;
2558 /* Generate confirmation prompt */
2559 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2561 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2562 source != NULL ? (void *) source
2563 : (void *) &panel->marked, dest_dir_, &do_bg);
2565 g_free (format);
2566 g_free (dest_dir_);
2568 if (dest == NULL || dest[0] == '\0')
2570 g_free (dest);
2571 ret_val = FALSE;
2572 goto ret_fast;
2575 else if (confirm_delete)
2577 char *format;
2578 char fmd_buf[BUF_MEDIUM];
2580 /* Generate confirmation prompt */
2581 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2583 if (source == NULL)
2584 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2585 else
2587 const int fmd_xlen = 64;
2588 i = fmd_xlen - str_term_width1 (format) - 4;
2589 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2592 g_free (format);
2594 if (safe_delete)
2595 query_set_sel (1);
2597 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2599 if (i != 0)
2601 ret_val = FALSE;
2602 goto ret_fast;
2606 tctx = file_op_total_context_new ();
2607 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2610 filegui_dialog_type_t dialog_type;
2612 if (operation == OP_DELETE)
2613 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2614 else
2616 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2617 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2619 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2620 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2623 /* Background also need ctx->ui, but not full */
2624 if (do_bg)
2625 file_op_context_create_ui_without_init (ctx, TRUE, dialog_type);
2626 else
2627 file_op_context_create_ui (ctx, TRUE, dialog_type);
2630 #ifdef ENABLE_BACKGROUND
2631 /* Did the user select to do a background operation? */
2632 if (do_bg)
2634 int v;
2636 v = do_background (ctx, g_strconcat (op_names[operation], ": ", panel->cwd, (char *) NULL));
2637 if (v == -1)
2638 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2640 /* If we are the parent */
2641 if (v == 1)
2643 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
2644 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
2645 /* file_op_context_destroy (ctx); */
2646 return FALSE;
2649 #endif /* ENABLE_BACKGROUND */
2651 /* Initialize things */
2652 /* We do not want to trash cache every time file is
2653 created/touched. However, this will make our cache contain
2654 invalid data. */
2655 if ((dest != NULL) && (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1)))
2656 save_dest = g_strdup (dest);
2658 if ((panel->cwd[0] != '\0') && (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1)))
2659 save_cwd = g_strdup (panel->cwd);
2661 /* Now, let's do the job */
2663 /* This code is only called by the tree and panel code */
2664 if (single_entry)
2666 /* We now have ETA in all cases */
2668 /* One file: FIXME mc_chdir will take user out of any vfs */
2669 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2671 vfs_path_t *vpath;
2672 int chdir_retcode;
2674 vpath = vfs_path_from_str (PATH_SEP_STR);
2675 chdir_retcode = mc_chdir (vpath);
2676 vfs_path_free (vpath);
2677 if (chdir_retcode < 0)
2679 ret_val = FALSE;
2680 goto clean_up;
2684 /* The source and src_stat variables have been initialized before */
2685 #ifdef WITH_FULL_PATHS
2686 if (g_path_is_absolute (source))
2687 source_with_path = g_strdup (source);
2688 else
2689 source_with_path = mc_build_filename (panel->cwd, source, (char *) NULL);
2690 #endif /* WITH_FULL_PATHS */
2692 if (panel_operate_init_totals (operation, panel, source_with_path, ctx) == FILE_CONT)
2694 if (operation == OP_DELETE)
2696 if (S_ISDIR (src_stat.st_mode))
2697 value = erase_dir (tctx, ctx, source_with_path);
2698 else
2699 value = erase_file (tctx, ctx, source_with_path);
2701 else
2703 temp = transform_source (ctx, source_with_path);
2704 if (temp == NULL)
2705 value = transform_error;
2706 else
2708 char *repl_dest, *temp2;
2710 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2711 temp2 = concat_dir_and_file (repl_dest, temp);
2712 g_free (temp);
2713 g_free (repl_dest);
2714 g_free (dest);
2715 dest = temp2;
2717 switch (operation)
2719 case OP_COPY:
2720 /* we use file_mask_op_follow_links only with OP_COPY */
2721 ctx->stat_func (source_with_path, &src_stat);
2723 if (S_ISDIR (src_stat.st_mode))
2724 value = copy_dir_dir (tctx, ctx, source_with_path, dest,
2725 TRUE, FALSE, FALSE, NULL);
2726 else
2727 value = copy_file_file (tctx, ctx, source_with_path, dest);
2728 break;
2730 case OP_MOVE:
2731 if (S_ISDIR (src_stat.st_mode))
2732 value = move_dir_dir (tctx, ctx, source_with_path, dest);
2733 else
2734 value = move_file_file (tctx, ctx, source_with_path, dest);
2735 break;
2737 default:
2738 /* Unknown file operation */
2739 abort ();
2742 } /* Copy or move operation */
2744 if ((value == FILE_CONT) && !force_single)
2745 unmark_files (panel);
2748 else
2750 /* Many files */
2752 /* Check destination for copy or move operation */
2753 while (operation != OP_DELETE)
2755 int dst_result;
2756 struct stat dst_stat;
2758 dst_result = mc_stat (dest, &dst_stat);
2760 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2761 break;
2763 if (ctx->skip_all
2764 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2765 goto clean_up;
2768 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2770 /* Loop for every file, perform the actual copy operation */
2771 for (i = 0; i < panel->count; i++)
2773 if (!panel->dir.list[i].f.marked)
2774 continue; /* Skip the unmarked ones */
2776 source = panel->dir.list[i].fname;
2777 src_stat = panel->dir.list[i].st;
2779 #ifdef WITH_FULL_PATHS
2780 g_free (source_with_path);
2781 if (g_path_is_absolute (source))
2782 source_with_path = g_strdup (source);
2783 else
2784 source_with_path = mc_build_filename (panel->cwd, source, (char *) NULL);
2785 #endif /* WITH_FULL_PATHS */
2787 if (operation == OP_DELETE)
2789 if (S_ISDIR (src_stat.st_mode))
2790 value = erase_dir (tctx, ctx, source_with_path);
2791 else
2792 value = erase_file (tctx, ctx, source_with_path);
2794 else
2796 temp = transform_source (ctx, source_with_path);
2798 if (temp == NULL)
2799 value = transform_error;
2800 else
2802 char *temp2, *temp3, *repl_dest;
2804 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2805 temp2 = concat_dir_and_file (repl_dest, temp);
2806 g_free (temp);
2807 g_free (repl_dest);
2808 temp3 = source_with_path;
2809 source_with_path = strutils_shell_unescape (source_with_path);
2810 g_free (temp3);
2811 temp3 = temp2;
2812 temp2 = strutils_shell_unescape (temp2);
2813 g_free (temp3);
2815 switch (operation)
2817 case OP_COPY:
2818 /* we use file_mask_op_follow_links only with OP_COPY */
2819 ctx->stat_func (source_with_path, &src_stat);
2820 if (S_ISDIR (src_stat.st_mode))
2821 value = copy_dir_dir (tctx, ctx, source_with_path, temp2,
2822 TRUE, FALSE, FALSE, NULL);
2823 else
2824 value = copy_file_file (tctx, ctx, source_with_path, temp2);
2825 free_linklist (&dest_dirs);
2826 break;
2828 case OP_MOVE:
2829 if (S_ISDIR (src_stat.st_mode))
2830 value = move_dir_dir (tctx, ctx, source_with_path, temp2);
2831 else
2832 value = move_file_file (tctx, ctx, source_with_path, temp2);
2833 break;
2835 default:
2836 /* Unknown file operation */
2837 abort ();
2840 g_free (temp2);
2842 } /* Copy or move operation */
2844 if (value == FILE_ABORT)
2845 break;
2847 if (value == FILE_CONT)
2848 do_file_mark (panel, i, 0);
2850 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2852 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2853 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
2856 if (operation != OP_DELETE)
2857 file_progress_show (ctx, 0, 0, "", FALSE);
2859 if (check_progress_buttons (ctx) == FILE_ABORT)
2860 break;
2862 mc_refresh ();
2863 } /* Loop for every file */
2865 } /* Many entries */
2867 clean_up:
2868 /* Clean up */
2869 if (save_cwd != NULL)
2871 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2872 g_free (save_cwd);
2875 if (save_dest != NULL)
2877 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2878 g_free (save_dest);
2881 free_linklist (&linklist);
2882 free_linklist (&dest_dirs);
2883 #ifdef WITH_FULL_PATHS
2884 g_free (source_with_path);
2885 #endif /* WITH_FULL_PATHS */
2886 g_free (dest);
2887 g_free (ctx->dest_mask);
2888 ctx->dest_mask = NULL;
2890 #ifdef ENABLE_BACKGROUND
2891 /* Let our parent know we are saying bye bye */
2892 if (mc_global.we_are_background)
2894 int cur_pid = getpid ();
2895 /* Send pid to parent with child context, it is fork and
2896 don't modify real parent ctx */
2897 ctx->pid = cur_pid;
2898 parent_call ((void *) end_bg_process, ctx, 0);
2900 vfs_shut ();
2901 _exit (0);
2903 #endif /* ENABLE_BACKGROUND */
2905 file_op_total_context_destroy (tctx);
2906 ret_fast:
2907 file_op_context_destroy (ctx);
2909 return ret_val;
2912 /* }}} */
2914 /* --------------------------------------------------------------------------------------------- */
2915 /* {{{ Query/status report routines */
2916 /** Report error with one file */
2917 FileProgressStatus
2918 file_error (const char *format, const char *file)
2920 char buf[BUF_MEDIUM];
2922 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
2924 return do_file_error (buf);
2927 /* --------------------------------------------------------------------------------------------- */
2930 Cause emacs to enter folding mode for this file:
2931 Local variables:
2932 end: