Changed interface of function mc_symlink()
[midnight-commander.git] / src / filemanager / file.c
blobd25b15542075598803fa8a7167c9676f305e8d7d
1 /*
2 File management.
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Janne Kukonlehto, 1994, 1995
10 Fred Leeflang, 1994, 1995
11 Miguel de Icaza, 1994, 1995, 1996
12 Jakub Jelinek, 1995, 1996
13 Norbert Warmuth, 1997
14 Pavel Machek, 1998
16 The copy code was based in GNU's cp, and was written by:
17 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
19 The move code was based in GNU's mv, and was written by:
20 Mike Parker and David MacKenzie.
22 Janne Kukonlehto added much error recovery to them for being used
23 in an interactive program.
25 This file is part of the Midnight Commander.
27 The Midnight Commander is free software: you can redistribute it
28 and/or modify it under the terms of the GNU General Public License as
29 published by the Free Software Foundation, either version 3 of the License,
30 or (at your option) any later version.
32 The Midnight Commander is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program. If not, see <http://www.gnu.org/licenses/>.
42 * Please note that all dialogs used here must be safe for background
43 * operations.
46 /** \file file.c
47 * \brief Source: file management
50 /* {{{ Include files */
52 #include <config.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
62 #include <fcntl.h>
64 #include "lib/global.h"
65 #include "lib/tty/tty.h"
66 #include "lib/tty/key.h"
67 #include "lib/search.h"
68 #include "lib/strescape.h"
69 #include "lib/strutil.h"
70 #include "lib/util.h"
71 #include "lib/vfs/vfs.h"
72 #include "lib/widget.h"
74 #include "src/setup.h"
75 #ifdef ENABLE_BACKGROUND
76 #include "src/background.h" /* do_background() */
77 #endif
79 #include "layout.h" /* rotate_dash() */
81 /* Needed for current_panel, other_panel and WTree */
82 #include "dir.h"
83 #include "filegui.h"
84 #include "tree.h"
85 #include "midnight.h" /* current_panel */
87 #include "file.h"
89 /* }}} */
91 /*** global variables ****************************************************************************/
93 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
94 const char *op_names[3] = {
95 N_("DialogTitle|Copy"),
96 N_("DialogTitle|Move"),
97 N_("DialogTitle|Delete")
100 /*** file scope macro definitions ****************************************************************/
102 /* Hack: the vfs code should not rely on this */
103 #define WITH_FULL_PATHS 1
105 #define FILEOP_UPDATE_INTERVAL 2
106 #define FILEOP_STALLING_INTERVAL 4
108 /*** file scope type declarations ****************************************************************/
110 /* This is a hard link cache */
111 struct link
113 struct link *next;
114 struct vfs_class *vfs;
115 dev_t dev;
116 ino_t ino;
117 short linkcount;
118 mode_t st_mode;
119 char name[1];
122 /* Status of the destination file */
123 typedef enum
125 DEST_NONE = 0, /* Not created */
126 DEST_SHORT = 1, /* Created, not fully copied */
127 DEST_FULL = 2 /* Created, fully copied */
128 } dest_status_t;
131 * This array introduced to avoid translation problems. The former (op_names)
132 * is assumed to be nouns, suitable in dialog box titles; this one should
133 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
134 * (I don't use spaces around the words, because someday they could be
135 * dropped, when widgets get smarter)
138 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
139 static const char *op_names1[] = {
140 N_("FileOperation|Copy"),
141 N_("FileOperation|Move"),
142 N_("FileOperation|Delete")
146 * These are formats for building a prompt. Parts encoded as follows:
147 * %o - operation from op_names1
148 * %f - file/files or files/directories, as appropriate
149 * %m - "with source mask" or question mark for delete
150 * %s - source name (truncated)
151 * %d - number of marked files
152 * %e - "to:" or question mark for delete
154 * xgettext:no-c-format */
155 static const char *one_format = N_("%o %f \"%s\"%m");
156 /* xgettext:no-c-format */
157 static const char *many_format = N_("%o %d %f%m");
159 static const char *prompt_parts[] = {
160 N_("file"),
161 N_("files"),
162 N_("directory"),
163 N_("directories"),
164 N_("files/directories"),
165 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
166 N_(" with source mask:"),
167 N_("to:")
170 static const char *question_format = N_("%s?");
172 /*** file scope variables ************************************************************************/
174 /* the hard link cache */
175 static struct link *linklist = NULL;
177 /* the files-to-be-erased list */
178 static struct link *erase_list;
181 * In copy_dir_dir we use two additional single linked lists: The first -
182 * variable name `parent_dirs' - holds information about already copied
183 * directories and is used to detect cyclic symbolic links.
184 * The second (`dest_dirs' below) holds information about just created
185 * target directories and is used to detect when an directory is copied
186 * into itself (we don't want to copy infinitly).
187 * Both lists don't use the linkcount and name structure members of struct
188 * link.
190 static struct link *dest_dirs = NULL;
192 static FileProgressStatus transform_error = FILE_CONT;
194 /*** file scope functions ************************************************************************/
195 /* --------------------------------------------------------------------------------------------- */
197 static char *
198 transform_source (FileOpContext * ctx, const char *source)
200 char *s, *q;
201 char *fnsource;
203 s = g_strdup (source);
205 /* We remove \n from the filename since regex routines would use \n as an anchor */
206 /* this is just to be allowed to maniupulate file names with \n on it */
207 for (q = s; *q != '\0'; q++)
208 if (*q == '\n')
209 *q = ' ';
211 fnsource = (char *) x_basename (s);
213 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
214 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
215 else
217 q = NULL;
218 transform_error = FILE_SKIP;
221 g_free (s);
222 return q;
225 /* --------------------------------------------------------------------------------------------- */
227 static void
228 free_linklist (struct link **lc_linklist)
230 struct link *lp, *lp2;
232 for (lp = *lc_linklist; lp != NULL; lp = lp2)
234 lp2 = lp->next;
235 g_free (lp);
237 *lc_linklist = NULL;
240 /* --------------------------------------------------------------------------------------------- */
242 static int
243 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
245 vfs_path_t *vpath;
246 vfs_path_element_t *vpath_element;
247 ino_t ino = sb->st_ino;
248 dev_t dev = sb->st_dev;
250 vpath = vfs_path_from_str (path);
251 vpath_element = vfs_path_get_by_index (vpath, -1);
253 while (lp != NULL)
255 if (lp->vfs == vpath_element->class)
256 if (lp->ino == ino && lp->dev == dev)
257 return 1;
258 lp = lp->next;
260 vfs_path_free (vpath);
261 return 0;
264 /* --------------------------------------------------------------------------------------------- */
266 * Check and made hardlink
268 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
269 * and a hardlink was succesfully made
272 static gboolean
273 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
275 struct link *lp;
276 vfs_path_t *src_vpath, *dst_vpath;
278 struct vfs_class *my_vfs;
279 ino_t ino = pstat->st_ino;
280 dev_t dev = pstat->st_dev;
281 struct stat link_stat;
282 const char *p;
284 src_vpath = vfs_path_from_str (src_name);
286 if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
288 vfs_path_free (src_vpath);
289 return FALSE;
291 my_vfs = vfs_path_get_by_index (src_vpath, -1)->class;
292 dst_vpath = vfs_path_from_str (dst_name);
294 for (lp = linklist; lp != NULL; lp = lp->next)
295 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev)
297 struct vfs_class *lp_name_class;
298 int stat_result;
299 vfs_path_t *tmp_vpath;
301 tmp_vpath = vfs_path_from_str (lp->name);
302 lp_name_class = vfs_path_get_by_index (tmp_vpath, -1)->class;
303 stat_result = mc_stat (tmp_vpath, &link_stat);
304 vfs_path_free (tmp_vpath);
306 if (!stat_result && link_stat.st_ino == ino
307 && link_stat.st_dev == dev && lp_name_class == my_vfs)
309 struct vfs_class *p_class, *dst_name_class;
311 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
312 was copied to */
314 dst_name_class = vfs_path_get_by_index (dst_vpath, -1)->class;
316 tmp_vpath = vfs_path_from_str (p);
317 p_class = vfs_path_get_by_index (tmp_vpath, -1)->class;
319 if (dst_name_class == p_class)
321 if (!mc_stat (tmp_vpath, &link_stat))
323 if (!mc_link (tmp_vpath, dst_vpath))
325 vfs_path_free (tmp_vpath);
326 vfs_path_free (src_vpath);
327 vfs_path_free (dst_vpath);
328 return TRUE;
332 vfs_path_free (tmp_vpath);
335 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
336 vfs_path_free (src_vpath);
337 vfs_path_free (dst_vpath);
338 return FALSE;
340 lp = (struct link *) g_try_malloc (sizeof (struct link) + strlen (src_name)
341 + strlen (dst_name) + 1);
342 if (lp)
344 char *lpdstname;
345 lp->vfs = my_vfs;
346 lp->ino = ino;
347 lp->dev = dev;
348 strcpy (lp->name, src_name);
349 lpdstname = lp->name + strlen (lp->name) + 1;
350 strcpy (lpdstname, dst_name);
351 lp->next = linklist;
352 linklist = lp;
354 vfs_path_free (src_vpath);
355 vfs_path_free (dst_vpath);
356 return FALSE;
359 /* --------------------------------------------------------------------------------------------- */
361 * Duplicate the contents of the symbolic link src_path in dst_path.
362 * Try to make a stable symlink if the option "stable symlink" was
363 * set in the file mask dialog.
364 * If dst_path is an existing symlink it will be deleted silently
365 * (upper levels take already care of existing files at dst_path).
368 static FileProgressStatus
369 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
371 char link_target[MC_MAXPATHLEN];
372 int len;
373 FileProgressStatus return_status;
374 struct stat sb;
375 vfs_path_t *src_vpath;
376 vfs_path_t *dst_vpath;
377 gboolean dst_is_symlink;
378 vfs_path_t *link_target_vpath = NULL;
380 src_vpath = vfs_path_from_str (src_path);
381 dst_vpath = vfs_path_from_str (dst_path);
382 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
384 retry_src_readlink:
385 len = mc_readlink (src_vpath, link_target, MC_MAXPATHLEN - 1);
386 if (len < 0)
388 if (ctx->skip_all)
389 return_status = FILE_SKIPALL;
390 else
392 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
393 if (return_status == FILE_SKIPALL)
394 ctx->skip_all = TRUE;
395 if (return_status == FILE_RETRY)
396 goto retry_src_readlink;
398 goto ret;
400 link_target[len] = 0;
402 if (ctx->stable_symlinks)
405 if (!vfs_file_is_local (src_vpath) || !vfs_file_is_local (dst_vpath))
407 message (D_ERROR, MSG_ERROR,
408 _("Cannot make stable symlinks across"
409 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
410 ctx->stable_symlinks = FALSE;
414 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
416 char *p, *q, *s;
418 const char *r = strrchr (src_path, PATH_SEP);
420 if (r)
422 p = g_strndup (src_path, r - src_path + 1);
423 if (g_path_is_absolute (dst_path))
424 q = g_strdup (dst_path);
425 else
426 q = g_strconcat (p, dst_path, (char *) NULL);
427 s = strrchr (q, PATH_SEP);
428 if (s)
430 s[1] = 0;
431 s = g_strconcat (p, link_target, (char *) NULL);
432 g_free (p);
433 g_strlcpy (link_target, s, sizeof (link_target));
434 g_free (s);
435 s = diff_two_paths (q, link_target);
436 if (s)
438 g_strlcpy (link_target, s, sizeof (link_target));
439 g_free (s);
442 else
443 g_free (p);
444 g_free (q);
447 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
449 retry_dst_symlink:
450 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
452 /* Success */
453 return_status = FILE_CONT;
454 goto ret;
457 * if dst_exists, it is obvious that this had failed.
458 * We can delete the old symlink and try again...
460 if (dst_is_symlink)
462 if (mc_unlink (dst_vpath) == 0)
463 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
465 /* Success */
466 return_status = FILE_CONT;
467 goto ret;
470 if (ctx->skip_all)
471 return_status = FILE_SKIPALL;
472 else
474 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
475 if (return_status == FILE_SKIPALL)
476 ctx->skip_all = TRUE;
477 if (return_status == FILE_RETRY)
478 goto retry_dst_symlink;
481 ret:
482 vfs_path_free (src_vpath);
483 vfs_path_free (dst_vpath);
484 vfs_path_free (link_target_vpath);
485 return return_status;
488 /* --------------------------------------------------------------------------------------------- */
490 static FileProgressStatus
491 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add)
493 struct timeval tv_current;
494 static struct timeval tv_start = { };
496 tctx->progress_count++;
497 tctx->progress_bytes += (uintmax_t) add;
499 if (tv_start.tv_sec == 0)
501 gettimeofday (&tv_start, (struct timezone *) NULL);
503 gettimeofday (&tv_current, (struct timezone *) NULL);
504 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
506 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
508 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
509 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
511 tv_start.tv_sec = tv_current.tv_sec;
514 return check_progress_buttons (ctx);
517 /* --------------------------------------------------------------------------------------------- */
519 static FileProgressStatus
520 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
522 char *msg;
523 int result = 0;
524 const char *head_msg;
526 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
528 msg = g_strdup_printf (fmt, a, b);
529 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
530 g_free (msg);
531 do_refresh ();
533 return (result == 1) ? FILE_ABORT : FILE_SKIP;
536 /* --------------------------------------------------------------------------------------------- */
538 static FileProgressStatus
539 warn_same_file (const char *fmt, const char *a, const char *b)
541 #ifdef ENABLE_BACKGROUND
542 /* *INDENT-OFF* */
543 union
545 void *p;
546 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
547 const char *a, const char *b);
548 } pntr;
549 /* *INDENT-ON* */
551 pntr.f = real_warn_same_file;
553 if (mc_global.we_are_background)
554 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
555 #endif
556 return real_warn_same_file (Foreground, fmt, a, b);
559 /* --------------------------------------------------------------------------------------------- */
560 /* {{{ Query/status report routines */
562 static FileProgressStatus
563 real_do_file_error (enum OperationMode mode, const char *error)
565 int result;
566 const char *msg;
568 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
569 result =
570 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
572 switch (result)
574 case 0:
575 do_refresh ();
576 return FILE_SKIP;
578 case 1:
579 do_refresh ();
580 return FILE_SKIPALL;
582 case 2:
583 do_refresh ();
584 return FILE_RETRY;
586 case 3:
587 default:
588 return FILE_ABORT;
592 /* --------------------------------------------------------------------------------------------- */
594 static FileProgressStatus
595 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
597 gchar *text;
599 if (ctx->recursive_result < RECURSIVE_ALWAYS)
601 const char *msg = mode == Foreground
602 ? _("\nDirectory not empty.\nDelete it recursively?")
603 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
604 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
606 if (safe_delete)
607 query_set_sel (1);
609 ctx->recursive_result =
610 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
611 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
613 if (ctx->recursive_result != RECURSIVE_ABORT)
614 do_refresh ();
615 g_free (text);
618 switch (ctx->recursive_result)
620 case RECURSIVE_YES:
621 case RECURSIVE_ALWAYS:
622 return FILE_CONT;
624 case RECURSIVE_NO:
625 case RECURSIVE_NEVER:
626 return FILE_SKIP;
628 case RECURSIVE_ABORT:
629 default:
630 return FILE_ABORT;
634 /* --------------------------------------------------------------------------------------------- */
636 #ifdef ENABLE_BACKGROUND
637 static FileProgressStatus
638 do_file_error (const char *str)
640 union
642 void *p;
643 FileProgressStatus (*f) (enum OperationMode, const char *);
644 } pntr;
645 pntr.f = real_do_file_error;
647 if (mc_global.we_are_background)
648 return parent_call (pntr.p, NULL, 1, strlen (str), str);
649 else
650 return real_do_file_error (Foreground, str);
653 /* --------------------------------------------------------------------------------------------- */
655 static FileProgressStatus
656 query_recursive (FileOpContext * ctx, const char *s)
658 union
660 void *p;
661 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
662 } pntr;
663 pntr.f = real_query_recursive;
665 if (mc_global.we_are_background)
666 return parent_call (pntr.p, ctx, 1, strlen (s), s);
667 else
668 return real_query_recursive (ctx, Foreground, s);
671 /* --------------------------------------------------------------------------------------------- */
673 static FileProgressStatus
674 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
675 struct stat *_d_stat)
677 union
679 void *p;
680 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
681 struct stat *, struct stat *);
682 } pntr;
683 pntr.f = file_progress_real_query_replace;
685 if (mc_global.we_are_background)
686 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
687 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
688 else
689 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
692 #else
693 /* --------------------------------------------------------------------------------------------- */
695 static FileProgressStatus
696 do_file_error (const char *str)
698 return real_do_file_error (Foreground, str);
701 /* --------------------------------------------------------------------------------------------- */
703 static FileProgressStatus
704 query_recursive (FileOpContext * ctx, const char *s)
706 return real_query_recursive (ctx, Foreground, s);
709 /* --------------------------------------------------------------------------------------------- */
711 static FileProgressStatus
712 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
713 struct stat *_d_stat)
715 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
718 #endif /* !ENABLE_BACKGROUND */
720 /* --------------------------------------------------------------------------------------------- */
721 /** Report error with two files */
723 static FileProgressStatus
724 files_error (const char *format, const char *file1, const char *file2)
726 char buf[BUF_MEDIUM];
727 char *nfile1 = g_strdup (path_trunc (file1, 15));
728 char *nfile2 = g_strdup (path_trunc (file2, 15));
730 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
732 g_free (nfile1);
733 g_free (nfile2);
735 return do_file_error (buf);
738 /* }}} */
740 /* --------------------------------------------------------------------------------------------- */
742 static void
743 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
744 struct timeval tv_current, struct timeval tv_transfer_start,
745 off_t file_size, off_t n_read_total)
747 long dt;
749 /* 1. Update rotating dash after some time */
750 rotate_dash ();
752 /* 3. Compute ETA */
753 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
755 if (n_read_total == 0)
756 ctx->eta_secs = 0.0;
757 else
759 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
760 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
763 /* 4. Compute BPS rate */
764 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
765 if (ctx->bps_time < 1)
766 ctx->bps_time = 1;
767 ctx->bps = n_read_total / ctx->bps_time;
769 /* 5. Compute total ETA and BPS */
770 if (ctx->progress_bytes != 0)
772 uintmax_t remain_bytes;
774 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
775 #if 1
777 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
779 if (total_secs < 1)
780 total_secs = 1;
782 tctx->bps = tctx->copied_bytes / total_secs;
783 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
785 #else
786 /* broken on lot of little files */
787 tctx->bps_count++;
788 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
789 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
790 #endif
794 /* --------------------------------------------------------------------------------------------- */
796 /* {{{ Move routines */
797 static FileProgressStatus
798 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
800 struct stat src_stats, dst_stats;
801 FileProgressStatus return_status = FILE_CONT;
802 gboolean copy_done = FALSE;
803 gboolean old_ask_overwrite;
804 vfs_path_t *src_vpath, *dst_vpath;
806 file_progress_show_source (ctx, s);
807 file_progress_show_target (ctx, d);
808 if (check_progress_buttons (ctx) == FILE_ABORT)
809 return FILE_ABORT;
811 mc_refresh ();
812 src_vpath = vfs_path_from_str (s);
813 dst_vpath = vfs_path_from_str (d);
815 while (mc_lstat (src_vpath, &src_stats) != 0)
817 /* Source doesn't exist */
818 if (ctx->skip_all)
819 return_status = FILE_SKIPALL;
820 else
822 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
823 if (return_status == FILE_SKIPALL)
824 ctx->skip_all = TRUE;
826 if (return_status != FILE_RETRY)
828 vfs_path_free (src_vpath);
829 vfs_path_free (dst_vpath);
830 return return_status;
834 if (mc_lstat (dst_vpath, &dst_stats) == 0)
836 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
837 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
839 if (S_ISDIR (dst_stats.st_mode))
841 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
842 do_refresh ();
843 vfs_path_free (src_vpath);
844 vfs_path_free (dst_vpath);
845 return FILE_SKIP;
848 if (confirm_overwrite)
850 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
851 if (return_status != FILE_CONT)
853 vfs_path_free (src_vpath);
854 vfs_path_free (dst_vpath);
855 return return_status;
858 /* Ok to overwrite */
861 if (!ctx->do_append)
863 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
865 return_status = make_symlink (ctx, s, d);
866 if (return_status == FILE_CONT)
867 goto retry_src_remove;
868 else
870 vfs_path_free (src_vpath);
871 vfs_path_free (dst_vpath);
872 return return_status;
876 if (mc_rename (src_vpath, dst_vpath) == 0)
878 vfs_path_free (src_vpath);
879 vfs_path_free (dst_vpath);
880 return progress_update_one (tctx, ctx, src_stats.st_size);
883 #if 0
884 /* Comparison to EXDEV seems not to work in nfs if you're moving from
885 one nfs to the same, but on the server it is on two different
886 filesystems. Then nfs returns EIO instead of EXDEV.
887 Hope it will not hurt if we always in case of error try to copy/delete. */
888 else
889 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
891 if (errno != EXDEV)
893 if (ctx->skip_all)
894 return_status = FILE_SKIPALL;
895 else
897 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
898 if (return_status == FILE_SKIPALL)
899 ctx->skip_all = TRUE;
900 if (return_status == FILE_RETRY)
901 goto retry_rename;
903 vfs_path_free (src_vpath);
904 vfs_path_free (dst_vpath);
906 return return_status;
908 #endif
910 /* Failed because filesystem boundary -> copy the file instead */
911 old_ask_overwrite = tctx->ask_overwrite;
912 tctx->ask_overwrite = FALSE;
913 return_status = copy_file_file (tctx, ctx, s, d);
914 tctx->ask_overwrite = old_ask_overwrite;
915 if (return_status != FILE_CONT)
917 vfs_path_free (src_vpath);
918 vfs_path_free (dst_vpath);
919 return return_status;
922 copy_done = TRUE;
924 file_progress_show_source (ctx, NULL);
925 file_progress_show (ctx, 0, 0, "", FALSE);
927 return_status = check_progress_buttons (ctx);
928 if (return_status != FILE_CONT)
930 vfs_path_free (src_vpath);
931 vfs_path_free (dst_vpath);
932 return return_status;
935 mc_refresh ();
937 retry_src_remove:
938 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
940 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
941 if (return_status == FILE_RETRY)
942 goto retry_src_remove;
943 if (return_status == FILE_SKIPALL)
944 ctx->skip_all = TRUE;
946 vfs_path_free (src_vpath);
947 vfs_path_free (dst_vpath);
948 return return_status;
951 if (!copy_done)
952 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
954 vfs_path_free (src_vpath);
955 vfs_path_free (dst_vpath);
957 return return_status;
960 /* }}} */
962 /* --------------------------------------------------------------------------------------------- */
963 /* {{{ Erase routines */
964 /** Don't update progress status if progress_count==NULL */
966 static FileProgressStatus
967 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
969 int return_status;
970 struct stat buf;
971 vfs_path_t *vpath = vfs_path_from_str (s);
973 file_progress_show_deleting (ctx, s);
974 if (check_progress_buttons (ctx) == FILE_ABORT)
975 return FILE_ABORT;
976 mc_refresh ();
978 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
980 /* ignore, most likely the mc_unlink fails, too */
981 buf.st_size = 0;
984 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
986 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
987 if (return_status == FILE_ABORT)
989 vfs_path_free (vpath);
990 return return_status;
992 if (return_status == FILE_RETRY)
993 continue;
994 if (return_status == FILE_SKIPALL)
995 ctx->skip_all = TRUE;
996 break;
999 vfs_path_free (vpath);
1000 if (tctx->progress_count == 0)
1001 return FILE_CONT;
1002 return progress_update_one (tctx, ctx, buf.st_size);
1005 /* --------------------------------------------------------------------------------------------- */
1008 Recursive remove of files
1009 abort->cancel stack
1010 skip ->warn every level, gets default
1011 skipall->remove as much as possible
1013 static FileProgressStatus
1014 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
1016 struct dirent *next;
1017 struct stat buf;
1018 DIR *reading;
1019 char *path;
1020 FileProgressStatus return_status = FILE_CONT;
1021 vfs_path_t *vpath;
1023 if (strcmp (s, "..") == 0)
1024 return FILE_RETRY;
1026 vpath = vfs_path_from_str (s);
1027 reading = mc_opendir (vpath);
1029 if (reading == NULL)
1031 return_status = FILE_RETRY;
1032 goto ret;
1035 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1037 vfs_path_t *tmp_vpath;
1039 if (!strcmp (next->d_name, "."))
1040 continue;
1041 if (!strcmp (next->d_name, ".."))
1042 continue;
1043 path = concat_dir_and_file (s, next->d_name);
1044 tmp_vpath = vfs_path_from_str (path);
1045 if (mc_lstat (tmp_vpath, &buf) != 0)
1047 g_free (path);
1048 mc_closedir (reading);
1049 vfs_path_free (tmp_vpath);
1050 return_status = FILE_RETRY;
1051 goto ret;
1053 if (S_ISDIR (buf.st_mode))
1054 return_status = recursive_erase (tctx, ctx, path);
1055 else
1056 return_status = erase_file (tctx, ctx, path);
1057 vfs_path_free (tmp_vpath);
1058 g_free (path);
1060 mc_closedir (reading);
1061 if (return_status == FILE_ABORT)
1062 goto ret;
1064 file_progress_show_deleting (ctx, s);
1065 if (check_progress_buttons (ctx) == FILE_ABORT)
1067 return_status = FILE_ABORT;
1068 goto ret;
1070 mc_refresh ();
1072 while (my_rmdir (s) != 0 && !ctx->skip_all)
1074 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1075 if (return_status == FILE_RETRY)
1076 continue;
1077 if (return_status == FILE_ABORT)
1078 goto ret;
1079 if (return_status == FILE_SKIPALL)
1080 ctx->skip_all = TRUE;
1081 break;
1084 ret:
1085 vfs_path_free (vpath);
1086 return return_status;
1089 /* --------------------------------------------------------------------------------------------- */
1090 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1091 in the directory path points to, 0 else. */
1093 static int
1094 check_dir_is_empty (const char *path)
1096 DIR *dir;
1097 struct dirent *d;
1098 int i;
1099 vfs_path_t *vpath = vfs_path_from_str (path);
1101 dir = mc_opendir (vpath);
1102 if (!dir)
1104 vfs_path_free (vpath);
1105 return -1;
1108 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1110 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1111 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1112 continue; /* "." or ".." */
1113 i = 0;
1114 break;
1117 mc_closedir (dir);
1118 vfs_path_free (vpath);
1119 return i;
1122 /* --------------------------------------------------------------------------------------------- */
1124 static FileProgressStatus
1125 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1127 FileProgressStatus error;
1129 if (strcmp (s, "..") == 0)
1130 return FILE_SKIP;
1132 if (strcmp (s, ".") == 0)
1133 return FILE_SKIP;
1135 file_progress_show_deleting (ctx, s);
1136 if (check_progress_buttons (ctx) == FILE_ABORT)
1137 return FILE_ABORT;
1138 mc_refresh ();
1140 if (1 != check_dir_is_empty (s)) /* not empty or error */
1141 return FILE_CONT;
1143 while (my_rmdir (s) != 0 && !ctx->skip_all)
1145 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1146 if (error == FILE_SKIPALL)
1147 ctx->skip_all = TRUE;
1148 if (error != FILE_RETRY)
1149 return error;
1152 return FILE_CONT;
1155 /* }}} */
1157 /* --------------------------------------------------------------------------------------------- */
1158 /* {{{ Panel operate routines */
1161 * Return currently selected entry name or the name of the first marked
1162 * entry if there is one.
1165 static char *
1166 panel_get_file (WPanel * panel)
1168 if (get_current_type () == view_tree)
1170 WTree *tree;
1172 tree = (WTree *) get_panel_widget (get_current_index ());
1173 return tree_selected_name (tree);
1176 if (panel->marked != 0)
1178 int i;
1180 for (i = 0; i < panel->count; i++)
1181 if (panel->dir.list[i].f.marked)
1182 return panel->dir.list[i].fname;
1185 return panel->dir.list[panel->selected].fname;
1188 /* --------------------------------------------------------------------------------------------- */
1190 * panel_compute_totals:
1192 * compute the number of files and the number of bytes
1193 * used up by the whole selection, recursing directories
1194 * as required. In addition, it checks to see if it will
1195 * overwrite any files by doing the copy.
1198 static FileProgressStatus
1199 panel_compute_totals (const WPanel * panel, const void *ui,
1200 compute_dir_size_callback cback,
1201 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1203 int i;
1205 *ret_marked = 0;
1206 *ret_total = 0;
1208 for (i = 0; i < panel->count; i++)
1210 struct stat *s;
1212 if (!panel->dir.list[i].f.marked)
1213 continue;
1215 s = &panel->dir.list[i].st;
1217 if (S_ISDIR (s->st_mode))
1219 char *dir_name;
1220 size_t subdir_count = 0;
1221 uintmax_t subdir_bytes = 0;
1222 FileProgressStatus status;
1224 dir_name = concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1226 status = compute_dir_size (dir_name, ui, cback,
1227 &subdir_count, &subdir_bytes, compute_symlinks);
1228 g_free (dir_name);
1230 if (status != FILE_CONT)
1231 return FILE_ABORT;
1233 *ret_marked += subdir_count;
1234 *ret_total += subdir_bytes;
1236 else
1238 (*ret_marked)++;
1239 *ret_total += (uintmax_t) s->st_size;
1243 return FILE_CONT;
1246 /* --------------------------------------------------------------------------------------------- */
1248 /** Initialize variables for progress bars */
1249 static FileProgressStatus
1250 panel_operate_init_totals (FileOperation operation,
1251 const WPanel * panel, const char *source, FileOpContext * ctx)
1253 FileProgressStatus status;
1255 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1257 ComputeDirSizeUI *ui;
1259 ui = compute_dir_size_create_ui ();
1261 if (source != NULL)
1262 status = compute_dir_size (source, ui, compute_dir_size_update_ui,
1263 &ctx->progress_count, &ctx->progress_bytes,
1264 ctx->follow_links);
1265 else
1266 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1267 &ctx->progress_count, &ctx->progress_bytes,
1268 ctx->follow_links);
1270 compute_dir_size_destroy_ui (ui);
1272 ctx->progress_totals_computed = (status == FILE_CONT);
1274 else
1276 status = FILE_CONT;
1277 ctx->progress_count = panel->marked;
1278 ctx->progress_bytes = panel->total;
1279 ctx->progress_totals_computed = FALSE;
1282 return status;
1285 /* --------------------------------------------------------------------------------------------- */
1287 * Generate user prompt for panel operation.
1288 * single_source is the name if the source entry or NULL for multiple
1289 * entries.
1290 * src_stat is only used when single_source is not NULL.
1293 static char *
1294 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1295 gboolean single_source, const struct stat *src_stat)
1297 const char *sp, *cp;
1298 char format_string[BUF_MEDIUM];
1299 char *dp = format_string;
1300 gboolean build_question = FALSE;
1302 static gboolean i18n_flag = FALSE;
1303 if (!i18n_flag)
1305 size_t i;
1307 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1308 op_names1[i] = Q_ (op_names1[i]);
1310 #ifdef ENABLE_NLS
1311 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1312 prompt_parts[i] = _(prompt_parts[i]);
1314 one_format = _(one_format);
1315 many_format = _(many_format);
1316 question_format = _(question_format);
1317 #endif /* ENABLE_NLS */
1318 i18n_flag = TRUE;
1321 sp = single_source ? one_format : many_format;
1323 while (*sp != '\0')
1325 switch (*sp)
1327 case '%':
1328 cp = NULL;
1329 switch (sp[1])
1331 case 'o':
1332 cp = op_names1[operation];
1333 break;
1334 case 'm':
1335 if (operation == OP_DELETE)
1337 cp = "";
1338 build_question = TRUE;
1340 else
1341 cp = prompt_parts[5];
1342 break;
1343 case 'e':
1344 if (operation == OP_DELETE)
1346 cp = "";
1347 build_question = TRUE;
1349 else
1350 cp = prompt_parts[6];
1351 break;
1352 case 'f':
1353 if (single_source)
1354 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1355 else
1356 cp = (panel->marked == panel->dirs_marked)
1357 ? prompt_parts[3]
1358 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1359 break;
1360 default:
1361 *dp++ = *sp++;
1364 if (cp != NULL)
1366 sp += 2;
1367 while (*cp != '\0')
1368 *dp++ = *cp++;
1370 break;
1371 default:
1372 *dp++ = *sp++;
1375 *dp = '\0';
1377 if (build_question)
1379 char tmp[BUF_MEDIUM];
1381 memmove (tmp, format_string, sizeof (tmp));
1382 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1385 return g_strdup (format_string);
1388 /* --------------------------------------------------------------------------------------------- */
1390 #ifdef ENABLE_BACKGROUND
1391 static int
1392 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1394 int pid = ctx->pid;
1396 (void) mode;
1397 ctx->pid = 0;
1399 unregister_task_with_pid (pid);
1400 /* file_op_context_destroy(ctx); */
1401 return 1;
1403 #endif
1404 /* }}} */
1406 /* --------------------------------------------------------------------------------------------- */
1407 /*** public functions ****************************************************************************/
1408 /* --------------------------------------------------------------------------------------------- */
1410 FileProgressStatus
1411 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1412 const char *src_path, const char *dst_path)
1414 uid_t src_uid = (uid_t) - 1;
1415 gid_t src_gid = (gid_t) - 1;
1417 int src_desc, dest_desc = -1;
1418 int n_read, n_written;
1419 mode_t src_mode = 0; /* The mode of the source file */
1420 struct stat sb, sb2;
1421 struct utimbuf utb;
1422 gboolean dst_exists = FALSE, appending = FALSE;
1423 off_t file_size = -1;
1424 FileProgressStatus return_status, temp_status;
1425 struct timeval tv_transfer_start;
1426 dest_status_t dst_status = DEST_NONE;
1427 int open_flags;
1428 gboolean is_first_time = TRUE;
1429 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1431 /* FIXME: We should not be using global variables! */
1432 ctx->do_reget = 0;
1433 return_status = FILE_RETRY;
1435 file_progress_show_source (ctx, src_path);
1436 file_progress_show_target (ctx, dst_path);
1437 if (check_progress_buttons (ctx) == FILE_ABORT)
1438 return FILE_ABORT;
1440 mc_refresh ();
1442 dst_vpath = vfs_path_from_str (dst_path);
1443 while (mc_stat (dst_vpath, &sb2) == 0)
1445 if (S_ISDIR (sb2.st_mode))
1447 if (ctx->skip_all)
1448 return_status = FILE_SKIPALL;
1449 else
1451 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1452 if (return_status == FILE_SKIPALL)
1453 ctx->skip_all = TRUE;
1454 if (return_status == FILE_RETRY)
1455 continue;
1457 goto ret_fast;
1459 dst_exists = TRUE;
1460 break;
1463 src_vpath = vfs_path_from_str (src_path);
1464 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1466 if (ctx->skip_all)
1467 return_status = FILE_SKIPALL;
1468 else
1470 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1471 if (return_status == FILE_SKIPALL)
1472 ctx->skip_all = TRUE;
1474 if (return_status != FILE_RETRY)
1475 goto ret_fast;
1478 if (dst_exists)
1480 /* Destination already exists */
1481 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1483 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1484 src_path, dst_path);
1485 goto ret_fast;
1487 /* Should we replace destination? */
1488 if (tctx->ask_overwrite)
1490 ctx->do_reget = 0;
1491 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1492 if (return_status != FILE_CONT)
1493 goto ret_fast;
1497 if (!ctx->do_append)
1499 /* Check the hardlinks */
1500 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb))
1502 /* We have made a hardlink - no more processing is necessary */
1503 return_status = FILE_CONT;
1504 goto ret_fast;
1507 if (S_ISLNK (sb.st_mode))
1509 return_status = make_symlink (ctx, src_path, dst_path);
1510 goto ret_fast;
1513 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1514 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1516 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1517 && !ctx->skip_all)
1519 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1520 if (return_status == FILE_RETRY)
1521 continue;
1522 if (return_status == FILE_SKIPALL)
1523 ctx->skip_all = TRUE;
1524 goto ret_fast;
1526 /* Success */
1528 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1529 && !ctx->skip_all)
1531 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1532 if (temp_status == FILE_SKIP)
1533 break;
1534 if (temp_status == FILE_SKIPALL)
1535 ctx->skip_all = TRUE;
1536 if (temp_status != FILE_RETRY)
1538 return_status = temp_status;
1539 goto ret_fast;
1543 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1544 && !ctx->skip_all)
1546 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1547 if (temp_status == FILE_SKIP)
1548 break;
1549 if (temp_status == FILE_SKIPALL)
1550 ctx->skip_all = TRUE;
1551 if (temp_status != FILE_RETRY)
1553 return_status = temp_status;
1554 goto ret_fast;
1558 return_status = FILE_CONT;
1559 goto ret_fast;
1563 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1565 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1567 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1568 if (return_status == FILE_RETRY)
1569 continue;
1570 if (return_status == FILE_SKIPALL)
1571 ctx->skip_all = TRUE;
1572 if (return_status == FILE_SKIP)
1573 break;
1574 ctx->do_append = 0;
1575 goto ret_fast;
1578 if (ctx->do_reget != 0)
1580 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1582 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1583 ctx->do_reget = 0;
1584 ctx->do_append = FALSE;
1588 while (mc_fstat (src_desc, &sb) != 0)
1590 if (ctx->skip_all)
1591 return_status = FILE_SKIPALL;
1592 else
1594 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1595 if (return_status == FILE_RETRY)
1596 continue;
1597 if (return_status == FILE_SKIPALL)
1598 ctx->skip_all = TRUE;
1599 ctx->do_append = FALSE;
1601 goto ret;
1603 src_mode = sb.st_mode;
1604 src_uid = sb.st_uid;
1605 src_gid = sb.st_gid;
1606 utb.actime = sb.st_atime;
1607 utb.modtime = sb.st_mtime;
1608 file_size = sb.st_size;
1610 open_flags = O_WRONLY;
1611 if (dst_exists)
1613 if (ctx->do_append != 0)
1614 open_flags |= O_APPEND;
1615 else
1616 open_flags |= O_CREAT | O_TRUNC;
1618 else
1620 open_flags |= O_CREAT | O_EXCL;
1623 while ((dest_desc = mc_open (dst_path, open_flags, src_mode)) < 0)
1625 if (errno != EEXIST)
1627 if (ctx->skip_all)
1628 return_status = FILE_SKIPALL;
1629 else
1631 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1632 if (return_status == FILE_RETRY)
1633 continue;
1634 if (return_status == FILE_SKIPALL)
1635 ctx->skip_all = TRUE;
1636 ctx->do_append = FALSE;
1639 goto ret;
1641 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1643 appending = ctx->do_append;
1644 ctx->do_append = FALSE;
1646 /* Find out the optimal buffer size. */
1647 while (mc_fstat (dest_desc, &sb) != 0)
1649 if (ctx->skip_all)
1650 return_status = FILE_SKIPALL;
1651 else
1653 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1654 if (return_status == FILE_RETRY)
1655 continue;
1656 if (return_status == FILE_SKIPALL)
1657 ctx->skip_all = TRUE;
1659 goto ret;
1662 while (TRUE)
1664 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1665 if (errno == 0)
1666 break;
1668 if (ctx->skip_all)
1669 return_status = FILE_SKIPALL;
1670 else
1672 return_status =
1673 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1674 if (return_status == FILE_RETRY)
1675 continue;
1676 if (return_status == FILE_SKIPALL)
1677 ctx->skip_all = TRUE;
1679 mc_close (dest_desc);
1680 dest_desc = -1;
1681 mc_unlink (dst_vpath);
1682 dst_status = DEST_NONE;
1683 goto ret;
1686 ctx->eta_secs = 0.0;
1687 ctx->bps = 0;
1689 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1690 file_progress_show (ctx, 0, file_size, "", TRUE);
1691 else
1692 file_progress_show (ctx, 1, 1, "", TRUE);
1693 return_status = check_progress_buttons (ctx);
1694 mc_refresh ();
1696 if (return_status != FILE_CONT)
1697 goto ret;
1700 off_t n_read_total = 0;
1701 struct timeval tv_current, tv_last_update, tv_last_input;
1702 int secs, update_secs;
1703 const char *stalled_msg = "";
1705 tv_last_update = tv_transfer_start;
1707 while (TRUE)
1709 char buf[BUF_8K];
1711 /* src_read */
1712 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1713 n_read = -1;
1714 else
1715 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1717 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1718 if (return_status == FILE_RETRY)
1719 continue;
1720 if (return_status == FILE_SKIPALL)
1721 ctx->skip_all = TRUE;
1722 goto ret;
1724 if (n_read == 0)
1725 break;
1727 gettimeofday (&tv_current, NULL);
1729 if (n_read > 0)
1731 char *t = buf;
1732 n_read_total += n_read;
1734 /* Windows NT ftp servers report that files have no
1735 * permissions: -------, so if we happen to have actually
1736 * read something, we should fix the permissions.
1738 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1739 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1740 gettimeofday (&tv_last_input, NULL);
1742 /* dst_write */
1743 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read && !ctx->skip_all)
1745 if (n_written > 0)
1747 n_read -= n_written;
1748 t += n_written;
1749 continue;
1751 return_status = file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1752 if (return_status == FILE_SKIP)
1753 break;
1754 if (return_status == FILE_SKIPALL)
1755 ctx->skip_all = TRUE;
1756 if (return_status != FILE_RETRY)
1757 goto ret;
1761 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1763 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1764 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1766 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1768 copy_file_file_display_progress (tctx, ctx,
1769 tv_current,
1770 tv_transfer_start, file_size, n_read_total);
1771 tv_last_update = tv_current;
1773 is_first_time = FALSE;
1775 if (update_secs > FILEOP_STALLING_INTERVAL)
1777 stalled_msg = _("(stalled)");
1781 gboolean force_update;
1783 force_update =
1784 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1786 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1788 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1789 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1792 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1793 force_update);
1795 mc_refresh ();
1797 return_status = check_progress_buttons (ctx);
1799 if (return_status != FILE_CONT)
1801 mc_refresh ();
1802 goto ret;
1807 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1809 ret:
1810 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1812 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1813 if (temp_status == FILE_RETRY)
1814 continue;
1815 if (temp_status == FILE_ABORT)
1816 return_status = temp_status;
1817 if (temp_status == FILE_SKIPALL)
1818 ctx->skip_all = TRUE;
1819 break;
1822 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1824 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1825 if (temp_status == FILE_RETRY)
1826 continue;
1827 if (temp_status == FILE_SKIPALL)
1828 ctx->skip_all = TRUE;
1829 return_status = temp_status;
1830 break;
1833 if (dst_status == DEST_SHORT)
1835 /* Remove short file */
1836 int result;
1838 result = query_dialog (Q_ ("DialogTitle|Copy"),
1839 _("Incomplete file was retrieved. Keep it?"),
1840 D_ERROR, 2, _("&Delete"), _("&Keep"));
1841 if (result == 0)
1842 mc_unlink (dst_vpath);
1844 else if (dst_status == DEST_FULL)
1846 /* Copy has succeeded */
1847 if (!appending && ctx->preserve_uidgid)
1849 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1851 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1852 if (temp_status == FILE_RETRY)
1853 continue;
1854 if (temp_status == FILE_SKIPALL)
1856 ctx->skip_all = TRUE;
1857 return_status = FILE_CONT;
1859 if (temp_status == FILE_SKIP)
1860 return_status = FILE_CONT;
1861 break;
1865 if (!appending)
1867 if (ctx->preserve)
1869 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1871 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1872 if (temp_status == FILE_RETRY)
1873 continue;
1874 if (temp_status == FILE_SKIPALL)
1876 ctx->skip_all = TRUE;
1877 return_status = FILE_CONT;
1879 if (temp_status == FILE_SKIP)
1880 return_status = FILE_CONT;
1881 break;
1884 else if (!dst_exists)
1886 src_mode = umask (-1);
1887 umask (src_mode);
1888 src_mode = 0100666 & ~src_mode;
1889 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
1891 mc_utime (dst_vpath, &utb);
1895 if (return_status == FILE_CONT)
1896 return_status = progress_update_one (tctx, ctx, file_size);
1898 ret_fast:
1899 vfs_path_free (src_vpath);
1900 vfs_path_free (dst_vpath);
1901 return return_status;
1904 /* --------------------------------------------------------------------------------------------- */
1906 * I think these copy_*_* functions should have a return type.
1907 * anyway, this function *must* have two directories as arguments.
1909 /* FIXME: This function needs to check the return values of the
1910 function calls */
1912 FileProgressStatus
1913 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1914 gboolean toplevel, gboolean move_over, gboolean do_delete, struct link * parent_dirs)
1916 struct dirent *next;
1917 struct stat buf, cbuf;
1918 DIR *reading;
1919 char *dest_dir = NULL;
1920 FileProgressStatus return_status = FILE_CONT;
1921 struct utimbuf utb;
1922 struct link *lp;
1923 char *d;
1924 vfs_path_t *src_vpath, *dst_vpath, *dest_dir_vpath = NULL;
1926 d = g_strdup (_d);
1928 src_vpath = vfs_path_from_str (s);
1929 dst_vpath = vfs_path_from_str (_d);
1931 /* First get the mode of the source dir */
1933 retry_src_stat:
1934 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
1936 if (ctx->skip_all)
1937 return_status = FILE_SKIPALL;
1938 else
1940 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1941 if (return_status == FILE_RETRY)
1942 goto retry_src_stat;
1943 if (return_status == FILE_SKIPALL)
1944 ctx->skip_all = TRUE;
1946 goto ret_fast;
1949 if (is_in_linklist (dest_dirs, s, &cbuf))
1951 /* Don't copy a directory we created before (we don't want to copy
1952 infinitely if a directory is copied into itself) */
1953 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1954 return_status = FILE_CONT;
1955 goto ret_fast;
1958 /* Hmm, hardlink to directory??? - Norbert */
1959 /* FIXME: In this step we should do something
1960 in case the destination already exist */
1961 /* Check the hardlinks */
1962 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf))
1964 /* We have made a hardlink - no more processing is necessary */
1965 goto ret_fast;
1968 if (!S_ISDIR (cbuf.st_mode))
1970 if (ctx->skip_all)
1971 return_status = FILE_SKIPALL;
1972 else
1974 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1975 if (return_status == FILE_RETRY)
1976 goto retry_src_stat;
1977 if (return_status == FILE_SKIPALL)
1978 ctx->skip_all = TRUE;
1980 goto ret_fast;
1983 if (is_in_linklist (parent_dirs, s, &cbuf))
1985 /* we found a cyclic symbolic link */
1986 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
1987 return_status = FILE_SKIP;
1988 goto ret_fast;
1991 lp = g_new (struct link, 1);
1992 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
1993 lp->ino = cbuf.st_ino;
1994 lp->dev = cbuf.st_dev;
1995 lp->next = parent_dirs;
1996 parent_dirs = lp;
1998 retry_dst_stat:
1999 /* Now, check if the dest dir exists, if not, create it. */
2000 if (mc_stat (dst_vpath, &buf) != 0)
2002 /* Here the dir doesn't exist : make it ! */
2003 if (move_over)
2005 if (mc_rename (src_vpath, dst_vpath) == 0)
2007 return_status = FILE_CONT;
2008 goto ret;
2011 dest_dir = d;
2012 d = NULL;
2014 else
2017 * If the destination directory exists, we want to copy the whole
2018 * directory, but we only want this to happen once.
2020 * Escape sequences added to the * to compiler warnings.
2021 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2022 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2024 if (!S_ISDIR (buf.st_mode))
2026 if (ctx->skip_all)
2027 return_status = FILE_SKIPALL;
2028 else
2030 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2031 if (return_status == FILE_SKIPALL)
2032 ctx->skip_all = TRUE;
2033 if (return_status == FILE_RETRY)
2034 goto retry_dst_stat;
2036 goto ret;
2038 /* Dive into subdir if exists */
2039 if (toplevel && ctx->dive_into_subdirs)
2041 dest_dir = concat_dir_and_file (d, x_basename (s));
2043 else
2045 dest_dir = d;
2046 d = NULL;
2047 goto dont_mkdir;
2050 dest_dir_vpath = vfs_path_from_str (dest_dir);
2051 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU))
2053 if (ctx->skip_all)
2054 return_status = FILE_SKIPALL;
2055 else
2057 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
2058 if (return_status == FILE_SKIPALL)
2059 ctx->skip_all = TRUE;
2061 if (return_status != FILE_RETRY)
2062 goto ret;
2065 lp = g_new0 (struct link, 1);
2066 mc_stat (dest_dir_vpath, &buf);
2067 lp->vfs = vfs_path_get_by_index (dest_dir_vpath, -1)->class;
2068 lp->ino = buf.st_ino;
2069 lp->dev = buf.st_dev;
2070 lp->next = dest_dirs;
2071 dest_dirs = lp;
2073 if (ctx->preserve_uidgid)
2075 while (mc_chown (dest_dir_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2077 if (ctx->skip_all)
2078 return_status = FILE_SKIPALL;
2079 else
2081 return_status =
2082 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
2083 if (return_status == FILE_SKIPALL)
2084 ctx->skip_all = TRUE;
2086 if (return_status != FILE_RETRY)
2087 goto ret;
2091 dont_mkdir:
2092 /* open the source dir for reading */
2093 reading = mc_opendir (src_vpath);
2094 if (reading == NULL)
2095 goto ret;
2097 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2099 char *path;
2100 vfs_path_t *tmp_vpath;
2102 * Now, we don't want '.' and '..' to be created / copied at any time
2104 if (!strcmp (next->d_name, "."))
2105 continue;
2106 if (!strcmp (next->d_name, ".."))
2107 continue;
2109 /* get the filename and add it to the src directory */
2110 path = concat_dir_and_file (s, next->d_name);
2111 tmp_vpath = vfs_path_from_str (path);
2113 (*ctx->stat_func) (tmp_vpath, &buf);
2114 if (S_ISDIR (buf.st_mode))
2116 char *mdpath;
2118 mdpath = concat_dir_and_file (dest_dir, next->d_name);
2120 * From here, we just intend to recursively copy subdirs, not
2121 * the double functionality of copying different when the target
2122 * dir already exists. So, we give the recursive call the flag 0
2123 * meaning no toplevel.
2125 return_status =
2126 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2127 g_free (mdpath);
2129 else
2131 char *dest_file;
2133 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
2134 return_status = copy_file_file (tctx, ctx, path, dest_file);
2135 g_free (dest_file);
2137 if (do_delete && return_status == FILE_CONT)
2139 if (ctx->erase_at_end)
2141 static struct link *tail;
2142 size_t len = strlen (path);
2143 lp = g_malloc (sizeof (struct link) + len);
2144 strncpy (lp->name, path, len + 1);
2145 lp->st_mode = buf.st_mode;
2146 lp->next = NULL;
2147 if (erase_list != NULL)
2149 tail->next = lp;
2150 tail = lp;
2152 else
2153 erase_list = tail = lp;
2155 else
2157 if (S_ISDIR (buf.st_mode))
2159 return_status = erase_dir_iff_empty (ctx, path);
2161 else
2162 return_status = erase_file (tctx, ctx, path);
2165 g_free (path);
2166 vfs_path_free (tmp_vpath);
2168 mc_closedir (reading);
2170 if (ctx->preserve)
2172 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2173 utb.actime = cbuf.st_atime;
2174 utb.modtime = cbuf.st_mtime;
2175 mc_utime (dest_dir_vpath, &utb);
2177 else
2179 cbuf.st_mode = umask (-1);
2180 umask (cbuf.st_mode);
2181 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2182 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2185 ret:
2186 g_free (dest_dir);
2187 vfs_path_free (dest_dir_vpath);
2188 g_free (parent_dirs);
2189 ret_fast:
2190 g_free (d);
2191 vfs_path_free (src_vpath);
2192 vfs_path_free (dst_vpath);
2193 return return_status;
2196 /* }}} */
2198 /* --------------------------------------------------------------------------------------------- */
2199 /* {{{ Move routines */
2201 FileProgressStatus
2202 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2204 struct stat sbuf, dbuf, destbuf;
2205 struct link *lp;
2206 char *destdir;
2207 FileProgressStatus return_status;
2208 gboolean move_over = FALSE;
2209 gboolean dstat_ok;
2210 vfs_path_t *src_vpath, *dst_vpath, *destdir_vpath;
2212 src_vpath = vfs_path_from_str (s);
2213 dst_vpath = vfs_path_from_str (d);
2215 file_progress_show_source (ctx, s);
2216 file_progress_show_target (ctx, d);
2217 if (check_progress_buttons (ctx) == FILE_ABORT)
2218 return FILE_ABORT;
2220 mc_refresh ();
2222 mc_stat (src_vpath, &sbuf);
2224 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2226 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2227 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2229 if (!dstat_ok)
2230 destdir = g_strdup (d); /* destination doesn't exist */
2231 else if (!ctx->dive_into_subdirs)
2233 destdir = g_strdup (d);
2234 move_over = TRUE;
2236 else
2237 destdir = concat_dir_and_file (d, x_basename (s));
2239 destdir_vpath = vfs_path_from_str (destdir);
2241 /* Check if the user inputted an existing dir */
2242 retry_dst_stat:
2243 if (mc_stat (destdir_vpath, &destbuf) == 0)
2245 if (move_over)
2247 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2249 if (return_status != FILE_CONT)
2250 goto ret;
2251 goto oktoret;
2253 else if (ctx->skip_all)
2254 return_status = FILE_SKIPALL;
2255 else
2257 if (S_ISDIR (destbuf.st_mode))
2258 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2259 else
2260 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2261 if (return_status == FILE_SKIPALL)
2262 ctx->skip_all = TRUE;
2263 if (return_status == FILE_RETRY)
2264 goto retry_dst_stat;
2266 g_free (destdir);
2267 vfs_path_free (destdir_vpath);
2268 vfs_path_free (src_vpath);
2269 vfs_path_free (dst_vpath);
2270 return return_status;
2273 retry_rename:
2274 if (mc_rename (src_vpath, destdir_vpath) == 0)
2276 return_status = FILE_CONT;
2277 goto ret;
2280 if (errno != EXDEV)
2282 if (!ctx->skip_all)
2284 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2285 if (return_status == FILE_SKIPALL)
2286 ctx->skip_all = TRUE;
2287 if (return_status == FILE_RETRY)
2288 goto retry_rename;
2290 goto ret;
2292 /* Failed because of filesystem boundary -> copy dir instead */
2293 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2295 if (return_status != FILE_CONT)
2296 goto ret;
2297 oktoret:
2298 file_progress_show_source (ctx, NULL);
2299 file_progress_show (ctx, 0, 0, "", FALSE);
2301 return_status = check_progress_buttons (ctx);
2302 if (return_status != FILE_CONT)
2303 goto ret;
2305 mc_refresh ();
2306 if (ctx->erase_at_end)
2308 for (; erase_list && return_status != FILE_ABORT;)
2310 if (S_ISDIR (erase_list->st_mode))
2312 return_status = erase_dir_iff_empty (ctx, erase_list->name);
2314 else
2315 return_status = erase_file (tctx, ctx, erase_list->name);
2316 lp = erase_list;
2317 erase_list = erase_list->next;
2318 g_free (lp);
2321 erase_dir_iff_empty (ctx, s);
2323 ret:
2324 g_free (destdir);
2325 vfs_path_free (destdir_vpath);
2326 while (erase_list)
2328 lp = erase_list;
2329 erase_list = erase_list->next;
2330 g_free (lp);
2332 vfs_path_free (src_vpath);
2333 vfs_path_free (dst_vpath);
2334 return return_status;
2337 /* }}} */
2339 /* --------------------------------------------------------------------------------------------- */
2340 /* {{{ Erase routines */
2342 FileProgressStatus
2343 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
2345 FileProgressStatus error;
2347 if (strcmp (s, "..") == 0)
2348 return FILE_SKIP;
2350 if (strcmp (s, ".") == 0)
2351 return FILE_SKIP;
2353 file_progress_show_deleting (ctx, s);
2354 if (check_progress_buttons (ctx) == FILE_ABORT)
2355 return FILE_ABORT;
2356 mc_refresh ();
2358 /* The old way to detect a non empty directory was:
2359 error = my_rmdir (s);
2360 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2361 For the linux user space nfs server (nfs-server-2.2beta29-2)
2362 we would have to check also for EIO. I hope the new way is
2363 fool proof. (Norbert)
2365 error = check_dir_is_empty (s);
2366 if (error == 0)
2367 { /* not empty */
2368 error = query_recursive (ctx, s);
2369 if (error == FILE_CONT)
2370 return recursive_erase (tctx, ctx, s);
2371 else
2372 return error;
2375 while (my_rmdir (s) == -1 && !ctx->skip_all)
2377 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2378 if (error != FILE_RETRY)
2379 return error;
2382 return FILE_CONT;
2385 /* }}} */
2387 /* --------------------------------------------------------------------------------------------- */
2388 /* {{{ Panel operate routines */
2390 ComputeDirSizeUI *
2391 compute_dir_size_create_ui (void)
2393 ComputeDirSizeUI *ui;
2395 const char *b_name = N_("&Abort");
2397 #ifdef ENABLE_NLS
2398 b_name = _(b_name);
2399 #endif
2401 ui = g_new (ComputeDirSizeUI, 1);
2403 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
2404 NULL, _("Directory scanning"), DLG_CENTER);
2405 ui->dirname = label_new (3, 3, "");
2406 add_widget (ui->dlg, ui->dirname);
2408 add_widget (ui->dlg,
2409 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
2410 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2412 /* We will manage the dialog without any help,
2413 that's why we have to call init_dlg */
2414 init_dlg (ui->dlg);
2416 return ui;
2419 /* --------------------------------------------------------------------------------------------- */
2421 void
2422 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2424 if (ui != NULL)
2426 /* schedule to update passive panel */
2427 other_panel->dirty = 1;
2429 /* close and destroy dialog */
2430 dlg_run_done (ui->dlg);
2431 destroy_dlg (ui->dlg);
2432 g_free (ui);
2436 /* --------------------------------------------------------------------------------------------- */
2438 FileProgressStatus
2439 compute_dir_size_update_ui (const void *ui, const char *dirname)
2441 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2442 int c;
2443 Gpm_Event event;
2445 if (ui == NULL)
2446 return FILE_CONT;
2448 label_set_text (this->dirname, str_trunc (dirname, this->dlg->cols - 6));
2450 event.x = -1; /* Don't show the GPM cursor */
2451 c = tty_get_event (&event, FALSE, FALSE);
2452 if (c == EV_NONE)
2453 return FILE_CONT;
2455 /* Reinitialize to avoid old values after events other than
2456 selecting a button */
2457 this->dlg->ret_value = FILE_CONT;
2459 dlg_process_event (this->dlg, c, &event);
2461 switch (this->dlg->ret_value)
2463 case B_CANCEL:
2464 case FILE_ABORT:
2465 return FILE_ABORT;
2466 default:
2467 return FILE_CONT;
2471 /* --------------------------------------------------------------------------------------------- */
2473 * compute_dir_size:
2475 * Computes the number of bytes used by the files in a directory
2478 FileProgressStatus
2479 compute_dir_size (const char *dirname, const void *ui,
2480 compute_dir_size_callback cback,
2481 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2483 int res;
2484 struct stat s;
2485 DIR *dir;
2486 struct dirent *dirent;
2487 FileProgressStatus ret = FILE_CONT;
2488 vfs_path_t *vpath;
2490 vpath = vfs_path_from_str (dirname);
2492 if (!compute_symlinks)
2494 res = mc_lstat (vpath, &s);
2495 if (res != 0)
2496 goto ret;
2498 /* don't scan symlink to directory */
2499 if (S_ISLNK (s.st_mode))
2501 (*ret_marked)++;
2502 *ret_total += (uintmax_t) s.st_size;
2503 goto ret;
2507 dir = mc_opendir (vpath);
2509 if (dir == NULL)
2510 goto ret;
2512 while ((dirent = mc_readdir (dir)) != NULL)
2514 char *fullname;
2515 vfs_path_t *tmp_vpath;
2517 ret = (cback != NULL) ? cback (ui, dirname) : FILE_CONT;
2519 if (ret != FILE_CONT)
2520 break;
2522 if (strcmp (dirent->d_name, ".") == 0)
2523 continue;
2524 if (strcmp (dirent->d_name, "..") == 0)
2525 continue;
2527 fullname = concat_dir_and_file (dirname, dirent->d_name);
2528 tmp_vpath = vfs_path_from_str (fullname);
2529 res = mc_lstat (tmp_vpath, &s);
2531 if (res != 0)
2533 g_free (fullname);
2534 vfs_path_free (tmp_vpath);
2535 continue;
2538 if (S_ISDIR (s.st_mode))
2540 size_t subdir_count = 0;
2541 uintmax_t subdir_bytes = 0;
2543 ret =
2544 compute_dir_size (fullname, ui, cback, &subdir_count, &subdir_bytes,
2545 compute_symlinks);
2547 if (ret != FILE_CONT)
2549 g_free (fullname);
2550 vfs_path_free (tmp_vpath);
2551 break;
2554 *ret_marked += subdir_count;
2555 *ret_total += subdir_bytes;
2557 else
2559 (*ret_marked)++;
2560 *ret_total += (uintmax_t) s.st_size;
2563 g_free (fullname);
2564 vfs_path_free (tmp_vpath);
2567 mc_closedir (dir);
2568 ret:
2569 vfs_path_free (vpath);
2570 return ret;
2573 /* --------------------------------------------------------------------------------------------- */
2575 * panel_operate:
2577 * Performs one of the operations on the selection on the source_panel
2578 * (copy, delete, move).
2580 * Returns TRUE if did change the directory
2581 * structure, Returns FALSE if user aborted
2583 * force_single forces operation on the current entry and affects
2584 * default destination. Current filename is used as default.
2587 gboolean
2588 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2590 WPanel *panel = (WPanel *) source_panel;
2591 const gboolean single_entry = force_single || (panel->marked <= 1)
2592 || (get_current_type () == view_tree);
2594 char *source = NULL;
2595 #ifdef WITH_FULL_PATHS
2596 char *source_with_path = NULL;
2597 #else
2598 #define source_with_path source
2599 #endif /* !WITH_FULL_PATHS */
2600 char *dest = NULL;
2601 vfs_path_t *dest_vpath = NULL;
2602 char *temp = NULL;
2603 char *save_cwd = NULL, *save_dest = NULL;
2604 struct stat src_stat;
2605 gboolean ret_val = TRUE;
2606 int i;
2607 FileProgressStatus value;
2608 FileOpContext *ctx;
2609 FileOpTotalContext *tctx;
2610 vfs_path_t *tmp_vpath;
2612 gboolean do_bg = FALSE; /* do background operation? */
2614 static gboolean i18n_flag = FALSE;
2615 if (!i18n_flag)
2617 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2618 op_names[i] = Q_ (op_names[i]);
2619 i18n_flag = TRUE;
2622 free_linklist (&linklist);
2623 free_linklist (&dest_dirs);
2625 if (single_entry)
2627 vfs_path_t *source_vpath;
2629 if (force_single)
2630 source = selection (panel)->fname;
2631 else
2632 source = panel_get_file (panel);
2634 if (strcmp (source, "..") == 0)
2636 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2637 return FALSE;
2640 source_vpath = vfs_path_from_str (source);
2641 /* Update stat to get actual info */
2642 if (mc_lstat (source_vpath, &src_stat) != 0)
2644 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2645 path_trunc (source, 30), unix_error_string (errno));
2647 /* Directory was changed outside MC. Reload it forced */
2648 if (!panel->is_panelized)
2650 panel_update_flags_t flags = UP_RELOAD;
2652 /* don't update panelized panel */
2653 if (get_other_type () == view_listing && other_panel->is_panelized)
2654 flags |= UP_ONLY_CURRENT;
2656 update_panels (flags, UP_KEEPSEL);
2658 vfs_path_free (source_vpath);
2659 return FALSE;
2661 vfs_path_free (source_vpath);
2664 ctx = file_op_context_new (operation);
2666 /* Show confirmation dialog */
2667 if (operation != OP_DELETE)
2669 char *dest_dir;
2670 char *dest_dir_;
2671 char *format;
2673 /* Forced single operations default to the original name */
2674 if (force_single)
2675 dest_dir = source;
2676 else if (get_other_type () == view_listing)
2677 dest_dir = other_panel->cwd;
2678 else
2679 dest_dir = panel->cwd;
2681 * Add trailing backslash only when do non-local ops.
2682 * It saves user from occasional file renames (when destination
2683 * dir is deleted)
2685 if (!force_single && dest_dir[0] != '\0' && dest_dir[strlen (dest_dir) - 1] != PATH_SEP)
2687 /* add trailing separator */
2688 dest_dir_ = g_strconcat (dest_dir, PATH_SEP_STR, (char *) NULL);
2690 else
2692 /* just copy */
2693 dest_dir_ = g_strdup (dest_dir);
2696 if (dest_dir_ == NULL)
2698 ret_val = FALSE;
2699 goto ret_fast;
2702 /* Generate confirmation prompt */
2703 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2705 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2706 source != NULL ? (void *) source
2707 : (void *) &panel->marked, dest_dir_, &do_bg);
2709 g_free (format);
2710 g_free (dest_dir_);
2712 if (dest == NULL || dest[0] == '\0')
2714 g_free (dest);
2715 ret_val = FALSE;
2716 goto ret_fast;
2718 dest_vpath = vfs_path_from_str (dest);
2720 else if (confirm_delete)
2722 char *format;
2723 char fmd_buf[BUF_MEDIUM];
2725 /* Generate confirmation prompt */
2726 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2728 if (source == NULL)
2729 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2730 else
2732 const int fmd_xlen = 64;
2733 i = fmd_xlen - str_term_width1 (format) - 4;
2734 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2737 g_free (format);
2739 if (safe_delete)
2740 query_set_sel (1);
2742 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2744 if (i != 0)
2746 ret_val = FALSE;
2747 goto ret_fast;
2751 tctx = file_op_total_context_new ();
2752 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2755 filegui_dialog_type_t dialog_type;
2757 if (operation == OP_DELETE)
2758 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2759 else
2761 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2762 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2764 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2765 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2768 /* Background also need ctx->ui, but not full */
2769 if (do_bg)
2770 file_op_context_create_ui_without_init (ctx, TRUE, dialog_type);
2771 else
2772 file_op_context_create_ui (ctx, TRUE, dialog_type);
2775 #ifdef ENABLE_BACKGROUND
2776 /* Did the user select to do a background operation? */
2777 if (do_bg)
2779 int v;
2781 v = do_background (ctx, g_strconcat (op_names[operation], ": ", panel->cwd, (char *) NULL));
2782 if (v == -1)
2783 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2785 /* If we are the parent */
2786 if (v == 1)
2788 tmp_vpath = vfs_path_from_str (panel->cwd);
2789 mc_setctl (tmp_vpath, VFS_SETCTL_FORGET, NULL);
2790 vfs_path_free (tmp_vpath);
2792 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
2793 vfs_path_free (dest_vpath);
2794 g_free (dest);
2795 /* file_op_context_destroy (ctx); */
2796 return FALSE;
2799 #endif /* ENABLE_BACKGROUND */
2801 /* Initialize things */
2802 /* We do not want to trash cache every time file is
2803 created/touched. However, this will make our cache contain
2804 invalid data. */
2805 if ((dest != NULL) && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2806 save_dest = g_strdup (dest);
2808 tmp_vpath = vfs_path_from_str (panel->cwd);
2809 if ((panel->cwd[0] != '\0') && (mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2810 save_cwd = g_strdup (panel->cwd);
2811 vfs_path_free (tmp_vpath);
2813 /* Now, let's do the job */
2815 /* This code is only called by the tree and panel code */
2816 if (single_entry)
2818 /* We now have ETA in all cases */
2820 /* One file: FIXME mc_chdir will take user out of any vfs */
2821 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2823 vfs_path_t *vpath;
2824 int chdir_retcode;
2826 vpath = vfs_path_from_str (PATH_SEP_STR);
2827 chdir_retcode = mc_chdir (vpath);
2828 vfs_path_free (vpath);
2829 if (chdir_retcode < 0)
2831 ret_val = FALSE;
2832 goto clean_up;
2836 /* The source and src_stat variables have been initialized before */
2837 #ifdef WITH_FULL_PATHS
2838 if (g_path_is_absolute (source))
2839 source_with_path = g_strdup (source);
2840 else
2841 source_with_path = mc_build_filename (panel->cwd, source, (char *) NULL);
2842 #endif /* WITH_FULL_PATHS */
2844 if (panel_operate_init_totals (operation, panel, source_with_path, ctx) == FILE_CONT)
2846 if (operation == OP_DELETE)
2848 if (S_ISDIR (src_stat.st_mode))
2849 value = erase_dir (tctx, ctx, source_with_path);
2850 else
2851 value = erase_file (tctx, ctx, source_with_path);
2853 else
2855 temp = transform_source (ctx, source_with_path);
2856 if (temp == NULL)
2857 value = transform_error;
2858 else
2860 char *repl_dest, *temp2;
2862 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2863 temp2 = concat_dir_and_file (repl_dest, temp);
2864 g_free (temp);
2865 g_free (repl_dest);
2866 g_free (dest);
2867 vfs_path_free (dest_vpath);
2868 dest = temp2;
2869 dest_vpath = vfs_path_from_str (dest);
2871 switch (operation)
2873 case OP_COPY:
2874 /* we use file_mask_op_follow_links only with OP_COPY */
2876 vfs_path_t *vpath = vfs_path_from_str (source_with_path);
2877 ctx->stat_func (vpath, &src_stat);
2878 vfs_path_free (vpath);
2881 if (S_ISDIR (src_stat.st_mode))
2882 value = copy_dir_dir (tctx, ctx, source_with_path, dest,
2883 TRUE, FALSE, FALSE, NULL);
2884 else
2885 value = copy_file_file (tctx, ctx, source_with_path, dest);
2886 break;
2888 case OP_MOVE:
2889 if (S_ISDIR (src_stat.st_mode))
2890 value = move_dir_dir (tctx, ctx, source_with_path, dest);
2891 else
2892 value = move_file_file (tctx, ctx, source_with_path, dest);
2893 break;
2895 default:
2896 /* Unknown file operation */
2897 abort ();
2900 } /* Copy or move operation */
2902 if ((value == FILE_CONT) && !force_single)
2903 unmark_files (panel);
2906 else
2908 /* Many files */
2910 /* Check destination for copy or move operation */
2911 while (operation != OP_DELETE)
2913 int dst_result;
2914 struct stat dst_stat;
2916 dst_result = mc_stat (dest_vpath, &dst_stat);
2918 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2919 break;
2921 if (ctx->skip_all
2922 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2923 goto clean_up;
2926 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2928 /* Loop for every file, perform the actual copy operation */
2929 for (i = 0; i < panel->count; i++)
2931 if (!panel->dir.list[i].f.marked)
2932 continue; /* Skip the unmarked ones */
2934 source = panel->dir.list[i].fname;
2935 src_stat = panel->dir.list[i].st;
2937 #ifdef WITH_FULL_PATHS
2938 g_free (source_with_path);
2939 if (g_path_is_absolute (source))
2940 source_with_path = g_strdup (source);
2941 else
2942 source_with_path = mc_build_filename (panel->cwd, source, (char *) NULL);
2943 #endif /* WITH_FULL_PATHS */
2945 if (operation == OP_DELETE)
2947 if (S_ISDIR (src_stat.st_mode))
2948 value = erase_dir (tctx, ctx, source_with_path);
2949 else
2950 value = erase_file (tctx, ctx, source_with_path);
2952 else
2954 temp = transform_source (ctx, source_with_path);
2956 if (temp == NULL)
2957 value = transform_error;
2958 else
2960 char *temp2, *temp3, *repl_dest;
2962 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2963 temp2 = concat_dir_and_file (repl_dest, temp);
2964 g_free (temp);
2965 g_free (repl_dest);
2966 temp3 = source_with_path;
2967 source_with_path = strutils_shell_unescape (source_with_path);
2968 g_free (temp3);
2969 temp3 = temp2;
2970 temp2 = strutils_shell_unescape (temp2);
2971 g_free (temp3);
2973 switch (operation)
2975 case OP_COPY:
2976 /* we use file_mask_op_follow_links only with OP_COPY */
2978 vfs_path_t *vpath;
2980 vpath = vfs_path_from_str (source_with_path);
2981 ctx->stat_func (vpath, &src_stat);
2982 vfs_path_free (vpath);
2984 if (S_ISDIR (src_stat.st_mode))
2985 value = copy_dir_dir (tctx, ctx, source_with_path, temp2,
2986 TRUE, FALSE, FALSE, NULL);
2987 else
2988 value = copy_file_file (tctx, ctx, source_with_path, temp2);
2989 free_linklist (&dest_dirs);
2990 break;
2992 case OP_MOVE:
2993 if (S_ISDIR (src_stat.st_mode))
2994 value = move_dir_dir (tctx, ctx, source_with_path, temp2);
2995 else
2996 value = move_file_file (tctx, ctx, source_with_path, temp2);
2997 break;
2999 default:
3000 /* Unknown file operation */
3001 abort ();
3004 g_free (temp2);
3006 } /* Copy or move operation */
3008 if (value == FILE_ABORT)
3009 break;
3011 if (value == FILE_CONT)
3012 do_file_mark (panel, i, 0);
3014 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3016 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3017 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3020 if (operation != OP_DELETE)
3021 file_progress_show (ctx, 0, 0, "", FALSE);
3023 if (check_progress_buttons (ctx) == FILE_ABORT)
3024 break;
3026 mc_refresh ();
3027 } /* Loop for every file */
3029 } /* Many entries */
3031 clean_up:
3032 /* Clean up */
3033 if (save_cwd != NULL)
3035 tmp_vpath = vfs_path_from_str (save_cwd);
3036 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3037 vfs_path_free (tmp_vpath);
3038 g_free (save_cwd);
3041 if (save_dest != NULL)
3043 tmp_vpath = vfs_path_from_str (save_dest);
3044 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3045 vfs_path_free (tmp_vpath);
3046 g_free (save_dest);
3049 free_linklist (&linklist);
3050 free_linklist (&dest_dirs);
3051 #ifdef WITH_FULL_PATHS
3052 g_free (source_with_path);
3053 #endif /* WITH_FULL_PATHS */
3054 g_free (dest);
3055 vfs_path_free (dest_vpath);
3056 g_free (ctx->dest_mask);
3057 ctx->dest_mask = NULL;
3059 #ifdef ENABLE_BACKGROUND
3060 /* Let our parent know we are saying bye bye */
3061 if (mc_global.we_are_background)
3063 int cur_pid = getpid ();
3064 /* Send pid to parent with child context, it is fork and
3065 don't modify real parent ctx */
3066 ctx->pid = cur_pid;
3067 parent_call ((void *) end_bg_process, ctx, 0);
3069 vfs_shut ();
3070 _exit (0);
3072 #endif /* ENABLE_BACKGROUND */
3074 file_op_total_context_destroy (tctx);
3075 ret_fast:
3076 file_op_context_destroy (ctx);
3078 return ret_val;
3081 /* }}} */
3083 /* --------------------------------------------------------------------------------------------- */
3084 /* {{{ Query/status report routines */
3085 /** Report error with one file */
3086 FileProgressStatus
3087 file_error (const char *format, const char *file)
3089 char buf[BUF_MEDIUM];
3091 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3093 return do_file_error (buf);
3096 /* --------------------------------------------------------------------------------------------- */
3099 Cause emacs to enter folding mode for this file:
3100 Local variables:
3101 end: