Added function mc_build_filename() for processing URL-paths as well
[midnight-commander.git] / src / filemanager / file.c
blob8aa9e1a6bafcfd19a8708a18617509bb3be9fe72
1 /* File management.
2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1994, 1995 Janne Kukonlehto
6 1994, 1995 Fred Leeflang
7 1994, 1995, 1996 Miguel de Icaza
8 1995, 1996 Jakub Jelinek
9 1997 Norbert Warmuth
10 1998 Pavel Machek
12 The copy code was based in GNU's cp, and was written by:
13 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
15 The move code was based in GNU's mv, and was written by:
16 Mike Parker and David MacKenzie.
18 Janne Kukonlehto added much error recovery to them for being used
19 in an interactive program.
21 This program is free software; you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation; either version 2 of the License, or
24 (at your option) any later version.
26 This program is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
31 You should have received a copy of the GNU General Public License
32 along with this program; if not, write to the Free Software
33 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
36 * Please note that all dialogs used here must be safe for background
37 * operations.
40 /** \file file.c
41 * \brief Source: file management
44 /* {{{ Include files */
46 #include <config.h>
48 #include <ctype.h>
49 #include <errno.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <unistd.h>
56 #include <fcntl.h>
58 #include "lib/global.h"
59 #include "lib/tty/tty.h"
60 #include "lib/tty/key.h"
61 #include "lib/search.h"
62 #include "lib/strescape.h"
63 #include "lib/strutil.h"
64 #include "lib/util.h"
65 #include "lib/vfs/vfs.h"
66 #include "lib/widget.h"
68 #include "src/setup.h"
69 #include "src/background.h"
71 #include "layout.h" /* rotate_dash() */
73 /* Needed for current_panel, other_panel and WTree */
74 #include "dir.h"
75 #include "filegui.h"
76 #include "tree.h"
77 #include "midnight.h" /* current_panel */
79 #include "file.h"
81 /* }}} */
83 /*** global variables ****************************************************************************/
85 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
86 const char *op_names[3] = {
87 N_("DialogTitle|Copy"),
88 N_("DialogTitle|Move"),
89 N_("DialogTitle|Delete")
92 /*** file scope macro definitions ****************************************************************/
94 /* Hack: the vfs code should not rely on this */
95 #define WITH_FULL_PATHS 1
97 #define FILEOP_UPDATE_INTERVAL 2
98 #define FILEOP_STALLING_INTERVAL 4
100 /*** file scope type declarations ****************************************************************/
102 /* This is a hard link cache */
103 struct link
105 struct link *next;
106 struct vfs_class *vfs;
107 dev_t dev;
108 ino_t ino;
109 short linkcount;
110 mode_t st_mode;
111 char name[1];
114 /* Status of the destination file */
115 typedef enum
117 DEST_NONE = 0, /* Not created */
118 DEST_SHORT = 1, /* Created, not fully copied */
119 DEST_FULL = 2 /* Created, fully copied */
120 } dest_status_t;
123 * This array introduced to avoid translation problems. The former (op_names)
124 * is assumed to be nouns, suitable in dialog box titles; this one should
125 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
126 * (I don't use spaces around the words, because someday they could be
127 * dropped, when widgets get smarter)
130 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
131 static const char *op_names1[] = {
132 N_("FileOperation|Copy"),
133 N_("FileOperation|Move"),
134 N_("FileOperation|Delete")
138 * These are formats for building a prompt. Parts encoded as follows:
139 * %o - operation from op_names1
140 * %f - file/files or files/directories, as appropriate
141 * %m - "with source mask" or question mark for delete
142 * %s - source name (truncated)
143 * %d - number of marked files
144 * %e - "to:" or question mark for delete
146 * xgettext:no-c-format */
147 static const char *one_format = N_("%o %f \"%s\"%m");
148 /* xgettext:no-c-format */
149 static const char *many_format = N_("%o %d %f%m");
151 static const char *prompt_parts[] = {
152 N_("file"),
153 N_("files"),
154 N_("directory"),
155 N_("directories"),
156 N_("files/directories"),
157 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
158 N_(" with source mask:"),
159 N_("to:")
162 static const char *question_format = N_("%s?");
164 /*** file scope variables ************************************************************************/
166 /* the hard link cache */
167 static struct link *linklist = NULL;
169 /* the files-to-be-erased list */
170 static struct link *erase_list;
173 * In copy_dir_dir we use two additional single linked lists: The first -
174 * variable name `parent_dirs' - holds information about already copied
175 * directories and is used to detect cyclic symbolic links.
176 * The second (`dest_dirs' below) holds information about just created
177 * target directories and is used to detect when an directory is copied
178 * into itself (we don't want to copy infinitly).
179 * Both lists don't use the linkcount and name structure members of struct
180 * link.
182 static struct link *dest_dirs = NULL;
184 static FileProgressStatus transform_error = FILE_CONT;
186 /*** file scope functions ************************************************************************/
187 /* --------------------------------------------------------------------------------------------- */
189 static char *
190 transform_source (FileOpContext * ctx, const char *source)
192 char *s, *q;
193 char *fnsource;
195 s = g_strdup (source);
197 /* We remove \n from the filename since regex routines would use \n as an anchor */
198 /* this is just to be allowed to maniupulate file names with \n on it */
199 for (q = s; *q != '\0'; q++)
200 if (*q == '\n')
201 *q = ' ';
203 fnsource = (char *) x_basename (s);
205 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
206 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
207 else
209 q = NULL;
210 transform_error = FILE_SKIP;
213 g_free (s);
214 return q;
217 /* --------------------------------------------------------------------------------------------- */
219 static void
220 free_linklist (struct link **lc_linklist)
222 struct link *lp, *lp2;
224 for (lp = *lc_linklist; lp != NULL; lp = lp2)
226 lp2 = lp->next;
227 g_free (lp);
229 *lc_linklist = NULL;
232 /* --------------------------------------------------------------------------------------------- */
234 static int
235 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
237 vfs_path_t *vpath;
238 vfs_path_element_t *vpath_element;
239 ino_t ino = sb->st_ino;
240 dev_t dev = sb->st_dev;
242 vpath = vfs_path_from_str (path);
243 vpath_element = vfs_path_get_by_index (vpath, -1);
245 while (lp != NULL)
247 if (lp->vfs == vpath_element->class)
248 if (lp->ino == ino && lp->dev == dev)
249 return 1;
250 lp = lp->next;
252 vfs_path_free (vpath);
253 return 0;
256 /* --------------------------------------------------------------------------------------------- */
258 * Check and made hardlink
260 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
261 * and a hardlink was succesfully made
264 static int
265 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
267 struct link *lp;
268 vfs_path_t *vpath;
270 struct vfs_class *my_vfs;
271 ino_t ino = pstat->st_ino;
272 dev_t dev = pstat->st_dev;
273 struct stat link_stat;
274 const char *p;
276 vpath = vfs_path_from_str (src_name);
278 if ((vfs_file_class_flags (vpath) & VFSF_NOLINKS) != 0)
280 vfs_path_free (vpath);
281 return FALSE;
283 my_vfs = vfs_path_get_by_index (vpath, -1)->class;
284 vfs_path_free (vpath);
286 for (lp = linklist; lp != NULL; lp = lp->next)
287 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev)
289 struct vfs_class *lp_name_class;
291 vpath = vfs_path_from_str (lp->name);
292 lp_name_class = vfs_path_get_by_index (vpath, -1)->class;
293 vfs_path_free (vpath);
295 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
296 && link_stat.st_dev == dev && lp_name_class == my_vfs)
298 struct vfs_class *p_class, *dst_name_class;
300 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
301 was copied to */
303 vpath = vfs_path_from_str (p);
304 p_class = vfs_path_get_by_index (vpath, -1)->class;
305 vfs_path_free (vpath);
307 vpath = vfs_path_from_str (dst_name);
308 dst_name_class = vfs_path_get_by_index (vpath, -1)->class;
309 vfs_path_free (vpath);
311 if (dst_name_class == p_class)
313 if (!mc_stat (p, &link_stat))
315 if (!mc_link (p, dst_name))
316 return TRUE;
320 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
321 return FALSE;
323 lp = (struct link *) g_try_malloc (sizeof (struct link) + strlen (src_name)
324 + strlen (dst_name) + 1);
325 if (lp)
327 char *lpdstname;
328 lp->vfs = my_vfs;
329 lp->ino = ino;
330 lp->dev = dev;
331 strcpy (lp->name, src_name);
332 lpdstname = lp->name + strlen (lp->name) + 1;
333 strcpy (lpdstname, dst_name);
334 lp->next = linklist;
335 linklist = lp;
337 return FALSE;
340 /* --------------------------------------------------------------------------------------------- */
342 * Duplicate the contents of the symbolic link src_path in dst_path.
343 * Try to make a stable symlink if the option "stable symlink" was
344 * set in the file mask dialog.
345 * If dst_path is an existing symlink it will be deleted silently
346 * (upper levels take already care of existing files at dst_path).
349 static FileProgressStatus
350 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
352 char link_target[MC_MAXPATHLEN];
353 int len;
354 FileProgressStatus return_status;
355 struct stat sb;
356 gboolean dst_is_symlink;
358 dst_is_symlink = (mc_lstat (dst_path, &sb) == 0) && S_ISLNK (sb.st_mode);
360 retry_src_readlink:
361 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
362 if (len < 0)
364 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
365 if (return_status == FILE_RETRY)
366 goto retry_src_readlink;
367 return return_status;
369 link_target[len] = 0;
371 if (ctx->stable_symlinks)
373 vfs_path_t *vpath1 = vfs_path_from_str (src_path);
374 vfs_path_t *vpath2 = vfs_path_from_str (dst_path);
376 if (!vfs_file_is_local (vpath1) || !vfs_file_is_local (vpath2))
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;
383 vfs_path_free (vpath1);
384 vfs_path_free (vpath2);
387 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
389 char *p, *q, *s;
391 const char *r = strrchr (src_path, PATH_SEP);
393 if (r)
395 p = g_strndup (src_path, r - src_path + 1);
396 if (g_path_is_absolute (dst_path))
397 q = g_strdup (dst_path);
398 else
399 q = g_strconcat (p, dst_path, (char *) NULL);
400 s = strrchr (q, PATH_SEP);
401 if (s)
403 s[1] = 0;
404 s = g_strconcat (p, link_target, (char *) NULL);
405 g_free (p);
406 g_strlcpy (link_target, s, sizeof (link_target));
407 g_free (s);
408 s = diff_two_paths (q, link_target);
409 if (s)
411 g_strlcpy (link_target, s, sizeof (link_target));
412 g_free (s);
415 else
416 g_free (p);
417 g_free (q);
420 retry_dst_symlink:
421 if (mc_symlink (link_target, dst_path) == 0)
422 /* Success */
423 return FILE_CONT;
425 * if dst_exists, it is obvious that this had failed.
426 * We can delete the old symlink and try again...
428 if (dst_is_symlink)
430 if (!mc_unlink (dst_path))
431 if (mc_symlink (link_target, dst_path) == 0)
432 /* Success */
433 return FILE_CONT;
435 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
436 if (return_status == FILE_RETRY)
437 goto retry_dst_symlink;
438 return return_status;
441 /* --------------------------------------------------------------------------------------------- */
443 static FileProgressStatus
444 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add,
445 gboolean is_toplevel_file)
447 struct timeval tv_current;
448 static struct timeval tv_start = { };
450 if (is_toplevel_file || ctx->progress_totals_computed)
452 tctx->progress_count++;
453 tctx->progress_bytes += (uintmax_t) add;
455 if (tv_start.tv_sec == 0)
457 gettimeofday (&tv_start, (struct timezone *) NULL);
459 gettimeofday (&tv_current, (struct timezone *) NULL);
460 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
462 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
464 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
465 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
467 tv_start.tv_sec = tv_current.tv_sec;
470 return check_progress_buttons (ctx);
473 /* --------------------------------------------------------------------------------------------- */
475 static FileProgressStatus
476 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
478 char *msg;
479 int result = 0;
480 const char *head_msg;
482 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
484 msg = g_strdup_printf (fmt, a, b);
485 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
486 g_free (msg);
487 do_refresh ();
489 return (result == 1) ? FILE_ABORT : FILE_SKIP;
492 /* --------------------------------------------------------------------------------------------- */
494 static FileProgressStatus
495 warn_same_file (const char *fmt, const char *a, const char *b)
497 #ifdef WITH_BACKGROUND
498 /* *INDENT-OFF* */
499 union
501 void *p;
502 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
503 const char *a, const char *b);
504 } pntr;
505 /* *INDENT-ON* */
507 pntr.f = real_warn_same_file;
509 if (mc_global.we_are_background)
510 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
511 #endif
512 return real_warn_same_file (Foreground, fmt, a, b);
515 /* --------------------------------------------------------------------------------------------- */
516 /* {{{ Query/status report routines */
518 static FileProgressStatus
519 real_do_file_error (enum OperationMode mode, const char *error)
521 int result;
522 const char *msg;
524 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
525 result =
526 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
528 switch (result)
530 case 0:
531 do_refresh ();
532 return FILE_SKIP;
534 case 1:
535 do_refresh ();
536 return FILE_SKIPALL;
538 case 2:
539 do_refresh ();
540 return FILE_RETRY;
542 case 3:
543 default:
544 return FILE_ABORT;
548 /* --------------------------------------------------------------------------------------------- */
550 static FileProgressStatus
551 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
553 gchar *text;
555 if (ctx->recursive_result < RECURSIVE_ALWAYS)
557 const char *msg = mode == Foreground
558 ? _("\nDirectory not empty.\nDelete it recursively?")
559 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
560 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
562 if (safe_delete)
563 query_set_sel (1);
565 ctx->recursive_result =
566 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
567 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
569 if (ctx->recursive_result != RECURSIVE_ABORT)
570 do_refresh ();
571 g_free (text);
574 switch (ctx->recursive_result)
576 case RECURSIVE_YES:
577 case RECURSIVE_ALWAYS:
578 return FILE_CONT;
580 case RECURSIVE_NO:
581 case RECURSIVE_NEVER:
582 return FILE_SKIP;
584 case RECURSIVE_ABORT:
585 default:
586 return FILE_ABORT;
590 /* --------------------------------------------------------------------------------------------- */
592 #ifdef WITH_BACKGROUND
593 static FileProgressStatus
594 do_file_error (const char *str)
596 union
598 void *p;
599 FileProgressStatus (*f) (enum OperationMode, const char *);
600 } pntr;
601 pntr.f = real_do_file_error;
603 if (mc_global.we_are_background)
604 return parent_call (pntr.p, NULL, 1, strlen (str), str);
605 else
606 return real_do_file_error (Foreground, str);
609 /* --------------------------------------------------------------------------------------------- */
611 static FileProgressStatus
612 query_recursive (FileOpContext * ctx, const char *s)
614 union
616 void *p;
617 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
618 } pntr;
619 pntr.f = real_query_recursive;
621 if (mc_global.we_are_background)
622 return parent_call (pntr.p, ctx, 1, strlen (s), s);
623 else
624 return real_query_recursive (ctx, Foreground, s);
627 /* --------------------------------------------------------------------------------------------- */
629 static FileProgressStatus
630 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
631 struct stat *_d_stat)
633 union
635 void *p;
636 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
637 struct stat *, struct stat *);
638 } pntr;
639 pntr.f = file_progress_real_query_replace;
641 if (mc_global.we_are_background)
642 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
643 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
644 else
645 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
648 #else
649 /* --------------------------------------------------------------------------------------------- */
651 static FileProgressStatus
652 do_file_error (const char *str)
654 return real_do_file_error (Foreground, str);
657 /* --------------------------------------------------------------------------------------------- */
659 static FileProgressStatus
660 query_recursive (FileOpContext * ctx, const char *s)
662 return real_query_recursive (ctx, Foreground, s);
665 /* --------------------------------------------------------------------------------------------- */
667 static FileProgressStatus
668 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
669 struct stat *_d_stat)
671 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
674 #endif /* !WITH_BACKGROUND */
676 /* --------------------------------------------------------------------------------------------- */
677 /** Report error with two files */
679 static FileProgressStatus
680 files_error (const char *format, const char *file1, const char *file2)
682 char buf[BUF_MEDIUM];
683 char *nfile1 = g_strdup (path_trunc (file1, 15));
684 char *nfile2 = g_strdup (path_trunc (file2, 15));
686 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
688 g_free (nfile1);
689 g_free (nfile2);
691 return do_file_error (buf);
694 /* }}} */
696 /* --------------------------------------------------------------------------------------------- */
698 static void
699 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
700 struct timeval tv_current, struct timeval tv_transfer_start,
701 off_t file_size, off_t n_read_total)
703 long dt;
705 /* 1. Update rotating dash after some time */
706 rotate_dash ();
708 /* 3. Compute ETA */
709 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
711 if (n_read_total)
713 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
714 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
716 else
717 ctx->eta_secs = 0.0;
719 /* 4. Compute BPS rate */
720 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
721 if (ctx->bps_time < 1)
722 ctx->bps_time = 1;
723 ctx->bps = n_read_total / ctx->bps_time;
725 /* 5. Compute total ETA and BPS */
726 if (ctx->progress_bytes != 0)
728 uintmax_t remain_bytes;
729 tctx->copyed_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
730 remain_bytes = ctx->progress_bytes - tctx->copyed_bytes;
731 #if 1
733 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
735 if (total_secs < 1)
736 total_secs = 1;
738 tctx->bps = tctx->copyed_bytes / total_secs;
739 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
741 #else
742 /* broken on lot of little files */
743 tctx->bps_count++;
744 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
745 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
746 #endif
750 /* --------------------------------------------------------------------------------------------- */
752 /* {{{ Move routines */
753 static FileProgressStatus
754 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
756 struct stat src_stats, dst_stats;
757 FileProgressStatus return_status = FILE_CONT;
758 gboolean copy_done = FALSE;
759 gboolean old_ask_overwrite;
761 file_progress_show_source (ctx, s);
762 file_progress_show_target (ctx, d);
763 if (check_progress_buttons (ctx) == FILE_ABORT)
764 return FILE_ABORT;
766 mc_refresh ();
768 while (mc_lstat (s, &src_stats) != 0)
770 /* Source doesn't exist */
771 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
772 if (return_status != FILE_RETRY)
773 return return_status;
776 if (mc_lstat (d, &dst_stats) == 0)
778 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
779 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
781 if (S_ISDIR (dst_stats.st_mode))
783 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
784 do_refresh ();
785 return FILE_SKIP;
788 if (confirm_overwrite)
790 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
791 if (return_status != FILE_CONT)
792 return return_status;
794 /* Ok to overwrite */
797 if (!ctx->do_append)
799 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
801 return_status = make_symlink (ctx, s, d);
802 if (return_status == FILE_CONT)
803 goto retry_src_remove;
804 else
805 return return_status;
808 if (mc_rename (s, d) == 0)
809 return progress_update_one (tctx, ctx, src_stats.st_size, TRUE);
811 #if 0
812 /* Comparison to EXDEV seems not to work in nfs if you're moving from
813 one nfs to the same, but on the server it is on two different
814 filesystems. Then nfs returns EIO instead of EXDEV.
815 Hope it will not hurt if we always in case of error try to copy/delete. */
816 else
817 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
819 if (errno != EXDEV)
821 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
822 if (return_status == FILE_RETRY)
823 goto retry_rename;
824 return return_status;
826 #endif
828 /* Failed because filesystem boundary -> copy the file instead */
829 old_ask_overwrite = tctx->ask_overwrite;
830 tctx->ask_overwrite = FALSE;
831 return_status = copy_file_file (tctx, ctx, s, d);
832 tctx->ask_overwrite = old_ask_overwrite;
833 if (return_status != FILE_CONT)
834 return return_status;
836 copy_done = TRUE;
838 file_progress_show_source (ctx, NULL);
839 file_progress_show (ctx, 0, 0, "", FALSE);
841 return_status = check_progress_buttons (ctx);
842 if (return_status != FILE_CONT)
843 return return_status;
845 mc_refresh ();
847 retry_src_remove:
848 if (mc_unlink (s))
850 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
851 if (return_status == FILE_RETRY)
852 goto retry_src_remove;
853 return return_status;
856 if (!copy_done)
857 return_status = progress_update_one (tctx, ctx, src_stats.st_size, TRUE);
859 return return_status;
862 /* }}} */
864 /* --------------------------------------------------------------------------------------------- */
865 /* {{{ Erase routines */
866 /** Don't update progress status if progress_count==NULL */
868 static FileProgressStatus
869 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s,
870 gboolean is_toplevel_file)
872 int return_status;
873 struct stat buf;
875 file_progress_show_deleting (ctx, s);
876 if (check_progress_buttons (ctx) == FILE_ABORT)
877 return FILE_ABORT;
878 mc_refresh ();
880 if (tctx->progress_count != 0 && mc_lstat (s, &buf) != 0)
882 /* ignore, most likely the mc_unlink fails, too */
883 buf.st_size = 0;
886 while (mc_unlink (s) != 0 && !ctx->skip_all)
888 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
889 if (return_status == FILE_ABORT)
890 return return_status;
891 if (return_status == FILE_RETRY)
892 continue;
893 if (return_status == FILE_SKIPALL)
894 ctx->skip_all = TRUE;
895 break;
898 if (tctx->progress_count == 0)
899 return FILE_CONT;
900 return progress_update_one (tctx, ctx, buf.st_size, is_toplevel_file);
903 /* --------------------------------------------------------------------------------------------- */
906 Recursive remove of files
907 abort->cancel stack
908 skip ->warn every level, gets default
909 skipall->remove as much as possible
911 static FileProgressStatus
912 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
914 struct dirent *next;
915 struct stat buf;
916 DIR *reading;
917 char *path;
918 FileProgressStatus return_status = FILE_CONT;
920 if (!strcmp (s, ".."))
921 return FILE_RETRY;
923 reading = mc_opendir (s);
925 if (!reading)
926 return FILE_RETRY;
928 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
930 if (!strcmp (next->d_name, "."))
931 continue;
932 if (!strcmp (next->d_name, ".."))
933 continue;
934 path = concat_dir_and_file (s, next->d_name);
935 if (mc_lstat (path, &buf))
937 g_free (path);
938 mc_closedir (reading);
939 return FILE_RETRY;
941 if (S_ISDIR (buf.st_mode))
942 return_status = recursive_erase (tctx, ctx, path);
943 else
944 return_status = erase_file (tctx, ctx, path, 0);
945 g_free (path);
947 mc_closedir (reading);
948 if (return_status == FILE_ABORT)
949 return return_status;
950 file_progress_show_deleting (ctx, s);
951 if (check_progress_buttons (ctx) == FILE_ABORT)
952 return FILE_ABORT;
953 mc_refresh ();
955 while (my_rmdir (s) != 0 && !ctx->skip_all)
957 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
958 if (return_status == FILE_RETRY)
959 continue;
960 if (return_status == FILE_ABORT)
961 return return_status;
962 if (return_status == FILE_SKIPALL)
963 ctx->skip_all = TRUE;
964 break;
967 return FILE_CONT;
970 /* --------------------------------------------------------------------------------------------- */
971 /** Return -1 on error, 1 if there are no entries besides "." and ".."
972 in the directory path points to, 0 else. */
974 static int
975 check_dir_is_empty (const char *path)
977 DIR *dir;
978 struct dirent *d;
979 int i;
981 dir = mc_opendir (path);
982 if (!dir)
983 return -1;
985 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
987 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
988 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
989 continue; /* "." or ".." */
990 i = 0;
991 break;
994 mc_closedir (dir);
995 return i;
998 /* --------------------------------------------------------------------------------------------- */
1000 static FileProgressStatus
1001 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1003 FileProgressStatus error;
1005 if (strcmp (s, "..") == 0)
1006 return FILE_SKIP;
1008 if (strcmp (s, ".") == 0)
1009 return FILE_SKIP;
1011 file_progress_show_deleting (ctx, s);
1012 if (check_progress_buttons (ctx) == FILE_ABORT)
1013 return FILE_ABORT;
1014 mc_refresh ();
1016 if (1 != check_dir_is_empty (s)) /* not empty or error */
1017 return FILE_CONT;
1019 while (my_rmdir (s))
1021 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1022 if (error != FILE_RETRY)
1023 return error;
1026 return FILE_CONT;
1029 /* }}} */
1031 /* --------------------------------------------------------------------------------------------- */
1032 /* {{{ Panel operate routines */
1035 * Return currently selected entry name or the name of the first marked
1036 * entry if there is one.
1039 static char *
1040 panel_get_file (WPanel * panel, struct stat *stat_buf)
1042 int i;
1044 if (get_current_type () == view_tree)
1046 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
1047 char *tree_name = tree_selected_name (tree);
1049 mc_stat (tree_name, stat_buf);
1050 return tree_name;
1053 if (panel->marked)
1055 for (i = 0; i < panel->count; i++)
1056 if (panel->dir.list[i].f.marked)
1058 *stat_buf = panel->dir.list[i].st;
1059 return panel->dir.list[i].fname;
1062 else
1064 *stat_buf = panel->dir.list[panel->selected].st;
1065 return panel->dir.list[panel->selected].fname;
1067 g_assert_not_reached ();
1068 return NULL;
1071 /* --------------------------------------------------------------------------------------------- */
1073 * panel_compute_totals:
1075 * compute the number of files and the number of bytes
1076 * used up by the whole selection, recursing directories
1077 * as required. In addition, it checks to see if it will
1078 * overwrite any files by doing the copy.
1081 static FileProgressStatus
1082 panel_compute_totals (const WPanel * panel, const void *ui,
1083 compute_dir_size_callback cback,
1084 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1086 int i;
1088 *ret_marked = 0;
1089 *ret_total = 0;
1091 for (i = 0; i < panel->count; i++)
1093 struct stat *s;
1095 if (!panel->dir.list[i].f.marked)
1096 continue;
1098 s = &panel->dir.list[i].st;
1100 if (S_ISDIR (s->st_mode))
1102 char *dir_name;
1103 size_t subdir_count = 0;
1104 uintmax_t subdir_bytes = 0;
1105 FileProgressStatus status;
1107 dir_name = concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1109 status = compute_dir_size (dir_name, ui, cback,
1110 &subdir_count, &subdir_bytes, compute_symlinks);
1111 g_free (dir_name);
1113 if (status != FILE_CONT)
1114 return FILE_ABORT;
1116 *ret_marked += subdir_count;
1117 *ret_total += subdir_bytes;
1119 else
1121 (*ret_marked)++;
1122 *ret_total += (uintmax_t) s->st_size;
1126 return FILE_CONT;
1129 /* --------------------------------------------------------------------------------------------- */
1131 /** Initialize variables for progress bars */
1132 static FileProgressStatus
1133 panel_operate_init_totals (FileOperation operation,
1134 const WPanel * panel, const char *source, FileOpContext * ctx)
1136 FileProgressStatus status;
1138 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1140 ComputeDirSizeUI *ui;
1142 ui = compute_dir_size_create_ui ();
1144 if (source != NULL)
1145 status = compute_dir_size (source, ui, compute_dir_size_update_ui,
1146 &ctx->progress_count, &ctx->progress_bytes,
1147 ctx->follow_links);
1148 else
1149 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1150 &ctx->progress_count, &ctx->progress_bytes,
1151 ctx->follow_links);
1153 compute_dir_size_destroy_ui (ui);
1155 ctx->progress_totals_computed = (status == FILE_CONT);
1157 else
1159 status = FILE_CONT;
1160 ctx->progress_count = panel->marked;
1161 ctx->progress_bytes = panel->total;
1162 ctx->progress_totals_computed = FALSE;
1165 return status;
1168 /* --------------------------------------------------------------------------------------------- */
1170 * Generate user prompt for panel operation.
1171 * single_source is the name if the source entry or NULL for multiple
1172 * entries.
1173 * src_stat is only used when single_source is not NULL.
1176 static char *
1177 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1178 gboolean single_source, const struct stat *src_stat)
1180 const char *sp, *cp;
1181 char format_string[BUF_MEDIUM];
1182 char *dp = format_string;
1183 gboolean build_question = FALSE;
1185 static gboolean i18n_flag = FALSE;
1186 if (!i18n_flag)
1188 size_t i;
1190 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1191 op_names1[i] = Q_ (op_names1[i]);
1193 #ifdef ENABLE_NLS
1194 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1195 prompt_parts[i] = _(prompt_parts[i]);
1197 one_format = _(one_format);
1198 many_format = _(many_format);
1199 question_format = _(question_format);
1200 #endif /* ENABLE_NLS */
1201 i18n_flag = TRUE;
1204 sp = single_source ? one_format : many_format;
1206 while (*sp != '\0')
1208 switch (*sp)
1210 case '%':
1211 cp = NULL;
1212 switch (sp[1])
1214 case 'o':
1215 cp = op_names1[operation];
1216 break;
1217 case 'm':
1218 if (operation == OP_DELETE)
1220 cp = "";
1221 build_question = TRUE;
1223 else
1224 cp = prompt_parts[5];
1225 break;
1226 case 'e':
1227 if (operation == OP_DELETE)
1229 cp = "";
1230 build_question = TRUE;
1232 else
1233 cp = prompt_parts[6];
1234 break;
1235 case 'f':
1236 if (single_source)
1237 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1238 else
1239 cp = (panel->marked == panel->dirs_marked)
1240 ? prompt_parts[3]
1241 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1242 break;
1243 default:
1244 *dp++ = *sp++;
1247 if (cp != NULL)
1249 sp += 2;
1250 while (*cp != '\0')
1251 *dp++ = *cp++;
1253 break;
1254 default:
1255 *dp++ = *sp++;
1258 *dp = '\0';
1260 if (build_question)
1262 char tmp[BUF_MEDIUM];
1264 memmove (tmp, format_string, sizeof (tmp));
1265 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1268 return g_strdup (format_string);
1271 /* --------------------------------------------------------------------------------------------- */
1273 #ifdef WITH_BACKGROUND
1274 static int
1275 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1277 int pid = ctx->pid;
1279 (void) mode;
1280 ctx->pid = 0;
1282 unregister_task_with_pid (pid);
1283 /* file_op_context_destroy(ctx); */
1284 return 1;
1286 #endif
1287 /* }}} */
1289 /* --------------------------------------------------------------------------------------------- */
1290 /*** public functions ****************************************************************************/
1291 /* --------------------------------------------------------------------------------------------- */
1293 FileProgressStatus
1294 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1295 const char *src_path, const char *dst_path)
1297 uid_t src_uid = (uid_t) - 1;
1298 gid_t src_gid = (gid_t) - 1;
1300 int src_desc, dest_desc = -1;
1301 int n_read, n_written;
1302 mode_t src_mode = 0; /* The mode of the source file */
1303 struct stat sb, sb2;
1304 struct utimbuf utb;
1305 gboolean dst_exists = FALSE, appending = FALSE;
1306 off_t n_read_total = 0, file_size = -1;
1307 FileProgressStatus return_status, temp_status;
1308 struct timeval tv_transfer_start;
1309 dest_status_t dst_status = DEST_NONE;
1310 int open_flags;
1311 gboolean is_first_time = TRUE;
1313 /* FIXME: We should not be using global variables! */
1314 ctx->do_reget = 0;
1315 return_status = FILE_RETRY;
1317 file_progress_show_source (ctx, src_path);
1318 file_progress_show_target (ctx, dst_path);
1319 if (check_progress_buttons (ctx) == FILE_ABORT)
1320 return FILE_ABORT;
1322 mc_refresh ();
1324 while (mc_stat (dst_path, &sb2) == 0)
1326 if (S_ISDIR (sb2.st_mode))
1328 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1329 if (return_status == FILE_RETRY)
1330 continue;
1331 return return_status;
1333 dst_exists = TRUE;
1334 break;
1337 while ((*ctx->stat_func) (src_path, &sb))
1339 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1340 if (return_status != FILE_RETRY)
1341 return return_status;
1344 if (dst_exists)
1346 /* Destination already exists */
1347 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1348 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), src_path, dst_path);
1349 /* Should we replace destination? */
1350 if (tctx->ask_overwrite)
1352 ctx->do_reget = 0;
1353 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1354 if (return_status != FILE_CONT)
1355 return return_status;
1359 if (!ctx->do_append)
1361 /* Check the hardlinks */
1362 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb) == 1)
1364 /* We have made a hardlink - no more processing is necessary */
1365 return FILE_CONT;
1368 if (S_ISLNK (sb.st_mode))
1369 return make_symlink (ctx, src_path, dst_path);
1371 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1372 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1374 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1375 && !ctx->skip_all)
1377 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1378 if (return_status == FILE_RETRY)
1379 continue;
1380 if (return_status == FILE_SKIPALL)
1381 ctx->skip_all = TRUE;
1382 return return_status;
1384 /* Success */
1386 while (ctx->preserve_uidgid && mc_chown (dst_path, sb.st_uid, sb.st_gid) != 0
1387 && !ctx->skip_all)
1389 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1390 if (temp_status == FILE_SKIPALL)
1391 ctx->skip_all = TRUE;
1392 if (temp_status == FILE_SKIP)
1393 break;
1394 if (temp_status != FILE_RETRY)
1395 return temp_status;
1398 while (ctx->preserve && mc_chmod (dst_path, sb.st_mode & ctx->umask_kill) != 0
1399 && !ctx->skip_all)
1401 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1402 if (temp_status == FILE_SKIPALL)
1403 ctx->skip_all = TRUE;
1404 if (temp_status == FILE_SKIP)
1405 break;
1406 if (temp_status != FILE_RETRY)
1407 return temp_status;
1410 return FILE_CONT;
1414 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1416 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1418 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1419 if (return_status == FILE_RETRY)
1420 continue;
1421 if (return_status == FILE_SKIPALL)
1422 ctx->skip_all = TRUE;
1423 if (return_status == FILE_SKIP)
1424 break;
1425 ctx->do_append = 0;
1426 return return_status;
1429 if (ctx->do_reget != 0)
1431 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1433 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1434 ctx->do_reget = 0;
1435 ctx->do_append = FALSE;
1439 while (mc_fstat (src_desc, &sb) != 0 && !ctx->skip_all)
1441 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1442 if (return_status == FILE_RETRY)
1443 continue;
1444 if (return_status == FILE_SKIPALL)
1445 ctx->skip_all = TRUE;
1446 ctx->do_append = FALSE;
1447 goto ret;
1449 src_mode = sb.st_mode;
1450 src_uid = sb.st_uid;
1451 src_gid = sb.st_gid;
1452 utb.actime = sb.st_atime;
1453 utb.modtime = sb.st_mtime;
1454 file_size = sb.st_size;
1456 open_flags = O_WRONLY;
1457 if (dst_exists)
1459 if (ctx->do_append != 0)
1460 open_flags |= O_APPEND;
1461 else
1462 open_flags |= O_CREAT | O_TRUNC;
1464 else
1466 open_flags |= O_CREAT | O_EXCL;
1469 while ((dest_desc = mc_open (dst_path, open_flags, src_mode)) < 0)
1471 if (errno == EEXIST || ctx->skip_all)
1472 goto ret;
1474 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1475 if (return_status == FILE_RETRY)
1476 continue;
1477 if (return_status == FILE_SKIPALL)
1478 ctx->skip_all = TRUE;
1479 ctx->do_append = FALSE;
1480 goto ret;
1482 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1484 appending = ctx->do_append;
1485 ctx->do_append = FALSE;
1487 /* Find out the optimal buffer size. */
1488 while (mc_fstat (dest_desc, &sb) != 0 && !ctx->skip_all)
1490 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1491 if (return_status == FILE_RETRY)
1492 continue;
1493 if (return_status == FILE_SKIPALL)
1494 ctx->skip_all = TRUE;
1495 goto ret;
1498 ctx->eta_secs = 0.0;
1499 ctx->bps = 0;
1501 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1502 file_progress_show (ctx, 0, file_size, "", TRUE);
1503 else
1504 file_progress_show (ctx, 1, 1, "", TRUE);
1505 return_status = check_progress_buttons (ctx);
1506 mc_refresh ();
1508 if (return_status != FILE_CONT)
1509 goto ret;
1512 struct timeval tv_current, tv_last_update, tv_last_input;
1513 int secs, update_secs;
1514 const char *stalled_msg = "";
1516 tv_last_update = tv_transfer_start;
1518 for (;;)
1520 char buf[BUF_8K];
1522 /* src_read */
1523 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1524 n_read = -1;
1525 else
1526 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1528 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1529 if (return_status == FILE_RETRY)
1530 continue;
1531 if (return_status == FILE_SKIPALL)
1532 ctx->skip_all = TRUE;
1533 goto ret;
1535 if (n_read == 0)
1536 break;
1538 gettimeofday (&tv_current, NULL);
1540 if (n_read > 0)
1542 char *t = buf;
1543 n_read_total += n_read;
1545 /* Windows NT ftp servers report that files have no
1546 * permissions: -------, so if we happen to have actually
1547 * read something, we should fix the permissions.
1549 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1550 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1551 gettimeofday (&tv_last_input, NULL);
1553 /* dst_write */
1554 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read && !ctx->skip_all)
1556 if (n_written > 0)
1558 n_read -= n_written;
1559 t += n_written;
1560 continue;
1562 return_status = file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1563 if (return_status == FILE_SKIPALL)
1564 ctx->skip_all = TRUE;
1565 if (return_status == FILE_SKIP)
1566 break;
1567 if (return_status != FILE_RETRY)
1568 goto ret;
1571 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1572 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1574 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1576 copy_file_file_display_progress (tctx, ctx,
1577 tv_current,
1578 tv_transfer_start, file_size, n_read_total);
1579 tv_last_update = tv_current;
1581 is_first_time = FALSE;
1583 if (update_secs > FILEOP_STALLING_INTERVAL)
1585 stalled_msg = _("(stalled)");
1589 gboolean force_update;
1591 force_update =
1592 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1594 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1596 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1597 file_progress_show_total (tctx, ctx,
1598 tctx->progress_bytes + n_read_total + ctx->do_reget,
1599 force_update);
1602 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1603 force_update);
1605 mc_refresh ();
1607 return_status = check_progress_buttons (ctx);
1609 if (return_status != FILE_CONT)
1611 mc_refresh ();
1612 goto ret;
1617 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1619 ret:
1620 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1622 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1623 if (temp_status == FILE_RETRY)
1624 continue;
1625 if (temp_status == FILE_ABORT)
1626 return_status = temp_status;
1627 if (temp_status == FILE_SKIPALL)
1628 ctx->skip_all = TRUE;
1629 break;
1632 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1634 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1635 if (temp_status == FILE_RETRY)
1636 continue;
1637 if (temp_status == FILE_SKIPALL)
1638 ctx->skip_all = TRUE;
1639 return_status = temp_status;
1640 break;
1643 if (dst_status == DEST_SHORT)
1645 /* Remove short file */
1646 int result;
1647 result = query_dialog (Q_ ("DialogTitle|Copy"),
1648 _("Incomplete file was retrieved. Keep it?"),
1649 D_ERROR, 2, _("&Delete"), _("&Keep"));
1650 if (result == 0)
1651 mc_unlink (dst_path);
1653 else if (dst_status == DEST_FULL)
1655 /* Copy has succeeded */
1656 if (!appending && ctx->preserve_uidgid)
1658 while (mc_chown (dst_path, src_uid, src_gid) != 0 && !ctx->skip_all)
1660 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1661 if (temp_status == FILE_RETRY)
1662 continue;
1663 if (temp_status == FILE_SKIPALL)
1665 ctx->skip_all = TRUE;
1666 return_status = FILE_CONT;
1668 if (temp_status == FILE_SKIP)
1669 return_status = FILE_CONT;
1670 break;
1674 if (!appending)
1676 if (ctx->preserve)
1678 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1680 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1681 if (temp_status == FILE_RETRY)
1682 continue;
1683 if (temp_status == FILE_SKIPALL)
1685 ctx->skip_all = TRUE;
1686 return_status = FILE_CONT;
1688 if (temp_status == FILE_SKIP)
1689 return_status = FILE_CONT;
1690 break;
1693 else
1695 src_mode = umask (-1);
1696 umask (src_mode);
1697 src_mode = 0100666 & ~src_mode;
1698 mc_chmod (dst_path, (src_mode & ctx->umask_kill));
1700 mc_utime (dst_path, &utb);
1704 if (return_status == FILE_CONT)
1705 return_status = progress_update_one (tctx, ctx, file_size, tctx->is_toplevel_file);
1707 return return_status;
1710 /* --------------------------------------------------------------------------------------------- */
1712 * I think these copy_*_* functions should have a return type.
1713 * anyway, this function *must* have two directories as arguments.
1715 /* FIXME: This function needs to check the return values of the
1716 function calls */
1718 FileProgressStatus
1719 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1720 gboolean toplevel, gboolean move_over, gboolean do_delete, struct link * parent_dirs)
1722 struct dirent *next;
1723 struct stat buf, cbuf;
1724 DIR *reading;
1725 char *dest_dir = NULL;
1726 FileProgressStatus return_status = FILE_CONT;
1727 struct utimbuf utb;
1728 struct link *lp;
1729 char *d;
1731 d = g_strdup (_d);
1733 /* First get the mode of the source dir */
1734 retry_src_stat:
1735 if ((*ctx->stat_func) (s, &cbuf))
1737 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1738 if (return_status == FILE_RETRY)
1739 goto retry_src_stat;
1740 goto ret_fast;
1743 if (is_in_linklist (dest_dirs, s, &cbuf))
1745 /* Don't copy a directory we created before (we don't want to copy
1746 infinitely if a directory is copied into itself) */
1747 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1748 return_status = FILE_CONT;
1749 goto ret_fast;
1752 /* Hmm, hardlink to directory??? - Norbert */
1753 /* FIXME: In this step we should do something
1754 in case the destination already exist */
1755 /* Check the hardlinks */
1756 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf) == 1)
1758 /* We have made a hardlink - no more processing is necessary */
1759 goto ret_fast;
1762 if (!S_ISDIR (cbuf.st_mode))
1764 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1765 if (return_status == FILE_RETRY)
1766 goto retry_src_stat;
1767 goto ret_fast;
1770 if (is_in_linklist (parent_dirs, s, &cbuf))
1772 /* we found a cyclic symbolic link */
1773 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
1774 return_status = FILE_SKIP;
1775 goto ret_fast;
1778 lp = g_new (struct link, 1);
1780 vfs_path_t *vpath = vfs_path_from_str (s);
1781 lp->vfs = vfs_path_get_by_index (vpath, -1)->class;
1782 vfs_path_free (vpath);
1784 lp->ino = cbuf.st_ino;
1785 lp->dev = cbuf.st_dev;
1786 lp->next = parent_dirs;
1787 parent_dirs = lp;
1789 retry_dst_stat:
1790 /* Now, check if the dest dir exists, if not, create it. */
1791 if (mc_stat (d, &buf))
1793 /* Here the dir doesn't exist : make it ! */
1794 if (move_over)
1796 if (mc_rename (s, d) == 0)
1798 return_status = FILE_CONT;
1799 goto ret;
1802 dest_dir = d;
1803 d = NULL;
1805 else
1808 * If the destination directory exists, we want to copy the whole
1809 * directory, but we only want this to happen once.
1811 * Escape sequences added to the * to compiler warnings.
1812 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
1813 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
1815 if (!S_ISDIR (buf.st_mode))
1817 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
1818 if (return_status == FILE_RETRY)
1819 goto retry_dst_stat;
1820 goto ret;
1822 /* Dive into subdir if exists */
1823 if (toplevel && ctx->dive_into_subdirs)
1825 dest_dir = concat_dir_and_file (d, x_basename (s));
1827 else
1829 dest_dir = d;
1830 d = NULL;
1831 goto dont_mkdir;
1834 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU))
1836 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
1837 if (return_status != FILE_RETRY)
1838 goto ret;
1841 lp = g_new (struct link, 1);
1842 mc_stat (dest_dir, &buf);
1844 vfs_path_t *vpath = vfs_path_from_str (dest_dir);
1845 lp->vfs = vfs_path_get_by_index (vpath, -1)->class;
1846 vfs_path_free (vpath);
1848 lp->ino = buf.st_ino;
1849 lp->dev = buf.st_dev;
1850 lp->next = dest_dirs;
1851 dest_dirs = lp;
1853 if (ctx->preserve_uidgid)
1855 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid))
1857 return_status = file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
1858 if (return_status != FILE_RETRY)
1859 goto ret;
1863 dont_mkdir:
1864 /* open the source dir for reading */
1865 reading = mc_opendir (s);
1866 if (reading == NULL)
1867 goto ret;
1869 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1871 char *path;
1873 * Now, we don't want '.' and '..' to be created / copied at any time
1875 if (!strcmp (next->d_name, "."))
1876 continue;
1877 if (!strcmp (next->d_name, ".."))
1878 continue;
1880 /* get the filename and add it to the src directory */
1881 path = concat_dir_and_file (s, next->d_name);
1883 (*ctx->stat_func) (path, &buf);
1884 if (S_ISDIR (buf.st_mode))
1886 char *mdpath;
1888 mdpath = concat_dir_and_file (dest_dir, next->d_name);
1890 * From here, we just intend to recursively copy subdirs, not
1891 * the double functionality of copying different when the target
1892 * dir already exists. So, we give the recursive call the flag 0
1893 * meaning no toplevel.
1895 return_status =
1896 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
1897 g_free (mdpath);
1899 else
1901 char *dest_file;
1903 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
1904 return_status = copy_file_file (tctx, ctx, path, dest_file);
1905 g_free (dest_file);
1907 if (do_delete && return_status == FILE_CONT)
1909 if (ctx->erase_at_end)
1911 static struct link *tail;
1912 size_t len = strlen (path);
1913 lp = g_malloc (sizeof (struct link) + len);
1914 strncpy (lp->name, path, len + 1);
1915 lp->st_mode = buf.st_mode;
1916 lp->next = NULL;
1917 if (erase_list != NULL)
1919 tail->next = lp;
1920 tail = lp;
1922 else
1923 erase_list = tail = lp;
1925 else
1927 if (S_ISDIR (buf.st_mode))
1929 return_status = erase_dir_iff_empty (ctx, path);
1931 else
1932 return_status = erase_file (tctx, ctx, path, FALSE);
1935 g_free (path);
1937 mc_closedir (reading);
1939 if (ctx->preserve)
1941 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1942 utb.actime = cbuf.st_atime;
1943 utb.modtime = cbuf.st_mtime;
1944 mc_utime (dest_dir, &utb);
1946 else
1948 cbuf.st_mode = umask (-1);
1949 umask (cbuf.st_mode);
1950 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
1951 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1954 ret:
1955 g_free (dest_dir);
1956 g_free (parent_dirs);
1957 ret_fast:
1958 g_free (d);
1959 return return_status;
1962 /* }}} */
1964 /* --------------------------------------------------------------------------------------------- */
1965 /* {{{ Move routines */
1967 FileProgressStatus
1968 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
1970 struct stat sbuf, dbuf, destbuf;
1971 struct link *lp;
1972 char *destdir;
1973 FileProgressStatus return_status;
1974 gboolean move_over = FALSE;
1975 gboolean dstat_ok;
1977 file_progress_show_source (ctx, s);
1978 file_progress_show_target (ctx, d);
1979 if (check_progress_buttons (ctx) == FILE_ABORT)
1980 return FILE_ABORT;
1982 mc_refresh ();
1984 mc_stat (s, &sbuf);
1985 dstat_ok = (mc_stat (d, &dbuf) == 0);
1987 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
1988 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
1990 if (!dstat_ok)
1991 destdir = g_strdup (d); /* destination doesn't exist */
1992 else if (!ctx->dive_into_subdirs)
1994 destdir = g_strdup (d);
1995 move_over = TRUE;
1997 else
1998 destdir = concat_dir_and_file (d, x_basename (s));
2000 /* Check if the user inputted an existing dir */
2001 retry_dst_stat:
2002 if (!mc_stat (destdir, &destbuf))
2004 if (move_over)
2006 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2008 if (return_status != FILE_CONT)
2009 goto ret;
2010 goto oktoret;
2012 else
2014 if (S_ISDIR (destbuf.st_mode))
2015 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2016 else
2017 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2018 if (return_status == FILE_RETRY)
2019 goto retry_dst_stat;
2021 g_free (destdir);
2022 return return_status;
2025 retry_rename:
2026 if (mc_rename (s, destdir) == 0)
2028 return_status = FILE_CONT;
2029 goto ret;
2032 if (errno != EXDEV)
2034 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2035 if (return_status == FILE_RETRY)
2036 goto retry_rename;
2037 goto ret;
2039 /* Failed because of filesystem boundary -> copy dir instead */
2040 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2042 if (return_status != FILE_CONT)
2043 goto ret;
2044 oktoret:
2045 file_progress_show_source (ctx, NULL);
2046 file_progress_show (ctx, 0, 0, "", FALSE);
2048 return_status = check_progress_buttons (ctx);
2049 if (return_status != FILE_CONT)
2050 goto ret;
2052 mc_refresh ();
2053 if (ctx->erase_at_end)
2055 for (; erase_list && return_status != FILE_ABORT;)
2057 if (S_ISDIR (erase_list->st_mode))
2059 return_status = erase_dir_iff_empty (ctx, erase_list->name);
2061 else
2062 return_status = erase_file (tctx, ctx, erase_list->name, FALSE);
2063 lp = erase_list;
2064 erase_list = erase_list->next;
2065 g_free (lp);
2068 erase_dir_iff_empty (ctx, s);
2070 ret:
2071 g_free (destdir);
2072 while (erase_list)
2074 lp = erase_list;
2075 erase_list = erase_list->next;
2076 g_free (lp);
2078 return return_status;
2081 /* }}} */
2083 /* --------------------------------------------------------------------------------------------- */
2084 /* {{{ Erase routines */
2086 FileProgressStatus
2087 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
2089 FileProgressStatus error;
2091 if (strcmp (s, "..") == 0)
2092 return FILE_SKIP;
2094 if (strcmp (s, ".") == 0)
2095 return FILE_SKIP;
2097 file_progress_show_deleting (ctx, s);
2098 if (check_progress_buttons (ctx) == FILE_ABORT)
2099 return FILE_ABORT;
2100 mc_refresh ();
2102 /* The old way to detect a non empty directory was:
2103 error = my_rmdir (s);
2104 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2105 For the linux user space nfs server (nfs-server-2.2beta29-2)
2106 we would have to check also for EIO. I hope the new way is
2107 fool proof. (Norbert)
2109 error = check_dir_is_empty (s);
2110 if (error == 0)
2111 { /* not empty */
2112 error = query_recursive (ctx, s);
2113 if (error == FILE_CONT)
2114 return recursive_erase (tctx, ctx, s);
2115 else
2116 return error;
2119 while (my_rmdir (s) == -1)
2121 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2122 if (error != FILE_RETRY)
2123 return error;
2126 return FILE_CONT;
2129 /* }}} */
2131 /* --------------------------------------------------------------------------------------------- */
2132 /* {{{ Panel operate routines */
2134 ComputeDirSizeUI *
2135 compute_dir_size_create_ui (void)
2137 ComputeDirSizeUI *ui;
2139 const char *b_name = N_("&Abort");
2141 #ifdef ENABLE_NLS
2142 b_name = _(b_name);
2143 #endif
2145 ui = g_new (ComputeDirSizeUI, 1);
2147 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
2148 NULL, _("Directory scanning"), DLG_CENTER);
2149 ui->dirname = label_new (3, 3, "");
2150 add_widget (ui->dlg, ui->dirname);
2152 add_widget (ui->dlg,
2153 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
2154 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2156 /* We will manage the dialog without any help,
2157 that's why we have to call init_dlg */
2158 init_dlg (ui->dlg);
2160 return ui;
2163 /* --------------------------------------------------------------------------------------------- */
2165 void
2166 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2168 if (ui != NULL)
2170 /* schedule to update passive panel */
2171 other_panel->dirty = 1;
2173 /* close and destroy dialog */
2174 dlg_run_done (ui->dlg);
2175 destroy_dlg (ui->dlg);
2176 g_free (ui);
2180 /* --------------------------------------------------------------------------------------------- */
2182 FileProgressStatus
2183 compute_dir_size_update_ui (const void *ui, const char *dirname)
2185 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2186 int c;
2187 Gpm_Event event;
2189 if (ui == NULL)
2190 return FILE_CONT;
2192 label_set_text (this->dirname, str_trunc (dirname, this->dlg->cols - 6));
2194 event.x = -1; /* Don't show the GPM cursor */
2195 c = tty_get_event (&event, FALSE, FALSE);
2196 if (c == EV_NONE)
2197 return FILE_CONT;
2199 /* Reinitialize to avoid old values after events other than
2200 selecting a button */
2201 this->dlg->ret_value = FILE_CONT;
2203 dlg_process_event (this->dlg, c, &event);
2205 switch (this->dlg->ret_value)
2207 case B_CANCEL:
2208 case FILE_ABORT:
2209 return FILE_ABORT;
2210 default:
2211 return FILE_CONT;
2215 /* --------------------------------------------------------------------------------------------- */
2217 * compute_dir_size:
2219 * Computes the number of bytes used by the files in a directory
2222 FileProgressStatus
2223 compute_dir_size (const char *dirname, const void *ui,
2224 compute_dir_size_callback cback,
2225 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2227 int res;
2228 struct stat s;
2229 DIR *dir;
2230 struct dirent *dirent;
2231 FileProgressStatus ret = FILE_CONT;
2233 if (!compute_symlinks)
2235 res = mc_lstat (dirname, &s);
2236 if (res != 0)
2237 return ret;
2239 /* don't scan symlink to directory */
2240 if (S_ISLNK (s.st_mode))
2242 (*ret_marked)++;
2243 *ret_total += (uintmax_t) s.st_size;
2244 return ret;
2248 dir = mc_opendir (dirname);
2250 if (dir == NULL)
2251 return ret;
2253 while ((dirent = mc_readdir (dir)) != NULL)
2255 char *fullname;
2257 ret = (cback != NULL) ? cback (ui, dirname) : FILE_CONT;
2259 if (ret != FILE_CONT)
2260 break;
2262 if (strcmp (dirent->d_name, ".") == 0)
2263 continue;
2264 if (strcmp (dirent->d_name, "..") == 0)
2265 continue;
2267 fullname = concat_dir_and_file (dirname, dirent->d_name);
2268 res = mc_lstat (fullname, &s);
2270 if (res != 0)
2272 g_free (fullname);
2273 continue;
2276 if (S_ISDIR (s.st_mode))
2278 size_t subdir_count = 0;
2279 uintmax_t subdir_bytes = 0;
2281 ret =
2282 compute_dir_size (fullname, ui, cback, &subdir_count, &subdir_bytes,
2283 compute_symlinks);
2285 if (ret != FILE_CONT)
2287 g_free (fullname);
2288 break;
2291 *ret_marked += subdir_count;
2292 *ret_total += subdir_bytes;
2294 else
2296 (*ret_marked)++;
2297 *ret_total += (uintmax_t) s.st_size;
2300 g_free (fullname);
2303 mc_closedir (dir);
2305 return ret;
2308 /* --------------------------------------------------------------------------------------------- */
2310 * panel_operate:
2312 * Performs one of the operations on the selection on the source_panel
2313 * (copy, delete, move).
2315 * Returns TRUE if did change the directory
2316 * structure, Returns FALSE if user aborted
2318 * force_single forces operation on the current entry and affects
2319 * default destination. Current filename is used as default.
2322 gboolean
2323 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2325 WPanel *panel = (WPanel *) source_panel;
2326 const gboolean single_entry = force_single || (panel->marked <= 1)
2327 || (get_current_type () == view_tree);
2329 char *source = NULL;
2330 #ifdef WITH_FULL_PATHS
2331 char *source_with_path = NULL;
2332 #else
2333 #define source_with_path source
2334 #endif /* !WITH_FULL_PATHS */
2335 char *dest = NULL;
2336 char *temp = NULL;
2337 char *save_cwd = NULL, *save_dest = NULL;
2338 struct stat src_stat;
2339 gboolean ret_val = TRUE;
2340 int i;
2341 FileProgressStatus value;
2342 FileOpContext *ctx;
2343 FileOpTotalContext *tctx;
2345 gboolean do_bg = FALSE; /* do background operation? */
2347 static gboolean i18n_flag = FALSE;
2348 if (!i18n_flag)
2350 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2351 op_names[i] = Q_ (op_names[i]);
2352 i18n_flag = TRUE;
2355 free_linklist (&linklist);
2356 free_linklist (&dest_dirs);
2358 /* Update panel contents to avoid actions on deleted files */
2359 if (!panel->is_panelized)
2361 panel_update_flags_t flags = UP_RELOAD;
2363 /* don't update panelized panel */
2364 if (get_other_type () == view_listing && other_panel->is_panelized)
2365 flags |= UP_ONLY_CURRENT;
2367 update_panels (flags, UP_KEEPSEL);
2368 repaint_screen ();
2371 if (single_entry)
2373 if (force_single)
2375 source = selection (panel)->fname;
2376 src_stat = selection (panel)->st;
2378 else
2379 source = panel_get_file (panel, &src_stat);
2381 if (!strcmp (source, ".."))
2383 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2384 return FALSE;
2388 ctx = file_op_context_new (operation);
2389 tctx = file_op_total_context_new ();
2390 gettimeofday (&(tctx->transfer_start), (struct timezone *) NULL);
2392 /* Show confirmation dialog */
2393 if (operation != OP_DELETE)
2395 char *dest_dir;
2396 char *dest_dir_;
2397 char *format;
2399 /* Forced single operations default to the original name */
2400 if (force_single)
2401 dest_dir = source;
2402 else if (get_other_type () == view_listing)
2403 dest_dir = other_panel->cwd;
2404 else
2405 dest_dir = panel->cwd;
2407 * Add trailing backslash only when do non-local ops.
2408 * It saves user from occasional file renames (when destination
2409 * dir is deleted)
2411 if (!force_single && dest_dir[0] != '\0' && dest_dir[strlen (dest_dir) - 1] != PATH_SEP)
2413 /* add trailing separator */
2414 dest_dir_ = g_strconcat (dest_dir, PATH_SEP_STR, (char *) NULL);
2416 else
2418 /* just copy */
2419 dest_dir_ = g_strdup (dest_dir);
2421 if (dest_dir_ == NULL)
2423 file_op_total_context_destroy (tctx);
2424 file_op_context_destroy (ctx);
2425 return FALSE;
2428 /* Generate confirmation prompt */
2429 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2431 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2432 source != NULL ? (void *) source
2433 : (void *) &panel->marked, dest_dir_, &do_bg);
2435 g_free (format);
2436 g_free (dest_dir_);
2438 if (dest == NULL || dest[0] == '\0')
2440 file_op_total_context_destroy (tctx);
2441 file_op_context_destroy (ctx);
2442 g_free (dest);
2443 return FALSE;
2446 else if (confirm_delete)
2448 char *format;
2449 char fmd_buf[BUF_MEDIUM];
2451 /* Generate confirmation prompt */
2452 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2454 if (source == NULL)
2455 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2456 else
2458 const int fmd_xlen = 64;
2459 i = fmd_xlen - str_term_width1 (format) - 4;
2460 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2463 g_free (format);
2465 if (safe_delete)
2466 query_set_sel (1);
2468 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2470 if (i != 0)
2472 file_op_total_context_destroy (tctx);
2473 file_op_context_destroy (ctx);
2474 return FALSE;
2479 filegui_dialog_type_t dialog_type;
2481 if (operation == OP_DELETE)
2482 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2483 else
2485 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2486 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2488 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2489 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2492 /* Background also need ctx->ui, but not full */
2493 if (do_bg)
2494 file_op_context_create_ui_without_init (ctx, 1, dialog_type);
2495 else
2496 file_op_context_create_ui (ctx, 1, dialog_type);
2499 #ifdef WITH_BACKGROUND
2500 /* Did the user select to do a background operation? */
2501 if (do_bg)
2503 int v;
2505 v = do_background (ctx, g_strconcat (op_names[operation], ": ", panel->cwd, (char *) NULL));
2506 if (v == -1)
2507 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2509 /* If we are the parent */
2510 if (v == 1)
2512 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
2513 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
2514 /* file_op_context_destroy (ctx); */
2515 return FALSE;
2518 #endif /* WITH_BACKGROUND */
2520 /* Initialize things */
2521 /* We do not want to trash cache every time file is
2522 created/touched. However, this will make our cache contain
2523 invalid data. */
2524 if ((dest != NULL) && (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1)))
2525 save_dest = g_strdup (dest);
2527 if ((panel->cwd[0] != '\0') && (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1)))
2528 save_cwd = g_strdup (panel->cwd);
2530 /* Now, let's do the job */
2532 /* This code is only called by the tree and panel code */
2533 if (single_entry)
2535 /* We now have ETA in all cases */
2537 /* One file: FIXME mc_chdir will take user out of any vfs */
2538 if ((operation != OP_COPY) && (get_current_type () == view_tree) &&
2539 (mc_chdir (PATH_SEP_STR) < 0))
2541 ret_val = FALSE;
2542 goto clean_up;
2545 /* The source and src_stat variables have been initialized before */
2546 #ifdef WITH_FULL_PATHS
2547 if (g_path_is_absolute (source))
2548 source_with_path = g_strdup (source);
2549 else
2550 source_with_path = mc_build_filename (panel->cwd, source, (char *) NULL);
2551 #endif /* WITH_FULL_PATHS */
2553 if (panel_operate_init_totals (operation, panel, source_with_path, ctx) == FILE_CONT)
2555 if (operation == OP_DELETE)
2557 if (S_ISDIR (src_stat.st_mode))
2558 value = erase_dir (tctx, ctx, source_with_path);
2559 else
2560 value = erase_file (tctx, ctx, source_with_path, 1);
2562 else
2564 temp = transform_source (ctx, source_with_path);
2565 if (temp == NULL)
2566 value = transform_error;
2567 else
2569 char *repl_dest, *temp2;
2571 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2572 temp2 = concat_dir_and_file (repl_dest, temp);
2573 g_free (temp);
2574 g_free (repl_dest);
2575 g_free (dest);
2576 dest = temp2;
2578 switch (operation)
2580 case OP_COPY:
2581 /* we use file_mask_op_follow_links only with OP_COPY */
2582 ctx->stat_func (source_with_path, &src_stat);
2584 if (S_ISDIR (src_stat.st_mode))
2585 value = copy_dir_dir (tctx, ctx, source_with_path, dest,
2586 TRUE, FALSE, FALSE, NULL);
2587 else
2588 value = copy_file_file (tctx, ctx, source_with_path, dest);
2589 break;
2591 case OP_MOVE:
2592 if (S_ISDIR (src_stat.st_mode))
2593 value = move_dir_dir (tctx, ctx, source_with_path, dest);
2594 else
2595 value = move_file_file (tctx, ctx, source_with_path, dest);
2596 break;
2598 default:
2599 /* Unknown file operation */
2600 abort ();
2603 } /* Copy or move operation */
2605 if ((value == FILE_CONT) && !force_single)
2606 unmark_files (panel);
2609 else
2611 /* Many files */
2613 /* Check destination for copy or move operation */
2614 while (operation != OP_DELETE)
2616 int dst_result;
2617 struct stat dst_stat;
2619 dst_result = mc_stat (dest, &dst_stat);
2621 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2622 break;
2624 if (file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2625 goto clean_up;
2628 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2630 /* Loop for every file, perform the actual copy operation */
2631 for (i = 0; i < panel->count; i++)
2633 if (!panel->dir.list[i].f.marked)
2634 continue; /* Skip the unmarked ones */
2636 source = panel->dir.list[i].fname;
2637 src_stat = panel->dir.list[i].st;
2639 #ifdef WITH_FULL_PATHS
2640 g_free (source_with_path);
2641 if (g_path_is_absolute (source))
2642 source_with_path = g_strdup (source);
2643 else
2644 source_with_path = mc_build_filename (panel->cwd, source, (char *) NULL);
2645 #endif /* WITH_FULL_PATHS */
2647 if (operation == OP_DELETE)
2649 if (S_ISDIR (src_stat.st_mode))
2650 value = erase_dir (tctx, ctx, source_with_path);
2651 else
2652 value = erase_file (tctx, ctx, source_with_path, 1);
2654 else
2656 temp = transform_source (ctx, source_with_path);
2658 if (temp == NULL)
2659 value = transform_error;
2660 else
2662 char *temp2, *temp3, *repl_dest;
2664 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2665 temp2 = concat_dir_and_file (repl_dest, temp);
2666 g_free (temp);
2667 g_free (repl_dest);
2668 temp3 = source_with_path;
2669 source_with_path = strutils_shell_unescape (source_with_path);
2670 g_free (temp3);
2671 temp3 = temp2;
2672 temp2 = strutils_shell_unescape (temp2);
2673 g_free (temp3);
2675 switch (operation)
2677 case OP_COPY:
2678 /* we use file_mask_op_follow_links only with OP_COPY */
2679 ctx->stat_func (source_with_path, &src_stat);
2680 if (S_ISDIR (src_stat.st_mode))
2681 value = copy_dir_dir (tctx, ctx, source_with_path, temp2,
2682 TRUE, FALSE, FALSE, NULL);
2683 else
2684 value = copy_file_file (tctx, ctx, source_with_path, temp2);
2685 free_linklist (&dest_dirs);
2686 break;
2688 case OP_MOVE:
2689 if (S_ISDIR (src_stat.st_mode))
2690 value = move_dir_dir (tctx, ctx, source_with_path, temp2);
2691 else
2692 value = move_file_file (tctx, ctx, source_with_path, temp2);
2693 break;
2695 default:
2696 /* Unknown file operation */
2697 abort ();
2700 g_free (temp2);
2702 } /* Copy or move operation */
2704 if (value == FILE_ABORT)
2705 break;
2707 if (value == FILE_CONT)
2708 do_file_mark (panel, i, 0);
2710 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2712 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2713 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
2716 if (operation != OP_DELETE)
2717 file_progress_show (ctx, 0, 0, "", FALSE);
2719 if (check_progress_buttons (ctx) == FILE_ABORT)
2720 break;
2722 mc_refresh ();
2723 } /* Loop for every file */
2725 } /* Many entries */
2727 clean_up:
2728 /* Clean up */
2729 if (save_cwd != NULL)
2731 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2732 g_free (save_cwd);
2735 if (save_dest != NULL)
2737 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2738 g_free (save_dest);
2741 free_linklist (&linklist);
2742 free_linklist (&dest_dirs);
2743 #ifdef WITH_FULL_PATHS
2744 g_free (source_with_path);
2745 #endif /* WITH_FULL_PATHS */
2746 g_free (dest);
2747 g_free (ctx->dest_mask);
2748 ctx->dest_mask = NULL;
2750 #ifdef WITH_BACKGROUND
2751 /* Let our parent know we are saying bye bye */
2752 if (mc_global.we_are_background)
2754 int cur_pid = getpid ();
2755 /* Send pid to parent with child context, it is fork and
2756 don't modify real parent ctx */
2757 ctx->pid = cur_pid;
2758 parent_call ((void *) end_bg_process, ctx, 0);
2760 vfs_shut ();
2761 _exit (0);
2763 #endif /* WITH_BACKGROUND */
2765 file_op_context_destroy (ctx);
2766 file_op_total_context_destroy (tctx);
2768 return ret_val;
2771 /* }}} */
2773 /* --------------------------------------------------------------------------------------------- */
2774 /* {{{ Query/status report routines */
2775 /** Report error with one file */
2776 FileProgressStatus
2777 file_error (const char *format, const char *file)
2779 char buf[BUF_MEDIUM];
2781 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
2783 return do_file_error (buf);
2786 /* --------------------------------------------------------------------------------------------- */
2789 Cause emacs to enter folding mode for this file:
2790 Local variables:
2791 end: