changed interface of function mc_chdir() for handle vfs_path_t object as parameter
[midnight-commander.git] / src / filemanager / file.c
blob185cddc39b7a289a2d8342f51b09f17cc387e44a
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 #include "src/background.h"
77 #include "layout.h" /* rotate_dash() */
79 /* Needed for current_panel, other_panel and WTree */
80 #include "dir.h"
81 #include "filegui.h"
82 #include "tree.h"
83 #include "midnight.h" /* current_panel */
85 #include "file.h"
87 /* }}} */
89 /*** global variables ****************************************************************************/
91 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
92 const char *op_names[3] = {
93 N_("DialogTitle|Copy"),
94 N_("DialogTitle|Move"),
95 N_("DialogTitle|Delete")
98 /*** file scope macro definitions ****************************************************************/
100 /* Hack: the vfs code should not rely on this */
101 #define WITH_FULL_PATHS 1
103 #define FILEOP_UPDATE_INTERVAL 2
104 #define FILEOP_STALLING_INTERVAL 4
106 /*** file scope type declarations ****************************************************************/
108 /* This is a hard link cache */
109 struct link
111 struct link *next;
112 struct vfs_class *vfs;
113 dev_t dev;
114 ino_t ino;
115 short linkcount;
116 mode_t st_mode;
117 char name[1];
120 /* Status of the destination file */
121 typedef enum
123 DEST_NONE = 0, /* Not created */
124 DEST_SHORT = 1, /* Created, not fully copied */
125 DEST_FULL = 2 /* Created, fully copied */
126 } dest_status_t;
129 * This array introduced to avoid translation problems. The former (op_names)
130 * is assumed to be nouns, suitable in dialog box titles; this one should
131 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
132 * (I don't use spaces around the words, because someday they could be
133 * dropped, when widgets get smarter)
136 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
137 static const char *op_names1[] = {
138 N_("FileOperation|Copy"),
139 N_("FileOperation|Move"),
140 N_("FileOperation|Delete")
144 * These are formats for building a prompt. Parts encoded as follows:
145 * %o - operation from op_names1
146 * %f - file/files or files/directories, as appropriate
147 * %m - "with source mask" or question mark for delete
148 * %s - source name (truncated)
149 * %d - number of marked files
150 * %e - "to:" or question mark for delete
152 * xgettext:no-c-format */
153 static const char *one_format = N_("%o %f \"%s\"%m");
154 /* xgettext:no-c-format */
155 static const char *many_format = N_("%o %d %f%m");
157 static const char *prompt_parts[] = {
158 N_("file"),
159 N_("files"),
160 N_("directory"),
161 N_("directories"),
162 N_("files/directories"),
163 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
164 N_(" with source mask:"),
165 N_("to:")
168 static const char *question_format = N_("%s?");
170 /*** file scope variables ************************************************************************/
172 /* the hard link cache */
173 static struct link *linklist = NULL;
175 /* the files-to-be-erased list */
176 static struct link *erase_list;
179 * In copy_dir_dir we use two additional single linked lists: The first -
180 * variable name `parent_dirs' - holds information about already copied
181 * directories and is used to detect cyclic symbolic links.
182 * The second (`dest_dirs' below) holds information about just created
183 * target directories and is used to detect when an directory is copied
184 * into itself (we don't want to copy infinitly).
185 * Both lists don't use the linkcount and name structure members of struct
186 * link.
188 static struct link *dest_dirs = NULL;
190 static FileProgressStatus transform_error = FILE_CONT;
192 /*** file scope functions ************************************************************************/
193 /* --------------------------------------------------------------------------------------------- */
195 static char *
196 transform_source (FileOpContext * ctx, const char *source)
198 char *s, *q;
199 char *fnsource;
201 s = g_strdup (source);
203 /* We remove \n from the filename since regex routines would use \n as an anchor */
204 /* this is just to be allowed to maniupulate file names with \n on it */
205 for (q = s; *q != '\0'; q++)
206 if (*q == '\n')
207 *q = ' ';
209 fnsource = (char *) x_basename (s);
211 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
212 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
213 else
215 q = NULL;
216 transform_error = FILE_SKIP;
219 g_free (s);
220 return q;
223 /* --------------------------------------------------------------------------------------------- */
225 static void
226 free_linklist (struct link **lc_linklist)
228 struct link *lp, *lp2;
230 for (lp = *lc_linklist; lp != NULL; lp = lp2)
232 lp2 = lp->next;
233 g_free (lp);
235 *lc_linklist = NULL;
238 /* --------------------------------------------------------------------------------------------- */
240 static int
241 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
243 vfs_path_t *vpath;
244 vfs_path_element_t *vpath_element;
245 ino_t ino = sb->st_ino;
246 dev_t dev = sb->st_dev;
248 vpath = vfs_path_from_str (path);
249 vpath_element = vfs_path_get_by_index (vpath, -1);
251 while (lp != NULL)
253 if (lp->vfs == vpath_element->class)
254 if (lp->ino == ino && lp->dev == dev)
255 return 1;
256 lp = lp->next;
258 vfs_path_free (vpath);
259 return 0;
262 /* --------------------------------------------------------------------------------------------- */
264 * Check and made hardlink
266 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
267 * and a hardlink was succesfully made
270 static gboolean
271 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
273 struct link *lp;
274 vfs_path_t *vpath;
276 struct vfs_class *my_vfs;
277 ino_t ino = pstat->st_ino;
278 dev_t dev = pstat->st_dev;
279 struct stat link_stat;
280 const char *p;
282 vpath = vfs_path_from_str (src_name);
284 if ((vfs_file_class_flags (vpath) & VFSF_NOLINKS) != 0)
286 vfs_path_free (vpath);
287 return FALSE;
289 my_vfs = vfs_path_get_by_index (vpath, -1)->class;
290 vfs_path_free (vpath);
292 for (lp = linklist; lp != NULL; lp = lp->next)
293 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev)
295 struct vfs_class *lp_name_class;
297 vpath = vfs_path_from_str (lp->name);
298 lp_name_class = vfs_path_get_by_index (vpath, -1)->class;
299 vfs_path_free (vpath);
301 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
302 && link_stat.st_dev == dev && lp_name_class == my_vfs)
304 struct vfs_class *p_class, *dst_name_class;
306 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
307 was copied to */
309 vpath = vfs_path_from_str (p);
310 p_class = vfs_path_get_by_index (vpath, -1)->class;
311 vfs_path_free (vpath);
313 vpath = vfs_path_from_str (dst_name);
314 dst_name_class = vfs_path_get_by_index (vpath, -1)->class;
315 vfs_path_free (vpath);
317 if (dst_name_class == p_class)
319 if (!mc_stat (p, &link_stat))
321 if (!mc_link (p, dst_name))
322 return TRUE;
326 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
327 return FALSE;
329 lp = (struct link *) g_try_malloc (sizeof (struct link) + strlen (src_name)
330 + strlen (dst_name) + 1);
331 if (lp)
333 char *lpdstname;
334 lp->vfs = my_vfs;
335 lp->ino = ino;
336 lp->dev = dev;
337 strcpy (lp->name, src_name);
338 lpdstname = lp->name + strlen (lp->name) + 1;
339 strcpy (lpdstname, dst_name);
340 lp->next = linklist;
341 linklist = lp;
343 return FALSE;
346 /* --------------------------------------------------------------------------------------------- */
348 * Duplicate the contents of the symbolic link src_path in dst_path.
349 * Try to make a stable symlink if the option "stable symlink" was
350 * set in the file mask dialog.
351 * If dst_path is an existing symlink it will be deleted silently
352 * (upper levels take already care of existing files at dst_path).
355 static FileProgressStatus
356 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
358 char link_target[MC_MAXPATHLEN];
359 int len;
360 FileProgressStatus return_status;
361 struct stat sb;
362 gboolean dst_is_symlink;
364 dst_is_symlink = (mc_lstat (dst_path, &sb) == 0) && S_ISLNK (sb.st_mode);
366 retry_src_readlink:
367 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
368 if (len < 0)
370 if (ctx->skip_all)
371 return_status = FILE_SKIPALL;
372 else
374 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
375 if (return_status == FILE_SKIPALL)
376 ctx->skip_all = TRUE;
377 if (return_status == FILE_RETRY)
378 goto retry_src_readlink;
380 return return_status;
382 link_target[len] = 0;
384 if (ctx->stable_symlinks)
386 vfs_path_t *vpath1 = vfs_path_from_str (src_path);
387 vfs_path_t *vpath2 = vfs_path_from_str (dst_path);
389 if (!vfs_file_is_local (vpath1) || !vfs_file_is_local (vpath2))
391 message (D_ERROR, MSG_ERROR,
392 _("Cannot make stable symlinks across"
393 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
394 ctx->stable_symlinks = FALSE;
396 vfs_path_free (vpath1);
397 vfs_path_free (vpath2);
400 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
402 char *p, *q, *s;
404 const char *r = strrchr (src_path, PATH_SEP);
406 if (r)
408 p = g_strndup (src_path, r - src_path + 1);
409 if (g_path_is_absolute (dst_path))
410 q = g_strdup (dst_path);
411 else
412 q = g_strconcat (p, dst_path, (char *) NULL);
413 s = strrchr (q, PATH_SEP);
414 if (s)
416 s[1] = 0;
417 s = g_strconcat (p, link_target, (char *) NULL);
418 g_free (p);
419 g_strlcpy (link_target, s, sizeof (link_target));
420 g_free (s);
421 s = diff_two_paths (q, link_target);
422 if (s)
424 g_strlcpy (link_target, s, sizeof (link_target));
425 g_free (s);
428 else
429 g_free (p);
430 g_free (q);
433 retry_dst_symlink:
434 if (mc_symlink (link_target, dst_path) == 0)
435 /* Success */
436 return FILE_CONT;
438 * if dst_exists, it is obvious that this had failed.
439 * We can delete the old symlink and try again...
441 if (dst_is_symlink)
443 if (!mc_unlink (dst_path))
444 if (mc_symlink (link_target, dst_path) == 0)
445 /* Success */
446 return FILE_CONT;
448 if (ctx->skip_all)
449 return_status = FILE_SKIPALL;
450 else
452 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
453 if (return_status == FILE_SKIPALL)
454 ctx->skip_all = TRUE;
455 if (return_status == FILE_RETRY)
456 goto retry_dst_symlink;
458 return return_status;
461 /* --------------------------------------------------------------------------------------------- */
463 static FileProgressStatus
464 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add)
466 struct timeval tv_current;
467 static struct timeval tv_start = { };
469 tctx->progress_count++;
470 tctx->progress_bytes += (uintmax_t) add;
472 if (tv_start.tv_sec == 0)
474 gettimeofday (&tv_start, (struct timezone *) NULL);
476 gettimeofday (&tv_current, (struct timezone *) NULL);
477 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
479 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
481 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
482 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
484 tv_start.tv_sec = tv_current.tv_sec;
487 return check_progress_buttons (ctx);
490 /* --------------------------------------------------------------------------------------------- */
492 static FileProgressStatus
493 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
495 char *msg;
496 int result = 0;
497 const char *head_msg;
499 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
501 msg = g_strdup_printf (fmt, a, b);
502 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
503 g_free (msg);
504 do_refresh ();
506 return (result == 1) ? FILE_ABORT : FILE_SKIP;
509 /* --------------------------------------------------------------------------------------------- */
511 static FileProgressStatus
512 warn_same_file (const char *fmt, const char *a, const char *b)
514 #ifdef WITH_BACKGROUND
515 /* *INDENT-OFF* */
516 union
518 void *p;
519 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
520 const char *a, const char *b);
521 } pntr;
522 /* *INDENT-ON* */
524 pntr.f = real_warn_same_file;
526 if (mc_global.we_are_background)
527 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
528 #endif
529 return real_warn_same_file (Foreground, fmt, a, b);
532 /* --------------------------------------------------------------------------------------------- */
533 /* {{{ Query/status report routines */
535 static FileProgressStatus
536 real_do_file_error (enum OperationMode mode, const char *error)
538 int result;
539 const char *msg;
541 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
542 result =
543 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
545 switch (result)
547 case 0:
548 do_refresh ();
549 return FILE_SKIP;
551 case 1:
552 do_refresh ();
553 return FILE_SKIPALL;
555 case 2:
556 do_refresh ();
557 return FILE_RETRY;
559 case 3:
560 default:
561 return FILE_ABORT;
565 /* --------------------------------------------------------------------------------------------- */
567 static FileProgressStatus
568 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
570 gchar *text;
572 if (ctx->recursive_result < RECURSIVE_ALWAYS)
574 const char *msg = mode == Foreground
575 ? _("\nDirectory not empty.\nDelete it recursively?")
576 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
577 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
579 if (safe_delete)
580 query_set_sel (1);
582 ctx->recursive_result =
583 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
584 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
586 if (ctx->recursive_result != RECURSIVE_ABORT)
587 do_refresh ();
588 g_free (text);
591 switch (ctx->recursive_result)
593 case RECURSIVE_YES:
594 case RECURSIVE_ALWAYS:
595 return FILE_CONT;
597 case RECURSIVE_NO:
598 case RECURSIVE_NEVER:
599 return FILE_SKIP;
601 case RECURSIVE_ABORT:
602 default:
603 return FILE_ABORT;
607 /* --------------------------------------------------------------------------------------------- */
609 #ifdef WITH_BACKGROUND
610 static FileProgressStatus
611 do_file_error (const char *str)
613 union
615 void *p;
616 FileProgressStatus (*f) (enum OperationMode, const char *);
617 } pntr;
618 pntr.f = real_do_file_error;
620 if (mc_global.we_are_background)
621 return parent_call (pntr.p, NULL, 1, strlen (str), str);
622 else
623 return real_do_file_error (Foreground, str);
626 /* --------------------------------------------------------------------------------------------- */
628 static FileProgressStatus
629 query_recursive (FileOpContext * ctx, const char *s)
631 union
633 void *p;
634 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
635 } pntr;
636 pntr.f = real_query_recursive;
638 if (mc_global.we_are_background)
639 return parent_call (pntr.p, ctx, 1, strlen (s), s);
640 else
641 return real_query_recursive (ctx, Foreground, s);
644 /* --------------------------------------------------------------------------------------------- */
646 static FileProgressStatus
647 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
648 struct stat *_d_stat)
650 union
652 void *p;
653 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
654 struct stat *, struct stat *);
655 } pntr;
656 pntr.f = file_progress_real_query_replace;
658 if (mc_global.we_are_background)
659 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
660 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
661 else
662 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
665 #else
666 /* --------------------------------------------------------------------------------------------- */
668 static FileProgressStatus
669 do_file_error (const char *str)
671 return real_do_file_error (Foreground, str);
674 /* --------------------------------------------------------------------------------------------- */
676 static FileProgressStatus
677 query_recursive (FileOpContext * ctx, const char *s)
679 return real_query_recursive (ctx, Foreground, s);
682 /* --------------------------------------------------------------------------------------------- */
684 static FileProgressStatus
685 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
686 struct stat *_d_stat)
688 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
691 #endif /* !WITH_BACKGROUND */
693 /* --------------------------------------------------------------------------------------------- */
694 /** Report error with two files */
696 static FileProgressStatus
697 files_error (const char *format, const char *file1, const char *file2)
699 char buf[BUF_MEDIUM];
700 char *nfile1 = g_strdup (path_trunc (file1, 15));
701 char *nfile2 = g_strdup (path_trunc (file2, 15));
703 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
705 g_free (nfile1);
706 g_free (nfile2);
708 return do_file_error (buf);
711 /* }}} */
713 /* --------------------------------------------------------------------------------------------- */
715 static void
716 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
717 struct timeval tv_current, struct timeval tv_transfer_start,
718 off_t file_size, off_t n_read_total)
720 long dt;
722 /* 1. Update rotating dash after some time */
723 rotate_dash ();
725 /* 3. Compute ETA */
726 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
728 if (n_read_total == 0)
729 ctx->eta_secs = 0.0;
730 else
732 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
733 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
736 /* 4. Compute BPS rate */
737 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
738 if (ctx->bps_time < 1)
739 ctx->bps_time = 1;
740 ctx->bps = n_read_total / ctx->bps_time;
742 /* 5. Compute total ETA and BPS */
743 if (ctx->progress_bytes != 0)
745 uintmax_t remain_bytes;
747 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
748 #if 1
750 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
752 if (total_secs < 1)
753 total_secs = 1;
755 tctx->bps = tctx->copied_bytes / total_secs;
756 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
758 #else
759 /* broken on lot of little files */
760 tctx->bps_count++;
761 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
762 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
763 #endif
767 /* --------------------------------------------------------------------------------------------- */
769 /* {{{ Move routines */
770 static FileProgressStatus
771 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
773 struct stat src_stats, dst_stats;
774 FileProgressStatus return_status = FILE_CONT;
775 gboolean copy_done = FALSE;
776 gboolean old_ask_overwrite;
778 file_progress_show_source (ctx, s);
779 file_progress_show_target (ctx, d);
780 if (check_progress_buttons (ctx) == FILE_ABORT)
781 return FILE_ABORT;
783 mc_refresh ();
785 while (mc_lstat (s, &src_stats) != 0)
787 /* Source doesn't exist */
788 if (ctx->skip_all)
789 return_status = FILE_SKIPALL;
790 else
792 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
793 if (return_status == FILE_SKIPALL)
794 ctx->skip_all = TRUE;
796 if (return_status != FILE_RETRY)
797 return return_status;
800 if (mc_lstat (d, &dst_stats) == 0)
802 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
803 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
805 if (S_ISDIR (dst_stats.st_mode))
807 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
808 do_refresh ();
809 return FILE_SKIP;
812 if (confirm_overwrite)
814 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
815 if (return_status != FILE_CONT)
816 return return_status;
818 /* Ok to overwrite */
821 if (!ctx->do_append)
823 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
825 return_status = make_symlink (ctx, s, d);
826 if (return_status == FILE_CONT)
827 goto retry_src_remove;
828 else
829 return return_status;
832 if (mc_rename (s, d) == 0)
833 return progress_update_one (tctx, ctx, src_stats.st_size);
835 #if 0
836 /* Comparison to EXDEV seems not to work in nfs if you're moving from
837 one nfs to the same, but on the server it is on two different
838 filesystems. Then nfs returns EIO instead of EXDEV.
839 Hope it will not hurt if we always in case of error try to copy/delete. */
840 else
841 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
843 if (errno != EXDEV)
845 if (ctx->skip_all)
846 return_status = FILE_SKIPALL;
847 else
849 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
850 if (return_status == FILE_SKIPALL)
851 ctx->skip_all = TRUE;
852 if (return_status == FILE_RETRY)
853 goto retry_rename;
855 return return_status;
857 #endif
859 /* Failed because filesystem boundary -> copy the file instead */
860 old_ask_overwrite = tctx->ask_overwrite;
861 tctx->ask_overwrite = FALSE;
862 return_status = copy_file_file (tctx, ctx, s, d);
863 tctx->ask_overwrite = old_ask_overwrite;
864 if (return_status != FILE_CONT)
865 return return_status;
867 copy_done = TRUE;
869 file_progress_show_source (ctx, NULL);
870 file_progress_show (ctx, 0, 0, "", FALSE);
872 return_status = check_progress_buttons (ctx);
873 if (return_status != FILE_CONT)
874 return return_status;
876 mc_refresh ();
878 retry_src_remove:
879 if (mc_unlink (s) != 0 && !ctx->skip_all)
881 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
882 if (return_status == FILE_RETRY)
883 goto retry_src_remove;
884 if (return_status == FILE_SKIPALL)
885 ctx->skip_all = TRUE;
886 return return_status;
889 if (!copy_done)
890 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
892 return return_status;
895 /* }}} */
897 /* --------------------------------------------------------------------------------------------- */
898 /* {{{ Erase routines */
899 /** Don't update progress status if progress_count==NULL */
901 static FileProgressStatus
902 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
904 int return_status;
905 struct stat buf;
907 file_progress_show_deleting (ctx, s);
908 if (check_progress_buttons (ctx) == FILE_ABORT)
909 return FILE_ABORT;
910 mc_refresh ();
912 if (tctx->progress_count != 0 && mc_lstat (s, &buf) != 0)
914 /* ignore, most likely the mc_unlink fails, too */
915 buf.st_size = 0;
918 while (mc_unlink (s) != 0 && !ctx->skip_all)
920 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
921 if (return_status == FILE_ABORT)
922 return return_status;
923 if (return_status == FILE_RETRY)
924 continue;
925 if (return_status == FILE_SKIPALL)
926 ctx->skip_all = TRUE;
927 break;
930 if (tctx->progress_count == 0)
931 return FILE_CONT;
932 return progress_update_one (tctx, ctx, buf.st_size);
935 /* --------------------------------------------------------------------------------------------- */
938 Recursive remove of files
939 abort->cancel stack
940 skip ->warn every level, gets default
941 skipall->remove as much as possible
943 static FileProgressStatus
944 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
946 struct dirent *next;
947 struct stat buf;
948 DIR *reading;
949 char *path;
950 FileProgressStatus return_status = FILE_CONT;
952 if (!strcmp (s, ".."))
953 return FILE_RETRY;
955 reading = mc_opendir (s);
957 if (!reading)
958 return FILE_RETRY;
960 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
962 if (!strcmp (next->d_name, "."))
963 continue;
964 if (!strcmp (next->d_name, ".."))
965 continue;
966 path = concat_dir_and_file (s, next->d_name);
967 if (mc_lstat (path, &buf))
969 g_free (path);
970 mc_closedir (reading);
971 return FILE_RETRY;
973 if (S_ISDIR (buf.st_mode))
974 return_status = recursive_erase (tctx, ctx, path);
975 else
976 return_status = erase_file (tctx, ctx, path);
977 g_free (path);
979 mc_closedir (reading);
980 if (return_status == FILE_ABORT)
981 return return_status;
982 file_progress_show_deleting (ctx, s);
983 if (check_progress_buttons (ctx) == FILE_ABORT)
984 return FILE_ABORT;
985 mc_refresh ();
987 while (my_rmdir (s) != 0 && !ctx->skip_all)
989 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
990 if (return_status == FILE_RETRY)
991 continue;
992 if (return_status == FILE_ABORT)
993 return return_status;
994 if (return_status == FILE_SKIPALL)
995 ctx->skip_all = TRUE;
996 break;
999 return FILE_CONT;
1002 /* --------------------------------------------------------------------------------------------- */
1003 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1004 in the directory path points to, 0 else. */
1006 static int
1007 check_dir_is_empty (const char *path)
1009 DIR *dir;
1010 struct dirent *d;
1011 int i;
1013 dir = mc_opendir (path);
1014 if (!dir)
1015 return -1;
1017 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1019 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1020 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1021 continue; /* "." or ".." */
1022 i = 0;
1023 break;
1026 mc_closedir (dir);
1027 return i;
1030 /* --------------------------------------------------------------------------------------------- */
1032 static FileProgressStatus
1033 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1035 FileProgressStatus error;
1037 if (strcmp (s, "..") == 0)
1038 return FILE_SKIP;
1040 if (strcmp (s, ".") == 0)
1041 return FILE_SKIP;
1043 file_progress_show_deleting (ctx, s);
1044 if (check_progress_buttons (ctx) == FILE_ABORT)
1045 return FILE_ABORT;
1046 mc_refresh ();
1048 if (1 != check_dir_is_empty (s)) /* not empty or error */
1049 return FILE_CONT;
1051 while (my_rmdir (s) != 0 && !ctx->skip_all)
1053 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1054 if (error == FILE_SKIPALL)
1055 ctx->skip_all = TRUE;
1056 if (error != FILE_RETRY)
1057 return error;
1060 return FILE_CONT;
1063 /* }}} */
1065 /* --------------------------------------------------------------------------------------------- */
1066 /* {{{ Panel operate routines */
1069 * Return currently selected entry name or the name of the first marked
1070 * entry if there is one.
1073 static char *
1074 panel_get_file (WPanel * panel)
1076 if (get_current_type () == view_tree)
1078 WTree *tree;
1080 tree = (WTree *) get_panel_widget (get_current_index ());
1081 return tree_selected_name (tree);
1084 if (panel->marked != 0)
1086 int i;
1088 for (i = 0; i < panel->count; i++)
1089 if (panel->dir.list[i].f.marked)
1090 return panel->dir.list[i].fname;
1093 return panel->dir.list[panel->selected].fname;
1096 /* --------------------------------------------------------------------------------------------- */
1098 * panel_compute_totals:
1100 * compute the number of files and the number of bytes
1101 * used up by the whole selection, recursing directories
1102 * as required. In addition, it checks to see if it will
1103 * overwrite any files by doing the copy.
1106 static FileProgressStatus
1107 panel_compute_totals (const WPanel * panel, const void *ui,
1108 compute_dir_size_callback cback,
1109 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1111 int i;
1113 *ret_marked = 0;
1114 *ret_total = 0;
1116 for (i = 0; i < panel->count; i++)
1118 struct stat *s;
1120 if (!panel->dir.list[i].f.marked)
1121 continue;
1123 s = &panel->dir.list[i].st;
1125 if (S_ISDIR (s->st_mode))
1127 char *dir_name;
1128 size_t subdir_count = 0;
1129 uintmax_t subdir_bytes = 0;
1130 FileProgressStatus status;
1132 dir_name = concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1134 status = compute_dir_size (dir_name, ui, cback,
1135 &subdir_count, &subdir_bytes, compute_symlinks);
1136 g_free (dir_name);
1138 if (status != FILE_CONT)
1139 return FILE_ABORT;
1141 *ret_marked += subdir_count;
1142 *ret_total += subdir_bytes;
1144 else
1146 (*ret_marked)++;
1147 *ret_total += (uintmax_t) s->st_size;
1151 return FILE_CONT;
1154 /* --------------------------------------------------------------------------------------------- */
1156 /** Initialize variables for progress bars */
1157 static FileProgressStatus
1158 panel_operate_init_totals (FileOperation operation,
1159 const WPanel * panel, const char *source, FileOpContext * ctx)
1161 FileProgressStatus status;
1163 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1165 ComputeDirSizeUI *ui;
1167 ui = compute_dir_size_create_ui ();
1169 if (source != NULL)
1170 status = compute_dir_size (source, ui, compute_dir_size_update_ui,
1171 &ctx->progress_count, &ctx->progress_bytes,
1172 ctx->follow_links);
1173 else
1174 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1175 &ctx->progress_count, &ctx->progress_bytes,
1176 ctx->follow_links);
1178 compute_dir_size_destroy_ui (ui);
1180 ctx->progress_totals_computed = (status == FILE_CONT);
1182 else
1184 status = FILE_CONT;
1185 ctx->progress_count = panel->marked;
1186 ctx->progress_bytes = panel->total;
1187 ctx->progress_totals_computed = FALSE;
1190 return status;
1193 /* --------------------------------------------------------------------------------------------- */
1195 * Generate user prompt for panel operation.
1196 * single_source is the name if the source entry or NULL for multiple
1197 * entries.
1198 * src_stat is only used when single_source is not NULL.
1201 static char *
1202 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1203 gboolean single_source, const struct stat *src_stat)
1205 const char *sp, *cp;
1206 char format_string[BUF_MEDIUM];
1207 char *dp = format_string;
1208 gboolean build_question = FALSE;
1210 static gboolean i18n_flag = FALSE;
1211 if (!i18n_flag)
1213 size_t i;
1215 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1216 op_names1[i] = Q_ (op_names1[i]);
1218 #ifdef ENABLE_NLS
1219 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1220 prompt_parts[i] = _(prompt_parts[i]);
1222 one_format = _(one_format);
1223 many_format = _(many_format);
1224 question_format = _(question_format);
1225 #endif /* ENABLE_NLS */
1226 i18n_flag = TRUE;
1229 sp = single_source ? one_format : many_format;
1231 while (*sp != '\0')
1233 switch (*sp)
1235 case '%':
1236 cp = NULL;
1237 switch (sp[1])
1239 case 'o':
1240 cp = op_names1[operation];
1241 break;
1242 case 'm':
1243 if (operation == OP_DELETE)
1245 cp = "";
1246 build_question = TRUE;
1248 else
1249 cp = prompt_parts[5];
1250 break;
1251 case 'e':
1252 if (operation == OP_DELETE)
1254 cp = "";
1255 build_question = TRUE;
1257 else
1258 cp = prompt_parts[6];
1259 break;
1260 case 'f':
1261 if (single_source)
1262 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1263 else
1264 cp = (panel->marked == panel->dirs_marked)
1265 ? prompt_parts[3]
1266 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1267 break;
1268 default:
1269 *dp++ = *sp++;
1272 if (cp != NULL)
1274 sp += 2;
1275 while (*cp != '\0')
1276 *dp++ = *cp++;
1278 break;
1279 default:
1280 *dp++ = *sp++;
1283 *dp = '\0';
1285 if (build_question)
1287 char tmp[BUF_MEDIUM];
1289 memmove (tmp, format_string, sizeof (tmp));
1290 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1293 return g_strdup (format_string);
1296 /* --------------------------------------------------------------------------------------------- */
1298 #ifdef WITH_BACKGROUND
1299 static int
1300 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1302 int pid = ctx->pid;
1304 (void) mode;
1305 ctx->pid = 0;
1307 unregister_task_with_pid (pid);
1308 /* file_op_context_destroy(ctx); */
1309 return 1;
1311 #endif
1312 /* }}} */
1314 /* --------------------------------------------------------------------------------------------- */
1315 /*** public functions ****************************************************************************/
1316 /* --------------------------------------------------------------------------------------------- */
1318 FileProgressStatus
1319 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1320 const char *src_path, const char *dst_path)
1322 uid_t src_uid = (uid_t) - 1;
1323 gid_t src_gid = (gid_t) - 1;
1325 int src_desc, dest_desc = -1;
1326 int n_read, n_written;
1327 mode_t src_mode = 0; /* The mode of the source file */
1328 struct stat sb, sb2;
1329 struct utimbuf utb;
1330 gboolean dst_exists = FALSE, appending = FALSE;
1331 off_t file_size = -1;
1332 FileProgressStatus return_status, temp_status;
1333 struct timeval tv_transfer_start;
1334 dest_status_t dst_status = DEST_NONE;
1335 int open_flags;
1336 gboolean is_first_time = TRUE;
1338 /* FIXME: We should not be using global variables! */
1339 ctx->do_reget = 0;
1340 return_status = FILE_RETRY;
1342 file_progress_show_source (ctx, src_path);
1343 file_progress_show_target (ctx, dst_path);
1344 if (check_progress_buttons (ctx) == FILE_ABORT)
1345 return FILE_ABORT;
1347 mc_refresh ();
1349 while (mc_stat (dst_path, &sb2) == 0)
1351 if (S_ISDIR (sb2.st_mode))
1353 if (ctx->skip_all)
1354 return_status = FILE_SKIPALL;
1355 else
1357 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1358 if (return_status == FILE_SKIPALL)
1359 ctx->skip_all = TRUE;
1360 if (return_status == FILE_RETRY)
1361 continue;
1363 return return_status;
1365 dst_exists = TRUE;
1366 break;
1369 while ((*ctx->stat_func) (src_path, &sb) != 0)
1371 if (ctx->skip_all)
1372 return_status = FILE_SKIPALL;
1373 else
1375 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1376 if (return_status == FILE_SKIPALL)
1377 ctx->skip_all = TRUE;
1379 if (return_status != FILE_RETRY)
1380 return return_status;
1383 if (dst_exists)
1385 /* Destination already exists */
1386 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1387 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), src_path, dst_path);
1388 /* Should we replace destination? */
1389 if (tctx->ask_overwrite)
1391 ctx->do_reget = 0;
1392 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1393 if (return_status != FILE_CONT)
1394 return return_status;
1398 if (!ctx->do_append)
1400 /* Check the hardlinks */
1401 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb))
1403 /* We have made a hardlink - no more processing is necessary */
1404 return FILE_CONT;
1407 if (S_ISLNK (sb.st_mode))
1408 return make_symlink (ctx, src_path, dst_path);
1410 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1411 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1413 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1414 && !ctx->skip_all)
1416 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1417 if (return_status == FILE_RETRY)
1418 continue;
1419 if (return_status == FILE_SKIPALL)
1420 ctx->skip_all = TRUE;
1421 return return_status;
1423 /* Success */
1425 while (ctx->preserve_uidgid && mc_chown (dst_path, sb.st_uid, sb.st_gid) != 0
1426 && !ctx->skip_all)
1428 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1429 if (temp_status == FILE_SKIP)
1430 break;
1431 if (temp_status == FILE_SKIPALL)
1432 ctx->skip_all = TRUE;
1433 if (temp_status != FILE_RETRY)
1434 return temp_status;
1437 while (ctx->preserve && mc_chmod (dst_path, sb.st_mode & ctx->umask_kill) != 0
1438 && !ctx->skip_all)
1440 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1441 if (temp_status == FILE_SKIP)
1442 break;
1443 if (temp_status == FILE_SKIPALL)
1444 ctx->skip_all = TRUE;
1445 if (temp_status != FILE_RETRY)
1446 return temp_status;
1449 return FILE_CONT;
1453 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1455 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1457 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1458 if (return_status == FILE_RETRY)
1459 continue;
1460 if (return_status == FILE_SKIPALL)
1461 ctx->skip_all = TRUE;
1462 if (return_status == FILE_SKIP)
1463 break;
1464 ctx->do_append = 0;
1465 return return_status;
1468 if (ctx->do_reget != 0)
1470 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1472 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1473 ctx->do_reget = 0;
1474 ctx->do_append = FALSE;
1478 while (mc_fstat (src_desc, &sb) != 0)
1480 if (ctx->skip_all)
1481 return_status = FILE_SKIPALL;
1482 else
1484 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1485 if (return_status == FILE_RETRY)
1486 continue;
1487 if (return_status == FILE_SKIPALL)
1488 ctx->skip_all = TRUE;
1489 ctx->do_append = FALSE;
1491 goto ret;
1493 src_mode = sb.st_mode;
1494 src_uid = sb.st_uid;
1495 src_gid = sb.st_gid;
1496 utb.actime = sb.st_atime;
1497 utb.modtime = sb.st_mtime;
1498 file_size = sb.st_size;
1500 open_flags = O_WRONLY;
1501 if (dst_exists)
1503 if (ctx->do_append != 0)
1504 open_flags |= O_APPEND;
1505 else
1506 open_flags |= O_CREAT | O_TRUNC;
1508 else
1510 open_flags |= O_CREAT | O_EXCL;
1513 while ((dest_desc = mc_open (dst_path, open_flags, src_mode)) < 0)
1515 if (errno != EEXIST)
1517 if (ctx->skip_all)
1518 return_status = FILE_SKIPALL;
1519 else
1521 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1522 if (return_status == FILE_RETRY)
1523 continue;
1524 if (return_status == FILE_SKIPALL)
1525 ctx->skip_all = TRUE;
1526 ctx->do_append = FALSE;
1529 goto ret;
1531 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1533 appending = ctx->do_append;
1534 ctx->do_append = FALSE;
1536 /* Find out the optimal buffer size. */
1537 while (mc_fstat (dest_desc, &sb) != 0)
1539 if (ctx->skip_all)
1540 return_status = FILE_SKIPALL;
1541 else
1543 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1544 if (return_status == FILE_RETRY)
1545 continue;
1546 if (return_status == FILE_SKIPALL)
1547 ctx->skip_all = TRUE;
1549 goto ret;
1552 while (TRUE)
1554 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1555 if (errno == 0)
1556 break;
1558 if (ctx->skip_all)
1559 return_status = FILE_SKIPALL;
1560 else
1562 return_status =
1563 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1564 if (return_status == FILE_RETRY)
1565 continue;
1566 if (return_status == FILE_SKIPALL)
1567 ctx->skip_all = TRUE;
1569 mc_close (dest_desc);
1570 dest_desc = -1;
1571 mc_unlink (dst_path);
1572 dst_status = DEST_NONE;
1573 goto ret;
1576 ctx->eta_secs = 0.0;
1577 ctx->bps = 0;
1579 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1580 file_progress_show (ctx, 0, file_size, "", TRUE);
1581 else
1582 file_progress_show (ctx, 1, 1, "", TRUE);
1583 return_status = check_progress_buttons (ctx);
1584 mc_refresh ();
1586 if (return_status != FILE_CONT)
1587 goto ret;
1590 off_t n_read_total = 0;
1591 struct timeval tv_current, tv_last_update, tv_last_input;
1592 int secs, update_secs;
1593 const char *stalled_msg = "";
1595 tv_last_update = tv_transfer_start;
1597 while (TRUE)
1599 char buf[BUF_8K];
1601 /* src_read */
1602 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1603 n_read = -1;
1604 else
1605 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1607 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1608 if (return_status == FILE_RETRY)
1609 continue;
1610 if (return_status == FILE_SKIPALL)
1611 ctx->skip_all = TRUE;
1612 goto ret;
1614 if (n_read == 0)
1615 break;
1617 gettimeofday (&tv_current, NULL);
1619 if (n_read > 0)
1621 char *t = buf;
1622 n_read_total += n_read;
1624 /* Windows NT ftp servers report that files have no
1625 * permissions: -------, so if we happen to have actually
1626 * read something, we should fix the permissions.
1628 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1629 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1630 gettimeofday (&tv_last_input, NULL);
1632 /* dst_write */
1633 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read && !ctx->skip_all)
1635 if (n_written > 0)
1637 n_read -= n_written;
1638 t += n_written;
1639 continue;
1641 return_status = file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1642 if (return_status == FILE_SKIP)
1643 break;
1644 if (return_status == FILE_SKIPALL)
1645 ctx->skip_all = TRUE;
1646 if (return_status != FILE_RETRY)
1647 goto ret;
1651 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1653 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1654 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1656 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1658 copy_file_file_display_progress (tctx, ctx,
1659 tv_current,
1660 tv_transfer_start, file_size, n_read_total);
1661 tv_last_update = tv_current;
1663 is_first_time = FALSE;
1665 if (update_secs > FILEOP_STALLING_INTERVAL)
1667 stalled_msg = _("(stalled)");
1671 gboolean force_update;
1673 force_update =
1674 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1676 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1678 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1679 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1682 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1683 force_update);
1685 mc_refresh ();
1687 return_status = check_progress_buttons (ctx);
1689 if (return_status != FILE_CONT)
1691 mc_refresh ();
1692 goto ret;
1697 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1699 ret:
1700 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1702 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1703 if (temp_status == FILE_RETRY)
1704 continue;
1705 if (temp_status == FILE_ABORT)
1706 return_status = temp_status;
1707 if (temp_status == FILE_SKIPALL)
1708 ctx->skip_all = TRUE;
1709 break;
1712 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1714 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1715 if (temp_status == FILE_RETRY)
1716 continue;
1717 if (temp_status == FILE_SKIPALL)
1718 ctx->skip_all = TRUE;
1719 return_status = temp_status;
1720 break;
1723 if (dst_status == DEST_SHORT)
1725 /* Remove short file */
1726 int result;
1727 result = query_dialog (Q_ ("DialogTitle|Copy"),
1728 _("Incomplete file was retrieved. Keep it?"),
1729 D_ERROR, 2, _("&Delete"), _("&Keep"));
1730 if (result == 0)
1731 mc_unlink (dst_path);
1733 else if (dst_status == DEST_FULL)
1735 /* Copy has succeeded */
1736 if (!appending && ctx->preserve_uidgid)
1738 while (mc_chown (dst_path, src_uid, src_gid) != 0 && !ctx->skip_all)
1740 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1741 if (temp_status == FILE_RETRY)
1742 continue;
1743 if (temp_status == FILE_SKIPALL)
1745 ctx->skip_all = TRUE;
1746 return_status = FILE_CONT;
1748 if (temp_status == FILE_SKIP)
1749 return_status = FILE_CONT;
1750 break;
1754 if (!appending)
1756 if (ctx->preserve)
1758 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1760 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1761 if (temp_status == FILE_RETRY)
1762 continue;
1763 if (temp_status == FILE_SKIPALL)
1765 ctx->skip_all = TRUE;
1766 return_status = FILE_CONT;
1768 if (temp_status == FILE_SKIP)
1769 return_status = FILE_CONT;
1770 break;
1773 else
1775 src_mode = umask (-1);
1776 umask (src_mode);
1777 src_mode = 0100666 & ~src_mode;
1778 mc_chmod (dst_path, (src_mode & ctx->umask_kill));
1780 mc_utime (dst_path, &utb);
1784 if (return_status == FILE_CONT)
1785 return_status = progress_update_one (tctx, ctx, file_size);
1787 return return_status;
1790 /* --------------------------------------------------------------------------------------------- */
1792 * I think these copy_*_* functions should have a return type.
1793 * anyway, this function *must* have two directories as arguments.
1795 /* FIXME: This function needs to check the return values of the
1796 function calls */
1798 FileProgressStatus
1799 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1800 gboolean toplevel, gboolean move_over, gboolean do_delete, struct link * parent_dirs)
1802 struct dirent *next;
1803 struct stat buf, cbuf;
1804 DIR *reading;
1805 char *dest_dir = NULL;
1806 FileProgressStatus return_status = FILE_CONT;
1807 struct utimbuf utb;
1808 struct link *lp;
1809 char *d;
1811 d = g_strdup (_d);
1813 /* First get the mode of the source dir */
1814 retry_src_stat:
1815 if ((*ctx->stat_func) (s, &cbuf) != 0)
1817 if (ctx->skip_all)
1818 return_status = FILE_SKIPALL;
1819 else
1821 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1822 if (return_status == FILE_RETRY)
1823 goto retry_src_stat;
1824 if (return_status == FILE_SKIPALL)
1825 ctx->skip_all = TRUE;
1827 goto ret_fast;
1830 if (is_in_linklist (dest_dirs, s, &cbuf))
1832 /* Don't copy a directory we created before (we don't want to copy
1833 infinitely if a directory is copied into itself) */
1834 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1835 return_status = FILE_CONT;
1836 goto ret_fast;
1839 /* Hmm, hardlink to directory??? - Norbert */
1840 /* FIXME: In this step we should do something
1841 in case the destination already exist */
1842 /* Check the hardlinks */
1843 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf))
1845 /* We have made a hardlink - no more processing is necessary */
1846 goto ret_fast;
1849 if (!S_ISDIR (cbuf.st_mode))
1851 if (ctx->skip_all)
1852 return_status = FILE_SKIPALL;
1853 else
1855 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1856 if (return_status == FILE_RETRY)
1857 goto retry_src_stat;
1858 if (return_status == FILE_SKIPALL)
1859 ctx->skip_all = TRUE;
1861 goto ret_fast;
1864 if (is_in_linklist (parent_dirs, s, &cbuf))
1866 /* we found a cyclic symbolic link */
1867 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
1868 return_status = FILE_SKIP;
1869 goto ret_fast;
1872 lp = g_new (struct link, 1);
1874 vfs_path_t *vpath = vfs_path_from_str (s);
1875 lp->vfs = vfs_path_get_by_index (vpath, -1)->class;
1876 vfs_path_free (vpath);
1878 lp->ino = cbuf.st_ino;
1879 lp->dev = cbuf.st_dev;
1880 lp->next = parent_dirs;
1881 parent_dirs = lp;
1883 retry_dst_stat:
1884 /* Now, check if the dest dir exists, if not, create it. */
1885 if (mc_stat (d, &buf))
1887 /* Here the dir doesn't exist : make it ! */
1888 if (move_over)
1890 if (mc_rename (s, d) == 0)
1892 return_status = FILE_CONT;
1893 goto ret;
1896 dest_dir = d;
1897 d = NULL;
1899 else
1902 * If the destination directory exists, we want to copy the whole
1903 * directory, but we only want this to happen once.
1905 * Escape sequences added to the * to compiler warnings.
1906 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
1907 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
1909 if (!S_ISDIR (buf.st_mode))
1911 if (ctx->skip_all)
1912 return_status = FILE_SKIPALL;
1913 else
1915 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
1916 if (return_status == FILE_SKIPALL)
1917 ctx->skip_all = TRUE;
1918 if (return_status == FILE_RETRY)
1919 goto retry_dst_stat;
1921 goto ret;
1923 /* Dive into subdir if exists */
1924 if (toplevel && ctx->dive_into_subdirs)
1926 dest_dir = concat_dir_and_file (d, x_basename (s));
1928 else
1930 dest_dir = d;
1931 d = NULL;
1932 goto dont_mkdir;
1935 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU))
1937 if (ctx->skip_all)
1938 return_status = FILE_SKIPALL;
1939 else
1941 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
1942 if (return_status == FILE_SKIPALL)
1943 ctx->skip_all = TRUE;
1945 if (return_status != FILE_RETRY)
1946 goto ret;
1949 lp = g_new (struct link, 1);
1950 mc_stat (dest_dir, &buf);
1952 vfs_path_t *vpath = vfs_path_from_str (dest_dir);
1953 lp->vfs = vfs_path_get_by_index (vpath, -1)->class;
1954 vfs_path_free (vpath);
1956 lp->ino = buf.st_ino;
1957 lp->dev = buf.st_dev;
1958 lp->next = dest_dirs;
1959 dest_dirs = lp;
1961 if (ctx->preserve_uidgid)
1963 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid) != 0)
1965 if (ctx->skip_all)
1966 return_status = FILE_SKIPALL;
1967 else
1969 return_status =
1970 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
1971 if (return_status == FILE_SKIPALL)
1972 ctx->skip_all = TRUE;
1974 if (return_status != FILE_RETRY)
1975 goto ret;
1979 dont_mkdir:
1980 /* open the source dir for reading */
1981 reading = mc_opendir (s);
1982 if (reading == NULL)
1983 goto ret;
1985 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1987 char *path;
1989 * Now, we don't want '.' and '..' to be created / copied at any time
1991 if (!strcmp (next->d_name, "."))
1992 continue;
1993 if (!strcmp (next->d_name, ".."))
1994 continue;
1996 /* get the filename and add it to the src directory */
1997 path = concat_dir_and_file (s, next->d_name);
1999 (*ctx->stat_func) (path, &buf);
2000 if (S_ISDIR (buf.st_mode))
2002 char *mdpath;
2004 mdpath = concat_dir_and_file (dest_dir, next->d_name);
2006 * From here, we just intend to recursively copy subdirs, not
2007 * the double functionality of copying different when the target
2008 * dir already exists. So, we give the recursive call the flag 0
2009 * meaning no toplevel.
2011 return_status =
2012 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2013 g_free (mdpath);
2015 else
2017 char *dest_file;
2019 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
2020 return_status = copy_file_file (tctx, ctx, path, dest_file);
2021 g_free (dest_file);
2023 if (do_delete && return_status == FILE_CONT)
2025 if (ctx->erase_at_end)
2027 static struct link *tail;
2028 size_t len = strlen (path);
2029 lp = g_malloc (sizeof (struct link) + len);
2030 strncpy (lp->name, path, len + 1);
2031 lp->st_mode = buf.st_mode;
2032 lp->next = NULL;
2033 if (erase_list != NULL)
2035 tail->next = lp;
2036 tail = lp;
2038 else
2039 erase_list = tail = lp;
2041 else
2043 if (S_ISDIR (buf.st_mode))
2045 return_status = erase_dir_iff_empty (ctx, path);
2047 else
2048 return_status = erase_file (tctx, ctx, path);
2051 g_free (path);
2053 mc_closedir (reading);
2055 if (ctx->preserve)
2057 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
2058 utb.actime = cbuf.st_atime;
2059 utb.modtime = cbuf.st_mtime;
2060 mc_utime (dest_dir, &utb);
2062 else
2064 cbuf.st_mode = umask (-1);
2065 umask (cbuf.st_mode);
2066 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2067 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
2070 ret:
2071 g_free (dest_dir);
2072 g_free (parent_dirs);
2073 ret_fast:
2074 g_free (d);
2075 return return_status;
2078 /* }}} */
2080 /* --------------------------------------------------------------------------------------------- */
2081 /* {{{ Move routines */
2083 FileProgressStatus
2084 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2086 struct stat sbuf, dbuf, destbuf;
2087 struct link *lp;
2088 char *destdir;
2089 FileProgressStatus return_status;
2090 gboolean move_over = FALSE;
2091 gboolean dstat_ok;
2093 file_progress_show_source (ctx, s);
2094 file_progress_show_target (ctx, d);
2095 if (check_progress_buttons (ctx) == FILE_ABORT)
2096 return FILE_ABORT;
2098 mc_refresh ();
2100 mc_stat (s, &sbuf);
2101 dstat_ok = (mc_stat (d, &dbuf) == 0);
2103 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2104 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2106 if (!dstat_ok)
2107 destdir = g_strdup (d); /* destination doesn't exist */
2108 else if (!ctx->dive_into_subdirs)
2110 destdir = g_strdup (d);
2111 move_over = TRUE;
2113 else
2114 destdir = concat_dir_and_file (d, x_basename (s));
2116 /* Check if the user inputted an existing dir */
2117 retry_dst_stat:
2118 if (mc_stat (destdir, &destbuf) == 0)
2120 if (move_over)
2122 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2124 if (return_status != FILE_CONT)
2125 goto ret;
2126 goto oktoret;
2128 else if (ctx->skip_all)
2129 return_status = FILE_SKIPALL;
2130 else
2132 if (S_ISDIR (destbuf.st_mode))
2133 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2134 else
2135 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2136 if (return_status == FILE_SKIPALL)
2137 ctx->skip_all = TRUE;
2138 if (return_status == FILE_RETRY)
2139 goto retry_dst_stat;
2141 g_free (destdir);
2142 return return_status;
2145 retry_rename:
2146 if (mc_rename (s, destdir) == 0)
2148 return_status = FILE_CONT;
2149 goto ret;
2152 if (errno != EXDEV)
2154 if (!ctx->skip_all)
2156 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2157 if (return_status == FILE_SKIPALL)
2158 ctx->skip_all = TRUE;
2159 if (return_status == FILE_RETRY)
2160 goto retry_rename;
2162 goto ret;
2164 /* Failed because of filesystem boundary -> copy dir instead */
2165 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2167 if (return_status != FILE_CONT)
2168 goto ret;
2169 oktoret:
2170 file_progress_show_source (ctx, NULL);
2171 file_progress_show (ctx, 0, 0, "", FALSE);
2173 return_status = check_progress_buttons (ctx);
2174 if (return_status != FILE_CONT)
2175 goto ret;
2177 mc_refresh ();
2178 if (ctx->erase_at_end)
2180 for (; erase_list && return_status != FILE_ABORT;)
2182 if (S_ISDIR (erase_list->st_mode))
2184 return_status = erase_dir_iff_empty (ctx, erase_list->name);
2186 else
2187 return_status = erase_file (tctx, ctx, erase_list->name);
2188 lp = erase_list;
2189 erase_list = erase_list->next;
2190 g_free (lp);
2193 erase_dir_iff_empty (ctx, s);
2195 ret:
2196 g_free (destdir);
2197 while (erase_list)
2199 lp = erase_list;
2200 erase_list = erase_list->next;
2201 g_free (lp);
2203 return return_status;
2206 /* }}} */
2208 /* --------------------------------------------------------------------------------------------- */
2209 /* {{{ Erase routines */
2211 FileProgressStatus
2212 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
2214 FileProgressStatus error;
2216 if (strcmp (s, "..") == 0)
2217 return FILE_SKIP;
2219 if (strcmp (s, ".") == 0)
2220 return FILE_SKIP;
2222 file_progress_show_deleting (ctx, s);
2223 if (check_progress_buttons (ctx) == FILE_ABORT)
2224 return FILE_ABORT;
2225 mc_refresh ();
2227 /* The old way to detect a non empty directory was:
2228 error = my_rmdir (s);
2229 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2230 For the linux user space nfs server (nfs-server-2.2beta29-2)
2231 we would have to check also for EIO. I hope the new way is
2232 fool proof. (Norbert)
2234 error = check_dir_is_empty (s);
2235 if (error == 0)
2236 { /* not empty */
2237 error = query_recursive (ctx, s);
2238 if (error == FILE_CONT)
2239 return recursive_erase (tctx, ctx, s);
2240 else
2241 return error;
2244 while (my_rmdir (s) == -1 && !ctx->skip_all)
2246 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2247 if (error != FILE_RETRY)
2248 return error;
2251 return FILE_CONT;
2254 /* }}} */
2256 /* --------------------------------------------------------------------------------------------- */
2257 /* {{{ Panel operate routines */
2259 ComputeDirSizeUI *
2260 compute_dir_size_create_ui (void)
2262 ComputeDirSizeUI *ui;
2264 const char *b_name = N_("&Abort");
2266 #ifdef ENABLE_NLS
2267 b_name = _(b_name);
2268 #endif
2270 ui = g_new (ComputeDirSizeUI, 1);
2272 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
2273 NULL, _("Directory scanning"), DLG_CENTER);
2274 ui->dirname = label_new (3, 3, "");
2275 add_widget (ui->dlg, ui->dirname);
2277 add_widget (ui->dlg,
2278 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
2279 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2281 /* We will manage the dialog without any help,
2282 that's why we have to call init_dlg */
2283 init_dlg (ui->dlg);
2285 return ui;
2288 /* --------------------------------------------------------------------------------------------- */
2290 void
2291 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2293 if (ui != NULL)
2295 /* schedule to update passive panel */
2296 other_panel->dirty = 1;
2298 /* close and destroy dialog */
2299 dlg_run_done (ui->dlg);
2300 destroy_dlg (ui->dlg);
2301 g_free (ui);
2305 /* --------------------------------------------------------------------------------------------- */
2307 FileProgressStatus
2308 compute_dir_size_update_ui (const void *ui, const char *dirname)
2310 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2311 int c;
2312 Gpm_Event event;
2314 if (ui == NULL)
2315 return FILE_CONT;
2317 label_set_text (this->dirname, str_trunc (dirname, this->dlg->cols - 6));
2319 event.x = -1; /* Don't show the GPM cursor */
2320 c = tty_get_event (&event, FALSE, FALSE);
2321 if (c == EV_NONE)
2322 return FILE_CONT;
2324 /* Reinitialize to avoid old values after events other than
2325 selecting a button */
2326 this->dlg->ret_value = FILE_CONT;
2328 dlg_process_event (this->dlg, c, &event);
2330 switch (this->dlg->ret_value)
2332 case B_CANCEL:
2333 case FILE_ABORT:
2334 return FILE_ABORT;
2335 default:
2336 return FILE_CONT;
2340 /* --------------------------------------------------------------------------------------------- */
2342 * compute_dir_size:
2344 * Computes the number of bytes used by the files in a directory
2347 FileProgressStatus
2348 compute_dir_size (const char *dirname, const void *ui,
2349 compute_dir_size_callback cback,
2350 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2352 int res;
2353 struct stat s;
2354 DIR *dir;
2355 struct dirent *dirent;
2356 FileProgressStatus ret = FILE_CONT;
2358 if (!compute_symlinks)
2360 res = mc_lstat (dirname, &s);
2361 if (res != 0)
2362 return ret;
2364 /* don't scan symlink to directory */
2365 if (S_ISLNK (s.st_mode))
2367 (*ret_marked)++;
2368 *ret_total += (uintmax_t) s.st_size;
2369 return ret;
2373 dir = mc_opendir (dirname);
2375 if (dir == NULL)
2376 return ret;
2378 while ((dirent = mc_readdir (dir)) != NULL)
2380 char *fullname;
2382 ret = (cback != NULL) ? cback (ui, dirname) : FILE_CONT;
2384 if (ret != FILE_CONT)
2385 break;
2387 if (strcmp (dirent->d_name, ".") == 0)
2388 continue;
2389 if (strcmp (dirent->d_name, "..") == 0)
2390 continue;
2392 fullname = concat_dir_and_file (dirname, dirent->d_name);
2393 res = mc_lstat (fullname, &s);
2395 if (res != 0)
2397 g_free (fullname);
2398 continue;
2401 if (S_ISDIR (s.st_mode))
2403 size_t subdir_count = 0;
2404 uintmax_t subdir_bytes = 0;
2406 ret =
2407 compute_dir_size (fullname, ui, cback, &subdir_count, &subdir_bytes,
2408 compute_symlinks);
2410 if (ret != FILE_CONT)
2412 g_free (fullname);
2413 break;
2416 *ret_marked += subdir_count;
2417 *ret_total += subdir_bytes;
2419 else
2421 (*ret_marked)++;
2422 *ret_total += (uintmax_t) s.st_size;
2425 g_free (fullname);
2428 mc_closedir (dir);
2430 return ret;
2433 /* --------------------------------------------------------------------------------------------- */
2435 * panel_operate:
2437 * Performs one of the operations on the selection on the source_panel
2438 * (copy, delete, move).
2440 * Returns TRUE if did change the directory
2441 * structure, Returns FALSE if user aborted
2443 * force_single forces operation on the current entry and affects
2444 * default destination. Current filename is used as default.
2447 gboolean
2448 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2450 WPanel *panel = (WPanel *) source_panel;
2451 const gboolean single_entry = force_single || (panel->marked <= 1)
2452 || (get_current_type () == view_tree);
2454 char *source = NULL;
2455 #ifdef WITH_FULL_PATHS
2456 char *source_with_path = NULL;
2457 #else
2458 #define source_with_path source
2459 #endif /* !WITH_FULL_PATHS */
2460 char *dest = NULL;
2461 char *temp = NULL;
2462 char *save_cwd = NULL, *save_dest = NULL;
2463 struct stat src_stat;
2464 gboolean ret_val = TRUE;
2465 int i;
2466 FileProgressStatus value;
2467 FileOpContext *ctx;
2468 FileOpTotalContext *tctx;
2470 gboolean do_bg = FALSE; /* do background operation? */
2472 static gboolean i18n_flag = FALSE;
2473 if (!i18n_flag)
2475 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2476 op_names[i] = Q_ (op_names[i]);
2477 i18n_flag = TRUE;
2480 free_linklist (&linklist);
2481 free_linklist (&dest_dirs);
2483 if (single_entry)
2485 if (force_single)
2486 source = selection (panel)->fname;
2487 else
2488 source = panel_get_file (panel);
2490 if (strcmp (source, "..") == 0)
2492 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2493 return FALSE;
2496 /* Update stat to get actual info */
2497 if (mc_stat (source, &src_stat) != 0)
2499 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2500 path_trunc (source, 30), unix_error_string (errno));
2502 /* Directory was changed outside MC. Reload it forced */
2503 if (!panel->is_panelized)
2505 panel_update_flags_t flags = UP_RELOAD;
2507 /* don't update panelized panel */
2508 if (get_other_type () == view_listing && other_panel->is_panelized)
2509 flags |= UP_ONLY_CURRENT;
2511 update_panels (flags, UP_KEEPSEL);
2514 return FALSE;
2518 ctx = file_op_context_new (operation);
2520 /* Show confirmation dialog */
2521 if (operation != OP_DELETE)
2523 char *dest_dir;
2524 char *dest_dir_;
2525 char *format;
2527 /* Forced single operations default to the original name */
2528 if (force_single)
2529 dest_dir = source;
2530 else if (get_other_type () == view_listing)
2531 dest_dir = other_panel->cwd;
2532 else
2533 dest_dir = panel->cwd;
2535 * Add trailing backslash only when do non-local ops.
2536 * It saves user from occasional file renames (when destination
2537 * dir is deleted)
2539 if (!force_single && dest_dir[0] != '\0' && dest_dir[strlen (dest_dir) - 1] != PATH_SEP)
2541 /* add trailing separator */
2542 dest_dir_ = g_strconcat (dest_dir, PATH_SEP_STR, (char *) NULL);
2544 else
2546 /* just copy */
2547 dest_dir_ = g_strdup (dest_dir);
2550 if (dest_dir_ == NULL)
2552 ret_val = FALSE;
2553 goto ret_fast;
2556 /* Generate confirmation prompt */
2557 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2559 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2560 source != NULL ? (void *) source
2561 : (void *) &panel->marked, dest_dir_, &do_bg);
2563 g_free (format);
2564 g_free (dest_dir_);
2566 if (dest == NULL || dest[0] == '\0')
2568 g_free (dest);
2569 ret_val = FALSE;
2570 goto ret_fast;
2573 else if (confirm_delete)
2575 char *format;
2576 char fmd_buf[BUF_MEDIUM];
2578 /* Generate confirmation prompt */
2579 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2581 if (source == NULL)
2582 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2583 else
2585 const int fmd_xlen = 64;
2586 i = fmd_xlen - str_term_width1 (format) - 4;
2587 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2590 g_free (format);
2592 if (safe_delete)
2593 query_set_sel (1);
2595 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2597 if (i != 0)
2599 ret_val = FALSE;
2600 goto ret_fast;
2604 tctx = file_op_total_context_new ();
2605 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2608 filegui_dialog_type_t dialog_type;
2610 if (operation == OP_DELETE)
2611 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2612 else
2614 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2615 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2617 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2618 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2621 /* Background also need ctx->ui, but not full */
2622 if (do_bg)
2623 file_op_context_create_ui_without_init (ctx, TRUE, dialog_type);
2624 else
2625 file_op_context_create_ui (ctx, TRUE, dialog_type);
2628 #ifdef WITH_BACKGROUND
2629 /* Did the user select to do a background operation? */
2630 if (do_bg)
2632 int v;
2634 v = do_background (ctx, g_strconcat (op_names[operation], ": ", panel->cwd, (char *) NULL));
2635 if (v == -1)
2636 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2638 /* If we are the parent */
2639 if (v == 1)
2641 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
2642 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
2643 /* file_op_context_destroy (ctx); */
2644 return FALSE;
2647 #endif /* WITH_BACKGROUND */
2649 /* Initialize things */
2650 /* We do not want to trash cache every time file is
2651 created/touched. However, this will make our cache contain
2652 invalid data. */
2653 if ((dest != NULL) && (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1)))
2654 save_dest = g_strdup (dest);
2656 if ((panel->cwd[0] != '\0') && (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1)))
2657 save_cwd = g_strdup (panel->cwd);
2659 /* Now, let's do the job */
2661 /* This code is only called by the tree and panel code */
2662 if (single_entry)
2664 /* We now have ETA in all cases */
2666 /* One file: FIXME mc_chdir will take user out of any vfs */
2667 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2669 vfs_path_t *vpath;
2670 int chdir_retcode;
2672 vpath = vfs_path_from_str (PATH_SEP_STR);
2673 chdir_retcode = mc_chdir (vpath);
2674 vfs_path_free (vpath);
2675 if (chdir_retcode < 0)
2677 ret_val = FALSE;
2678 goto clean_up;
2682 /* The source and src_stat variables have been initialized before */
2683 #ifdef WITH_FULL_PATHS
2684 if (g_path_is_absolute (source))
2685 source_with_path = g_strdup (source);
2686 else
2687 source_with_path = mc_build_filename (panel->cwd, source, (char *) NULL);
2688 #endif /* WITH_FULL_PATHS */
2690 if (panel_operate_init_totals (operation, panel, source_with_path, ctx) == FILE_CONT)
2692 if (operation == OP_DELETE)
2694 if (S_ISDIR (src_stat.st_mode))
2695 value = erase_dir (tctx, ctx, source_with_path);
2696 else
2697 value = erase_file (tctx, ctx, source_with_path);
2699 else
2701 temp = transform_source (ctx, source_with_path);
2702 if (temp == NULL)
2703 value = transform_error;
2704 else
2706 char *repl_dest, *temp2;
2708 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2709 temp2 = concat_dir_and_file (repl_dest, temp);
2710 g_free (temp);
2711 g_free (repl_dest);
2712 g_free (dest);
2713 dest = temp2;
2715 switch (operation)
2717 case OP_COPY:
2718 /* we use file_mask_op_follow_links only with OP_COPY */
2719 ctx->stat_func (source_with_path, &src_stat);
2721 if (S_ISDIR (src_stat.st_mode))
2722 value = copy_dir_dir (tctx, ctx, source_with_path, dest,
2723 TRUE, FALSE, FALSE, NULL);
2724 else
2725 value = copy_file_file (tctx, ctx, source_with_path, dest);
2726 break;
2728 case OP_MOVE:
2729 if (S_ISDIR (src_stat.st_mode))
2730 value = move_dir_dir (tctx, ctx, source_with_path, dest);
2731 else
2732 value = move_file_file (tctx, ctx, source_with_path, dest);
2733 break;
2735 default:
2736 /* Unknown file operation */
2737 abort ();
2740 } /* Copy or move operation */
2742 if ((value == FILE_CONT) && !force_single)
2743 unmark_files (panel);
2746 else
2748 /* Many files */
2750 /* Check destination for copy or move operation */
2751 while (operation != OP_DELETE)
2753 int dst_result;
2754 struct stat dst_stat;
2756 dst_result = mc_stat (dest, &dst_stat);
2758 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2759 break;
2761 if (ctx->skip_all
2762 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2763 goto clean_up;
2766 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2768 /* Loop for every file, perform the actual copy operation */
2769 for (i = 0; i < panel->count; i++)
2771 if (!panel->dir.list[i].f.marked)
2772 continue; /* Skip the unmarked ones */
2774 source = panel->dir.list[i].fname;
2775 src_stat = panel->dir.list[i].st;
2777 #ifdef WITH_FULL_PATHS
2778 g_free (source_with_path);
2779 if (g_path_is_absolute (source))
2780 source_with_path = g_strdup (source);
2781 else
2782 source_with_path = mc_build_filename (panel->cwd, source, (char *) NULL);
2783 #endif /* WITH_FULL_PATHS */
2785 if (operation == OP_DELETE)
2787 if (S_ISDIR (src_stat.st_mode))
2788 value = erase_dir (tctx, ctx, source_with_path);
2789 else
2790 value = erase_file (tctx, ctx, source_with_path);
2792 else
2794 temp = transform_source (ctx, source_with_path);
2796 if (temp == NULL)
2797 value = transform_error;
2798 else
2800 char *temp2, *temp3, *repl_dest;
2802 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2803 temp2 = concat_dir_and_file (repl_dest, temp);
2804 g_free (temp);
2805 g_free (repl_dest);
2806 temp3 = source_with_path;
2807 source_with_path = strutils_shell_unescape (source_with_path);
2808 g_free (temp3);
2809 temp3 = temp2;
2810 temp2 = strutils_shell_unescape (temp2);
2811 g_free (temp3);
2813 switch (operation)
2815 case OP_COPY:
2816 /* we use file_mask_op_follow_links only with OP_COPY */
2817 ctx->stat_func (source_with_path, &src_stat);
2818 if (S_ISDIR (src_stat.st_mode))
2819 value = copy_dir_dir (tctx, ctx, source_with_path, temp2,
2820 TRUE, FALSE, FALSE, NULL);
2821 else
2822 value = copy_file_file (tctx, ctx, source_with_path, temp2);
2823 free_linklist (&dest_dirs);
2824 break;
2826 case OP_MOVE:
2827 if (S_ISDIR (src_stat.st_mode))
2828 value = move_dir_dir (tctx, ctx, source_with_path, temp2);
2829 else
2830 value = move_file_file (tctx, ctx, source_with_path, temp2);
2831 break;
2833 default:
2834 /* Unknown file operation */
2835 abort ();
2838 g_free (temp2);
2840 } /* Copy or move operation */
2842 if (value == FILE_ABORT)
2843 break;
2845 if (value == FILE_CONT)
2846 do_file_mark (panel, i, 0);
2848 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2850 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2851 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
2854 if (operation != OP_DELETE)
2855 file_progress_show (ctx, 0, 0, "", FALSE);
2857 if (check_progress_buttons (ctx) == FILE_ABORT)
2858 break;
2860 mc_refresh ();
2861 } /* Loop for every file */
2863 } /* Many entries */
2865 clean_up:
2866 /* Clean up */
2867 if (save_cwd != NULL)
2869 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2870 g_free (save_cwd);
2873 if (save_dest != NULL)
2875 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2876 g_free (save_dest);
2879 free_linklist (&linklist);
2880 free_linklist (&dest_dirs);
2881 #ifdef WITH_FULL_PATHS
2882 g_free (source_with_path);
2883 #endif /* WITH_FULL_PATHS */
2884 g_free (dest);
2885 g_free (ctx->dest_mask);
2886 ctx->dest_mask = NULL;
2888 #ifdef WITH_BACKGROUND
2889 /* Let our parent know we are saying bye bye */
2890 if (mc_global.we_are_background)
2892 int cur_pid = getpid ();
2893 /* Send pid to parent with child context, it is fork and
2894 don't modify real parent ctx */
2895 ctx->pid = cur_pid;
2896 parent_call ((void *) end_bg_process, ctx, 0);
2898 vfs_shut ();
2899 _exit (0);
2901 #endif /* WITH_BACKGROUND */
2903 file_op_total_context_destroy (tctx);
2904 ret_fast:
2905 file_op_context_destroy (ctx);
2907 return ret_val;
2910 /* }}} */
2912 /* --------------------------------------------------------------------------------------------- */
2913 /* {{{ Query/status report routines */
2914 /** Report error with one file */
2915 FileProgressStatus
2916 file_error (const char *format, const char *file)
2918 char buf[BUF_MEDIUM];
2920 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
2922 return do_file_error (buf);
2925 /* --------------------------------------------------------------------------------------------- */
2928 Cause emacs to enter folding mode for this file:
2929 Local variables:
2930 end: