Code refactoring
[midnight-commander.git] / src / filemanager / file.c
blob0a3a03fcab353eb6c1aa55a0ab01f5ac83a8e108
1 /*
2 File management.
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Janne Kukonlehto, 1994, 1995
10 Fred Leeflang, 1994, 1995
11 Miguel de Icaza, 1994, 1995, 1996
12 Jakub Jelinek, 1995, 1996
13 Norbert Warmuth, 1997
14 Pavel Machek, 1998
16 The copy code was based in GNU's cp, and was written by:
17 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
19 The move code was based in GNU's mv, and was written by:
20 Mike Parker and David MacKenzie.
22 Janne Kukonlehto added much error recovery to them for being used
23 in an interactive program.
25 This file is part of the Midnight Commander.
27 The Midnight Commander is free software: you can redistribute it
28 and/or modify it under the terms of the GNU General Public License as
29 published by the Free Software Foundation, either version 3 of the License,
30 or (at your option) any later version.
32 The Midnight Commander is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program. If not, see <http://www.gnu.org/licenses/>.
42 * Please note that all dialogs used here must be safe for background
43 * operations.
46 /** \file file.c
47 * \brief Source: file management
50 /* {{{ Include files */
52 #include <config.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
62 #include <fcntl.h>
64 #include "lib/global.h"
65 #include "lib/tty/tty.h"
66 #include "lib/tty/key.h"
67 #include "lib/search.h"
68 #include "lib/strescape.h"
69 #include "lib/strutil.h"
70 #include "lib/util.h"
71 #include "lib/vfs/vfs.h"
72 #include "lib/widget.h"
74 #include "src/setup.h"
75 #ifdef ENABLE_BACKGROUND
76 #include "src/background.h" /* do_background() */
77 #endif
79 #include "layout.h" /* rotate_dash() */
81 /* Needed for current_panel, other_panel and WTree */
82 #include "dir.h"
83 #include "filegui.h"
84 #include "filenot.h"
85 #include "tree.h"
86 #include "midnight.h" /* current_panel */
88 #include "file.h"
90 /* }}} */
92 /*** global variables ****************************************************************************/
94 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
95 const char *op_names[3] = {
96 N_("DialogTitle|Copy"),
97 N_("DialogTitle|Move"),
98 N_("DialogTitle|Delete")
101 /*** file scope macro definitions ****************************************************************/
103 /* Hack: the vfs code should not rely on this */
104 #define WITH_FULL_PATHS 1
106 #define FILEOP_UPDATE_INTERVAL 2
107 #define FILEOP_STALLING_INTERVAL 4
109 /*** file scope type declarations ****************************************************************/
111 /* This is a hard link cache */
112 struct link
114 struct link *next;
115 struct vfs_class *vfs;
116 dev_t dev;
117 ino_t ino;
118 short linkcount;
119 mode_t st_mode;
120 char name[1];
123 /* Status of the destination file */
124 typedef enum
126 DEST_NONE = 0, /* Not created */
127 DEST_SHORT = 1, /* Created, not fully copied */
128 DEST_FULL = 2 /* Created, fully copied */
129 } dest_status_t;
132 * This array introduced to avoid translation problems. The former (op_names)
133 * is assumed to be nouns, suitable in dialog box titles; this one should
134 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
135 * (I don't use spaces around the words, because someday they could be
136 * dropped, when widgets get smarter)
139 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
140 static const char *op_names1[] = {
141 N_("FileOperation|Copy"),
142 N_("FileOperation|Move"),
143 N_("FileOperation|Delete")
147 * These are formats for building a prompt. Parts encoded as follows:
148 * %o - operation from op_names1
149 * %f - file/files or files/directories, as appropriate
150 * %m - "with source mask" or question mark for delete
151 * %s - source name (truncated)
152 * %d - number of marked files
153 * %e - "to:" or question mark for delete
155 * xgettext:no-c-format */
156 static const char *one_format = N_("%o %f \"%s\"%m");
157 /* xgettext:no-c-format */
158 static const char *many_format = N_("%o %d %f%m");
160 static const char *prompt_parts[] = {
161 N_("file"),
162 N_("files"),
163 N_("directory"),
164 N_("directories"),
165 N_("files/directories"),
166 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
167 N_(" with source mask:"),
168 N_("to:")
171 static const char *question_format = N_("%s?");
173 /*** file scope variables ************************************************************************/
175 /* the hard link cache */
176 static struct link *linklist = NULL;
178 /* the files-to-be-erased list */
179 static struct link *erase_list;
182 * In copy_dir_dir we use two additional single linked lists: The first -
183 * variable name `parent_dirs' - holds information about already copied
184 * directories and is used to detect cyclic symbolic links.
185 * The second (`dest_dirs' below) holds information about just created
186 * target directories and is used to detect when an directory is copied
187 * into itself (we don't want to copy infinitly).
188 * Both lists don't use the linkcount and name structure members of struct
189 * link.
191 static struct link *dest_dirs = NULL;
193 static FileProgressStatus transform_error = FILE_CONT;
195 /*** file scope functions ************************************************************************/
196 /* --------------------------------------------------------------------------------------------- */
198 static char *
199 transform_source (FileOpContext * ctx, const char *source)
201 char *s, *q;
202 char *fnsource;
204 s = g_strdup (source);
206 /* We remove \n from the filename since regex routines would use \n as an anchor */
207 /* this is just to be allowed to maniupulate file names with \n on it */
208 for (q = s; *q != '\0'; q++)
209 if (*q == '\n')
210 *q = ' ';
212 fnsource = (char *) x_basename (s);
214 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
215 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
216 else
218 q = NULL;
219 transform_error = FILE_SKIP;
222 g_free (s);
223 return q;
226 /* --------------------------------------------------------------------------------------------- */
228 static void
229 free_linklist (struct link **lc_linklist)
231 struct link *lp, *lp2;
233 for (lp = *lc_linklist; lp != NULL; lp = lp2)
235 lp2 = lp->next;
236 g_free (lp);
238 *lc_linklist = NULL;
241 /* --------------------------------------------------------------------------------------------- */
243 static int
244 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
246 vfs_path_t *vpath;
247 vfs_path_element_t *vpath_element;
248 ino_t ino = sb->st_ino;
249 dev_t dev = sb->st_dev;
251 vpath = vfs_path_from_str (path);
252 vpath_element = vfs_path_get_by_index (vpath, -1);
254 while (lp != NULL)
256 if (lp->vfs == vpath_element->class)
257 if (lp->ino == ino && lp->dev == dev)
258 return 1;
259 lp = lp->next;
261 vfs_path_free (vpath);
262 return 0;
265 /* --------------------------------------------------------------------------------------------- */
267 * Check and made hardlink
269 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
270 * and a hardlink was succesfully made
273 static gboolean
274 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
276 struct link *lp;
277 vfs_path_t *src_vpath, *dst_vpath;
279 struct vfs_class *my_vfs;
280 ino_t ino = pstat->st_ino;
281 dev_t dev = pstat->st_dev;
282 struct stat link_stat;
283 const char *p;
285 src_vpath = vfs_path_from_str (src_name);
287 if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
289 vfs_path_free (src_vpath);
290 return FALSE;
292 my_vfs = vfs_path_get_by_index (src_vpath, -1)->class;
293 dst_vpath = vfs_path_from_str (dst_name);
295 for (lp = linklist; lp != NULL; lp = lp->next)
296 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev)
298 struct vfs_class *lp_name_class;
299 int stat_result;
300 vfs_path_t *tmp_vpath;
302 tmp_vpath = vfs_path_from_str (lp->name);
303 lp_name_class = vfs_path_get_by_index (tmp_vpath, -1)->class;
304 stat_result = mc_stat (tmp_vpath, &link_stat);
305 vfs_path_free (tmp_vpath);
307 if (!stat_result && link_stat.st_ino == ino
308 && link_stat.st_dev == dev && lp_name_class == my_vfs)
310 struct vfs_class *p_class, *dst_name_class;
312 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
313 was copied to */
315 dst_name_class = vfs_path_get_by_index (dst_vpath, -1)->class;
317 tmp_vpath = vfs_path_from_str (p);
318 p_class = vfs_path_get_by_index (tmp_vpath, -1)->class;
320 if (dst_name_class == p_class)
322 if (!mc_stat (tmp_vpath, &link_stat))
324 if (!mc_link (tmp_vpath, dst_vpath))
326 vfs_path_free (tmp_vpath);
327 vfs_path_free (src_vpath);
328 vfs_path_free (dst_vpath);
329 return TRUE;
333 vfs_path_free (tmp_vpath);
336 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
337 vfs_path_free (src_vpath);
338 vfs_path_free (dst_vpath);
339 return FALSE;
341 lp = (struct link *) g_try_malloc (sizeof (struct link) + strlen (src_name)
342 + strlen (dst_name) + 1);
343 if (lp)
345 char *lpdstname;
346 lp->vfs = my_vfs;
347 lp->ino = ino;
348 lp->dev = dev;
349 strcpy (lp->name, src_name);
350 lpdstname = lp->name + strlen (lp->name) + 1;
351 strcpy (lpdstname, dst_name);
352 lp->next = linklist;
353 linklist = lp;
355 vfs_path_free (src_vpath);
356 vfs_path_free (dst_vpath);
357 return FALSE;
360 /* --------------------------------------------------------------------------------------------- */
362 * Duplicate the contents of the symbolic link src_path in dst_path.
363 * Try to make a stable symlink if the option "stable symlink" was
364 * set in the file mask dialog.
365 * If dst_path is an existing symlink it will be deleted silently
366 * (upper levels take already care of existing files at dst_path).
369 static FileProgressStatus
370 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
372 char link_target[MC_MAXPATHLEN];
373 int len;
374 FileProgressStatus return_status;
375 struct stat sb;
376 vfs_path_t *src_vpath;
377 vfs_path_t *dst_vpath;
378 gboolean dst_is_symlink;
379 vfs_path_t *link_target_vpath = NULL;
381 src_vpath = vfs_path_from_str (src_path);
382 dst_vpath = vfs_path_from_str (dst_path);
383 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
385 retry_src_readlink:
386 len = mc_readlink (src_vpath, link_target, MC_MAXPATHLEN - 1);
387 if (len < 0)
389 if (ctx->skip_all)
390 return_status = FILE_SKIPALL;
391 else
393 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
394 if (return_status == FILE_SKIPALL)
395 ctx->skip_all = TRUE;
396 if (return_status == FILE_RETRY)
397 goto retry_src_readlink;
399 goto ret;
401 link_target[len] = 0;
403 if (ctx->stable_symlinks)
406 if (!vfs_file_is_local (src_vpath) || !vfs_file_is_local (dst_vpath))
408 message (D_ERROR, MSG_ERROR,
409 _("Cannot make stable symlinks across"
410 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
411 ctx->stable_symlinks = FALSE;
415 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
417 char *p, *s;
418 vfs_path_t *q;
420 const char *r = strrchr (src_path, PATH_SEP);
422 if (r)
424 p = g_strndup (src_path, r - src_path + 1);
425 if (g_path_is_absolute (dst_path))
426 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
427 else
428 q = vfs_path_build_filename (p, dst_path, (char *) NULL);
430 if (vfs_path_tokens_count (q) > 1)
432 vfs_path_t *tmp_vpath1, *tmp_vpath2;
434 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
435 s = g_strconcat (p, link_target, (char *) NULL);
436 g_free (p);
437 g_strlcpy (link_target, s, sizeof (link_target));
438 g_free (s);
439 tmp_vpath2 = vfs_path_from_str (link_target);
440 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
441 vfs_path_free (tmp_vpath1);
442 vfs_path_free (tmp_vpath2);
443 if (s)
445 g_strlcpy (link_target, s, sizeof (link_target));
446 g_free (s);
449 else
450 g_free (p);
451 vfs_path_free (q);
454 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
456 retry_dst_symlink:
457 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
459 /* Success */
460 return_status = FILE_CONT;
461 goto ret;
464 * if dst_exists, it is obvious that this had failed.
465 * We can delete the old symlink and try again...
467 if (dst_is_symlink)
469 if (mc_unlink (dst_vpath) == 0)
470 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
472 /* Success */
473 return_status = FILE_CONT;
474 goto ret;
477 if (ctx->skip_all)
478 return_status = FILE_SKIPALL;
479 else
481 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
482 if (return_status == FILE_SKIPALL)
483 ctx->skip_all = TRUE;
484 if (return_status == FILE_RETRY)
485 goto retry_dst_symlink;
488 ret:
489 vfs_path_free (src_vpath);
490 vfs_path_free (dst_vpath);
491 vfs_path_free (link_target_vpath);
492 return return_status;
495 /* --------------------------------------------------------------------------------------------- */
497 static FileProgressStatus
498 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add)
500 struct timeval tv_current;
501 static struct timeval tv_start = { };
503 tctx->progress_count++;
504 tctx->progress_bytes += (uintmax_t) add;
506 if (tv_start.tv_sec == 0)
508 gettimeofday (&tv_start, (struct timezone *) NULL);
510 gettimeofday (&tv_current, (struct timezone *) NULL);
511 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
513 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
515 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
516 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
518 tv_start.tv_sec = tv_current.tv_sec;
521 return check_progress_buttons (ctx);
524 /* --------------------------------------------------------------------------------------------- */
526 static FileProgressStatus
527 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
529 char *msg;
530 int result = 0;
531 const char *head_msg;
533 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
535 msg = g_strdup_printf (fmt, a, b);
536 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
537 g_free (msg);
538 do_refresh ();
540 return (result == 1) ? FILE_ABORT : FILE_SKIP;
543 /* --------------------------------------------------------------------------------------------- */
545 static FileProgressStatus
546 warn_same_file (const char *fmt, const char *a, const char *b)
548 #ifdef ENABLE_BACKGROUND
549 /* *INDENT-OFF* */
550 union
552 void *p;
553 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
554 const char *a, const char *b);
555 } pntr;
556 /* *INDENT-ON* */
558 pntr.f = real_warn_same_file;
560 if (mc_global.we_are_background)
561 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
562 #endif
563 return real_warn_same_file (Foreground, fmt, a, b);
566 /* --------------------------------------------------------------------------------------------- */
567 /* {{{ Query/status report routines */
569 static FileProgressStatus
570 real_do_file_error (enum OperationMode mode, const char *error)
572 int result;
573 const char *msg;
575 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
576 result =
577 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
579 switch (result)
581 case 0:
582 do_refresh ();
583 return FILE_SKIP;
585 case 1:
586 do_refresh ();
587 return FILE_SKIPALL;
589 case 2:
590 do_refresh ();
591 return FILE_RETRY;
593 case 3:
594 default:
595 return FILE_ABORT;
599 /* --------------------------------------------------------------------------------------------- */
601 static FileProgressStatus
602 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
604 gchar *text;
606 if (ctx->recursive_result < RECURSIVE_ALWAYS)
608 const char *msg = mode == Foreground
609 ? _("\nDirectory not empty.\nDelete it recursively?")
610 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
611 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
613 if (safe_delete)
614 query_set_sel (1);
616 ctx->recursive_result =
617 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
618 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
620 if (ctx->recursive_result != RECURSIVE_ABORT)
621 do_refresh ();
622 g_free (text);
625 switch (ctx->recursive_result)
627 case RECURSIVE_YES:
628 case RECURSIVE_ALWAYS:
629 return FILE_CONT;
631 case RECURSIVE_NO:
632 case RECURSIVE_NEVER:
633 return FILE_SKIP;
635 case RECURSIVE_ABORT:
636 default:
637 return FILE_ABORT;
641 /* --------------------------------------------------------------------------------------------- */
643 #ifdef ENABLE_BACKGROUND
644 static FileProgressStatus
645 do_file_error (const char *str)
647 union
649 void *p;
650 FileProgressStatus (*f) (enum OperationMode, const char *);
651 } pntr;
652 pntr.f = real_do_file_error;
654 if (mc_global.we_are_background)
655 return parent_call (pntr.p, NULL, 1, strlen (str), str);
656 else
657 return real_do_file_error (Foreground, str);
660 /* --------------------------------------------------------------------------------------------- */
662 static FileProgressStatus
663 query_recursive (FileOpContext * ctx, const char *s)
665 union
667 void *p;
668 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
669 } pntr;
670 pntr.f = real_query_recursive;
672 if (mc_global.we_are_background)
673 return parent_call (pntr.p, ctx, 1, strlen (s), s);
674 else
675 return real_query_recursive (ctx, Foreground, s);
678 /* --------------------------------------------------------------------------------------------- */
680 static FileProgressStatus
681 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
682 struct stat *_d_stat)
684 union
686 void *p;
687 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
688 struct stat *, struct stat *);
689 } pntr;
690 pntr.f = file_progress_real_query_replace;
692 if (mc_global.we_are_background)
693 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
694 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
695 else
696 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
699 #else
700 /* --------------------------------------------------------------------------------------------- */
702 static FileProgressStatus
703 do_file_error (const char *str)
705 return real_do_file_error (Foreground, str);
708 /* --------------------------------------------------------------------------------------------- */
710 static FileProgressStatus
711 query_recursive (FileOpContext * ctx, const char *s)
713 return real_query_recursive (ctx, Foreground, s);
716 /* --------------------------------------------------------------------------------------------- */
718 static FileProgressStatus
719 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
720 struct stat *_d_stat)
722 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
725 #endif /* !ENABLE_BACKGROUND */
727 /* --------------------------------------------------------------------------------------------- */
728 /** Report error with two files */
730 static FileProgressStatus
731 files_error (const char *format, const char *file1, const char *file2)
733 char buf[BUF_MEDIUM];
734 char *nfile1 = g_strdup (path_trunc (file1, 15));
735 char *nfile2 = g_strdup (path_trunc (file2, 15));
737 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
739 g_free (nfile1);
740 g_free (nfile2);
742 return do_file_error (buf);
745 /* }}} */
747 /* --------------------------------------------------------------------------------------------- */
749 static void
750 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
751 struct timeval tv_current, struct timeval tv_transfer_start,
752 off_t file_size, off_t n_read_total)
754 long dt;
756 /* 1. Update rotating dash after some time */
757 rotate_dash ();
759 /* 3. Compute ETA */
760 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
762 if (n_read_total == 0)
763 ctx->eta_secs = 0.0;
764 else
766 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
767 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
770 /* 4. Compute BPS rate */
771 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
772 if (ctx->bps_time < 1)
773 ctx->bps_time = 1;
774 ctx->bps = n_read_total / ctx->bps_time;
776 /* 5. Compute total ETA and BPS */
777 if (ctx->progress_bytes != 0)
779 uintmax_t remain_bytes;
781 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
782 #if 1
784 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
786 if (total_secs < 1)
787 total_secs = 1;
789 tctx->bps = tctx->copied_bytes / total_secs;
790 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
792 #else
793 /* broken on lot of little files */
794 tctx->bps_count++;
795 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
796 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
797 #endif
801 /* --------------------------------------------------------------------------------------------- */
803 /* {{{ Move routines */
804 static FileProgressStatus
805 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
807 struct stat src_stats, dst_stats;
808 FileProgressStatus return_status = FILE_CONT;
809 gboolean copy_done = FALSE;
810 gboolean old_ask_overwrite;
811 vfs_path_t *src_vpath, *dst_vpath;
813 file_progress_show_source (ctx, s);
814 file_progress_show_target (ctx, d);
815 if (check_progress_buttons (ctx) == FILE_ABORT)
816 return FILE_ABORT;
818 mc_refresh ();
819 src_vpath = vfs_path_from_str (s);
820 dst_vpath = vfs_path_from_str (d);
822 while (mc_lstat (src_vpath, &src_stats) != 0)
824 /* Source doesn't exist */
825 if (ctx->skip_all)
826 return_status = FILE_SKIPALL;
827 else
829 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
830 if (return_status == FILE_SKIPALL)
831 ctx->skip_all = TRUE;
833 if (return_status != FILE_RETRY)
835 vfs_path_free (src_vpath);
836 vfs_path_free (dst_vpath);
837 return return_status;
841 if (mc_lstat (dst_vpath, &dst_stats) == 0)
843 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
844 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
846 if (S_ISDIR (dst_stats.st_mode))
848 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
849 do_refresh ();
850 vfs_path_free (src_vpath);
851 vfs_path_free (dst_vpath);
852 return FILE_SKIP;
855 if (confirm_overwrite)
857 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
858 if (return_status != FILE_CONT)
860 vfs_path_free (src_vpath);
861 vfs_path_free (dst_vpath);
862 return return_status;
865 /* Ok to overwrite */
868 if (!ctx->do_append)
870 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
872 return_status = make_symlink (ctx, s, d);
873 if (return_status == FILE_CONT)
874 goto retry_src_remove;
875 else
877 vfs_path_free (src_vpath);
878 vfs_path_free (dst_vpath);
879 return return_status;
883 if (mc_rename (src_vpath, dst_vpath) == 0)
885 vfs_path_free (src_vpath);
886 vfs_path_free (dst_vpath);
887 return progress_update_one (tctx, ctx, src_stats.st_size);
890 #if 0
891 /* Comparison to EXDEV seems not to work in nfs if you're moving from
892 one nfs to the same, but on the server it is on two different
893 filesystems. Then nfs returns EIO instead of EXDEV.
894 Hope it will not hurt if we always in case of error try to copy/delete. */
895 else
896 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
898 if (errno != EXDEV)
900 if (ctx->skip_all)
901 return_status = FILE_SKIPALL;
902 else
904 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
905 if (return_status == FILE_SKIPALL)
906 ctx->skip_all = TRUE;
907 if (return_status == FILE_RETRY)
908 goto retry_rename;
910 vfs_path_free (src_vpath);
911 vfs_path_free (dst_vpath);
913 return return_status;
915 #endif
917 /* Failed because filesystem boundary -> copy the file instead */
918 old_ask_overwrite = tctx->ask_overwrite;
919 tctx->ask_overwrite = FALSE;
920 return_status = copy_file_file (tctx, ctx, s, d);
921 tctx->ask_overwrite = old_ask_overwrite;
922 if (return_status != FILE_CONT)
924 vfs_path_free (src_vpath);
925 vfs_path_free (dst_vpath);
926 return return_status;
929 copy_done = TRUE;
931 file_progress_show_source (ctx, NULL);
932 file_progress_show (ctx, 0, 0, "", FALSE);
934 return_status = check_progress_buttons (ctx);
935 if (return_status != FILE_CONT)
937 vfs_path_free (src_vpath);
938 vfs_path_free (dst_vpath);
939 return return_status;
942 mc_refresh ();
944 retry_src_remove:
945 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
947 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
948 if (return_status == FILE_RETRY)
949 goto retry_src_remove;
950 if (return_status == FILE_SKIPALL)
951 ctx->skip_all = TRUE;
953 vfs_path_free (src_vpath);
954 vfs_path_free (dst_vpath);
955 return return_status;
958 if (!copy_done)
959 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
961 vfs_path_free (src_vpath);
962 vfs_path_free (dst_vpath);
964 return return_status;
967 /* }}} */
969 /* --------------------------------------------------------------------------------------------- */
970 /* {{{ Erase routines */
971 /** Don't update progress status if progress_count==NULL */
973 static FileProgressStatus
974 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
976 int return_status;
977 struct stat buf;
978 vfs_path_t *vpath = vfs_path_from_str (s);
980 file_progress_show_deleting (ctx, s);
981 if (check_progress_buttons (ctx) == FILE_ABORT)
982 return FILE_ABORT;
983 mc_refresh ();
985 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
987 /* ignore, most likely the mc_unlink fails, too */
988 buf.st_size = 0;
991 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
993 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
994 if (return_status == FILE_ABORT)
996 vfs_path_free (vpath);
997 return return_status;
999 if (return_status == FILE_RETRY)
1000 continue;
1001 if (return_status == FILE_SKIPALL)
1002 ctx->skip_all = TRUE;
1003 break;
1006 vfs_path_free (vpath);
1007 if (tctx->progress_count == 0)
1008 return FILE_CONT;
1009 return progress_update_one (tctx, ctx, buf.st_size);
1012 /* --------------------------------------------------------------------------------------------- */
1015 Recursive remove of files
1016 abort->cancel stack
1017 skip ->warn every level, gets default
1018 skipall->remove as much as possible
1020 static FileProgressStatus
1021 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
1023 struct dirent *next;
1024 struct stat buf;
1025 DIR *reading;
1026 char *path;
1027 FileProgressStatus return_status = FILE_CONT;
1028 vfs_path_t *vpath;
1030 if (strcmp (s, "..") == 0)
1031 return FILE_RETRY;
1033 vpath = vfs_path_from_str (s);
1034 reading = mc_opendir (vpath);
1036 if (reading == NULL)
1038 return_status = FILE_RETRY;
1039 goto ret;
1042 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1044 vfs_path_t *tmp_vpath;
1046 if (!strcmp (next->d_name, "."))
1047 continue;
1048 if (!strcmp (next->d_name, ".."))
1049 continue;
1050 path = concat_dir_and_file (s, next->d_name);
1051 tmp_vpath = vfs_path_from_str (path);
1052 if (mc_lstat (tmp_vpath, &buf) != 0)
1054 g_free (path);
1055 mc_closedir (reading);
1056 vfs_path_free (tmp_vpath);
1057 return_status = FILE_RETRY;
1058 goto ret;
1060 if (S_ISDIR (buf.st_mode))
1061 return_status = recursive_erase (tctx, ctx, path);
1062 else
1063 return_status = erase_file (tctx, ctx, path);
1064 vfs_path_free (tmp_vpath);
1065 g_free (path);
1067 mc_closedir (reading);
1068 if (return_status == FILE_ABORT)
1069 goto ret;
1071 file_progress_show_deleting (ctx, s);
1072 if (check_progress_buttons (ctx) == FILE_ABORT)
1074 return_status = FILE_ABORT;
1075 goto ret;
1077 mc_refresh ();
1079 while (my_rmdir (s) != 0 && !ctx->skip_all)
1081 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1082 if (return_status == FILE_RETRY)
1083 continue;
1084 if (return_status == FILE_ABORT)
1085 goto ret;
1086 if (return_status == FILE_SKIPALL)
1087 ctx->skip_all = TRUE;
1088 break;
1091 ret:
1092 vfs_path_free (vpath);
1093 return return_status;
1096 /* --------------------------------------------------------------------------------------------- */
1097 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1098 in the directory path points to, 0 else. */
1100 static int
1101 check_dir_is_empty (const char *path)
1103 DIR *dir;
1104 struct dirent *d;
1105 int i;
1106 vfs_path_t *vpath = vfs_path_from_str (path);
1108 dir = mc_opendir (vpath);
1109 if (!dir)
1111 vfs_path_free (vpath);
1112 return -1;
1115 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1117 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1118 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1119 continue; /* "." or ".." */
1120 i = 0;
1121 break;
1124 mc_closedir (dir);
1125 vfs_path_free (vpath);
1126 return i;
1129 /* --------------------------------------------------------------------------------------------- */
1131 static FileProgressStatus
1132 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1134 FileProgressStatus error;
1136 if (strcmp (s, "..") == 0)
1137 return FILE_SKIP;
1139 if (strcmp (s, ".") == 0)
1140 return FILE_SKIP;
1142 file_progress_show_deleting (ctx, s);
1143 if (check_progress_buttons (ctx) == FILE_ABORT)
1144 return FILE_ABORT;
1145 mc_refresh ();
1147 if (1 != check_dir_is_empty (s)) /* not empty or error */
1148 return FILE_CONT;
1150 while (my_rmdir (s) != 0 && !ctx->skip_all)
1152 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1153 if (error == FILE_SKIPALL)
1154 ctx->skip_all = TRUE;
1155 if (error != FILE_RETRY)
1156 return error;
1159 return FILE_CONT;
1162 /* }}} */
1164 /* --------------------------------------------------------------------------------------------- */
1165 /* {{{ Panel operate routines */
1168 * Return currently selected entry name or the name of the first marked
1169 * entry if there is one.
1172 static char *
1173 panel_get_file (WPanel * panel)
1175 if (get_current_type () == view_tree)
1177 WTree *tree;
1179 tree = (WTree *) get_panel_widget (get_current_index ());
1180 return vfs_path_to_str (tree_selected_name (tree));
1183 if (panel->marked != 0)
1185 int i;
1187 for (i = 0; i < panel->count; i++)
1188 if (panel->dir.list[i].f.marked)
1189 return g_strdup (panel->dir.list[i].fname);
1191 return g_strdup (panel->dir.list[panel->selected].fname);
1194 /* --------------------------------------------------------------------------------------------- */
1196 * panel_compute_totals:
1198 * compute the number of files and the number of bytes
1199 * used up by the whole selection, recursing directories
1200 * as required. In addition, it checks to see if it will
1201 * overwrite any files by doing the copy.
1204 static FileProgressStatus
1205 panel_compute_totals (const WPanel * panel, const void *ui,
1206 compute_dir_size_callback cback,
1207 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1209 int i;
1211 *ret_marked = 0;
1212 *ret_total = 0;
1214 for (i = 0; i < panel->count; i++)
1216 struct stat *s;
1218 if (!panel->dir.list[i].f.marked)
1219 continue;
1221 s = &panel->dir.list[i].st;
1223 if (S_ISDIR (s->st_mode))
1225 vfs_path_t *p;
1226 size_t subdir_count = 0;
1227 uintmax_t subdir_bytes = 0;
1228 FileProgressStatus status;
1230 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, NULL);
1231 status =
1232 compute_dir_size (p, ui, cback, &subdir_count, &subdir_bytes, compute_symlinks);
1233 vfs_path_free (p);
1235 if (status != FILE_CONT)
1236 return FILE_ABORT;
1238 *ret_marked += subdir_count;
1239 *ret_total += subdir_bytes;
1241 else
1243 (*ret_marked)++;
1244 *ret_total += (uintmax_t) s->st_size;
1248 return FILE_CONT;
1251 /* --------------------------------------------------------------------------------------------- */
1253 /** Initialize variables for progress bars */
1254 static FileProgressStatus
1255 panel_operate_init_totals (FileOperation operation,
1256 const WPanel * panel, const char *source, FileOpContext * ctx)
1258 FileProgressStatus status;
1260 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1262 ComputeDirSizeUI *ui;
1264 ui = compute_dir_size_create_ui ();
1266 if (source == NULL)
1267 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1268 &ctx->progress_count, &ctx->progress_bytes,
1269 ctx->follow_links);
1270 else
1272 vfs_path_t *p;
1274 p = vfs_path_from_str (source);
1275 status = compute_dir_size (p, ui, compute_dir_size_update_ui,
1276 &ctx->progress_count, &ctx->progress_bytes,
1277 ctx->follow_links);
1278 vfs_path_free (p);
1281 compute_dir_size_destroy_ui (ui);
1283 ctx->progress_totals_computed = (status == FILE_CONT);
1285 else
1287 status = FILE_CONT;
1288 ctx->progress_count = panel->marked;
1289 ctx->progress_bytes = panel->total;
1290 ctx->progress_totals_computed = FALSE;
1293 return status;
1296 /* --------------------------------------------------------------------------------------------- */
1298 * Generate user prompt for panel operation.
1299 * single_source is the name if the source entry or NULL for multiple
1300 * entries.
1301 * src_stat is only used when single_source is not NULL.
1304 static char *
1305 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1306 gboolean single_source, const struct stat *src_stat)
1308 const char *sp, *cp;
1309 char format_string[BUF_MEDIUM];
1310 char *dp = format_string;
1311 gboolean build_question = FALSE;
1313 static gboolean i18n_flag = FALSE;
1314 if (!i18n_flag)
1316 size_t i;
1318 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1319 op_names1[i] = Q_ (op_names1[i]);
1321 #ifdef ENABLE_NLS
1322 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1323 prompt_parts[i] = _(prompt_parts[i]);
1325 one_format = _(one_format);
1326 many_format = _(many_format);
1327 question_format = _(question_format);
1328 #endif /* ENABLE_NLS */
1329 i18n_flag = TRUE;
1332 sp = single_source ? one_format : many_format;
1334 while (*sp != '\0')
1336 switch (*sp)
1338 case '%':
1339 cp = NULL;
1340 switch (sp[1])
1342 case 'o':
1343 cp = op_names1[operation];
1344 break;
1345 case 'm':
1346 if (operation == OP_DELETE)
1348 cp = "";
1349 build_question = TRUE;
1351 else
1352 cp = prompt_parts[5];
1353 break;
1354 case 'e':
1355 if (operation == OP_DELETE)
1357 cp = "";
1358 build_question = TRUE;
1360 else
1361 cp = prompt_parts[6];
1362 break;
1363 case 'f':
1364 if (single_source)
1365 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1366 else
1367 cp = (panel->marked == panel->dirs_marked)
1368 ? prompt_parts[3]
1369 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1370 break;
1371 default:
1372 *dp++ = *sp++;
1375 if (cp != NULL)
1377 sp += 2;
1378 while (*cp != '\0')
1379 *dp++ = *cp++;
1381 break;
1382 default:
1383 *dp++ = *sp++;
1386 *dp = '\0';
1388 if (build_question)
1390 char tmp[BUF_MEDIUM];
1392 memmove (tmp, format_string, sizeof (tmp));
1393 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1396 return g_strdup (format_string);
1399 /* --------------------------------------------------------------------------------------------- */
1401 #ifdef ENABLE_BACKGROUND
1402 static int
1403 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1405 int pid = ctx->pid;
1407 (void) mode;
1408 ctx->pid = 0;
1410 unregister_task_with_pid (pid);
1411 /* file_op_context_destroy(ctx); */
1412 return 1;
1414 #endif
1415 /* }}} */
1417 /* --------------------------------------------------------------------------------------------- */
1418 /*** public functions ****************************************************************************/
1419 /* --------------------------------------------------------------------------------------------- */
1421 FileProgressStatus
1422 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1423 const char *src_path, const char *dst_path)
1425 uid_t src_uid = (uid_t) - 1;
1426 gid_t src_gid = (gid_t) - 1;
1428 int src_desc, dest_desc = -1;
1429 int n_read, n_written;
1430 mode_t src_mode = 0; /* The mode of the source file */
1431 struct stat sb, sb2;
1432 struct utimbuf utb;
1433 gboolean dst_exists = FALSE, appending = FALSE;
1434 off_t file_size = -1;
1435 FileProgressStatus return_status, temp_status;
1436 struct timeval tv_transfer_start;
1437 dest_status_t dst_status = DEST_NONE;
1438 int open_flags;
1439 gboolean is_first_time = TRUE;
1440 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1442 /* FIXME: We should not be using global variables! */
1443 ctx->do_reget = 0;
1444 return_status = FILE_RETRY;
1446 file_progress_show_source (ctx, src_path);
1447 file_progress_show_target (ctx, dst_path);
1448 if (check_progress_buttons (ctx) == FILE_ABORT)
1449 return FILE_ABORT;
1451 mc_refresh ();
1453 dst_vpath = vfs_path_from_str (dst_path);
1454 while (mc_stat (dst_vpath, &sb2) == 0)
1456 if (S_ISDIR (sb2.st_mode))
1458 if (ctx->skip_all)
1459 return_status = FILE_SKIPALL;
1460 else
1462 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1463 if (return_status == FILE_SKIPALL)
1464 ctx->skip_all = TRUE;
1465 if (return_status == FILE_RETRY)
1466 continue;
1468 goto ret_fast;
1470 dst_exists = TRUE;
1471 break;
1474 src_vpath = vfs_path_from_str (src_path);
1475 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1477 if (ctx->skip_all)
1478 return_status = FILE_SKIPALL;
1479 else
1481 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1482 if (return_status == FILE_SKIPALL)
1483 ctx->skip_all = TRUE;
1485 if (return_status != FILE_RETRY)
1486 goto ret_fast;
1489 if (dst_exists)
1491 /* Destination already exists */
1492 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1494 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1495 src_path, dst_path);
1496 goto ret_fast;
1498 /* Should we replace destination? */
1499 if (tctx->ask_overwrite)
1501 ctx->do_reget = 0;
1502 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1503 if (return_status != FILE_CONT)
1504 goto ret_fast;
1508 if (!ctx->do_append)
1510 /* Check the hardlinks */
1511 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb))
1513 /* We have made a hardlink - no more processing is necessary */
1514 return_status = FILE_CONT;
1515 goto ret_fast;
1518 if (S_ISLNK (sb.st_mode))
1520 return_status = make_symlink (ctx, src_path, dst_path);
1521 goto ret_fast;
1524 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1525 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1527 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1528 && !ctx->skip_all)
1530 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1531 if (return_status == FILE_RETRY)
1532 continue;
1533 if (return_status == FILE_SKIPALL)
1534 ctx->skip_all = TRUE;
1535 goto ret_fast;
1537 /* Success */
1539 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1540 && !ctx->skip_all)
1542 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1543 if (temp_status == FILE_SKIP)
1544 break;
1545 if (temp_status == FILE_SKIPALL)
1546 ctx->skip_all = TRUE;
1547 if (temp_status != FILE_RETRY)
1549 return_status = temp_status;
1550 goto ret_fast;
1554 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1555 && !ctx->skip_all)
1557 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1558 if (temp_status == FILE_SKIP)
1559 break;
1560 if (temp_status == FILE_SKIPALL)
1561 ctx->skip_all = TRUE;
1562 if (temp_status != FILE_RETRY)
1564 return_status = temp_status;
1565 goto ret_fast;
1569 return_status = FILE_CONT;
1570 goto ret_fast;
1574 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1576 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1578 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1579 if (return_status == FILE_RETRY)
1580 continue;
1581 if (return_status == FILE_SKIPALL)
1582 ctx->skip_all = TRUE;
1583 if (return_status == FILE_SKIP)
1584 break;
1585 ctx->do_append = 0;
1586 goto ret_fast;
1589 if (ctx->do_reget != 0)
1591 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1593 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1594 ctx->do_reget = 0;
1595 ctx->do_append = FALSE;
1599 while (mc_fstat (src_desc, &sb) != 0)
1601 if (ctx->skip_all)
1602 return_status = FILE_SKIPALL;
1603 else
1605 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1606 if (return_status == FILE_RETRY)
1607 continue;
1608 if (return_status == FILE_SKIPALL)
1609 ctx->skip_all = TRUE;
1610 ctx->do_append = FALSE;
1612 goto ret;
1614 src_mode = sb.st_mode;
1615 src_uid = sb.st_uid;
1616 src_gid = sb.st_gid;
1617 utb.actime = sb.st_atime;
1618 utb.modtime = sb.st_mtime;
1619 file_size = sb.st_size;
1621 open_flags = O_WRONLY;
1622 if (dst_exists)
1624 if (ctx->do_append != 0)
1625 open_flags |= O_APPEND;
1626 else
1627 open_flags |= O_CREAT | O_TRUNC;
1629 else
1631 open_flags |= O_CREAT | O_EXCL;
1634 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
1636 if (errno != EEXIST)
1638 if (ctx->skip_all)
1639 return_status = FILE_SKIPALL;
1640 else
1642 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1643 if (return_status == FILE_RETRY)
1644 continue;
1645 if (return_status == FILE_SKIPALL)
1646 ctx->skip_all = TRUE;
1647 ctx->do_append = FALSE;
1650 goto ret;
1652 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1654 appending = ctx->do_append;
1655 ctx->do_append = FALSE;
1657 /* Find out the optimal buffer size. */
1658 while (mc_fstat (dest_desc, &sb) != 0)
1660 if (ctx->skip_all)
1661 return_status = FILE_SKIPALL;
1662 else
1664 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1665 if (return_status == FILE_RETRY)
1666 continue;
1667 if (return_status == FILE_SKIPALL)
1668 ctx->skip_all = TRUE;
1670 goto ret;
1673 while (TRUE)
1675 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1676 if (errno == 0)
1677 break;
1679 if (ctx->skip_all)
1680 return_status = FILE_SKIPALL;
1681 else
1683 return_status =
1684 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1685 if (return_status == FILE_RETRY)
1686 continue;
1687 if (return_status == FILE_SKIPALL)
1688 ctx->skip_all = TRUE;
1690 mc_close (dest_desc);
1691 dest_desc = -1;
1692 mc_unlink (dst_vpath);
1693 dst_status = DEST_NONE;
1694 goto ret;
1697 ctx->eta_secs = 0.0;
1698 ctx->bps = 0;
1700 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1701 file_progress_show (ctx, 0, file_size, "", TRUE);
1702 else
1703 file_progress_show (ctx, 1, 1, "", TRUE);
1704 return_status = check_progress_buttons (ctx);
1705 mc_refresh ();
1707 if (return_status != FILE_CONT)
1708 goto ret;
1711 off_t n_read_total = 0;
1712 struct timeval tv_current, tv_last_update, tv_last_input;
1713 int secs, update_secs;
1714 const char *stalled_msg = "";
1716 tv_last_update = tv_transfer_start;
1718 while (TRUE)
1720 char buf[BUF_8K];
1722 /* src_read */
1723 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1724 n_read = -1;
1725 else
1726 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1728 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1729 if (return_status == FILE_RETRY)
1730 continue;
1731 if (return_status == FILE_SKIPALL)
1732 ctx->skip_all = TRUE;
1733 goto ret;
1735 if (n_read == 0)
1736 break;
1738 gettimeofday (&tv_current, NULL);
1740 if (n_read > 0)
1742 char *t = buf;
1743 n_read_total += n_read;
1745 /* Windows NT ftp servers report that files have no
1746 * permissions: -------, so if we happen to have actually
1747 * read something, we should fix the permissions.
1749 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1750 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1751 gettimeofday (&tv_last_input, NULL);
1753 /* dst_write */
1754 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read && !ctx->skip_all)
1756 if (n_written > 0)
1758 n_read -= n_written;
1759 t += n_written;
1760 continue;
1762 return_status = file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1763 if (return_status == FILE_SKIP)
1764 break;
1765 if (return_status == FILE_SKIPALL)
1766 ctx->skip_all = TRUE;
1767 if (return_status != FILE_RETRY)
1768 goto ret;
1772 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1774 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1775 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1777 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1779 copy_file_file_display_progress (tctx, ctx,
1780 tv_current,
1781 tv_transfer_start, file_size, n_read_total);
1782 tv_last_update = tv_current;
1784 is_first_time = FALSE;
1786 if (update_secs > FILEOP_STALLING_INTERVAL)
1788 stalled_msg = _("(stalled)");
1792 gboolean force_update;
1794 force_update =
1795 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1797 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1799 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1800 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1803 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1804 force_update);
1806 mc_refresh ();
1808 return_status = check_progress_buttons (ctx);
1810 if (return_status != FILE_CONT)
1812 mc_refresh ();
1813 goto ret;
1818 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1820 ret:
1821 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1823 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1824 if (temp_status == FILE_RETRY)
1825 continue;
1826 if (temp_status == FILE_ABORT)
1827 return_status = temp_status;
1828 if (temp_status == FILE_SKIPALL)
1829 ctx->skip_all = TRUE;
1830 break;
1833 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1835 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1836 if (temp_status == FILE_RETRY)
1837 continue;
1838 if (temp_status == FILE_SKIPALL)
1839 ctx->skip_all = TRUE;
1840 return_status = temp_status;
1841 break;
1844 if (dst_status == DEST_SHORT)
1846 /* Remove short file */
1847 int result;
1849 result = query_dialog (Q_ ("DialogTitle|Copy"),
1850 _("Incomplete file was retrieved. Keep it?"),
1851 D_ERROR, 2, _("&Delete"), _("&Keep"));
1852 if (result == 0)
1853 mc_unlink (dst_vpath);
1855 else if (dst_status == DEST_FULL)
1857 /* Copy has succeeded */
1858 if (!appending && ctx->preserve_uidgid)
1860 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1862 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1863 if (temp_status == FILE_RETRY)
1864 continue;
1865 if (temp_status == FILE_SKIPALL)
1867 ctx->skip_all = TRUE;
1868 return_status = FILE_CONT;
1870 if (temp_status == FILE_SKIP)
1871 return_status = FILE_CONT;
1872 break;
1876 if (!appending)
1878 if (ctx->preserve)
1880 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1882 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1883 if (temp_status == FILE_RETRY)
1884 continue;
1885 if (temp_status == FILE_SKIPALL)
1887 ctx->skip_all = TRUE;
1888 return_status = FILE_CONT;
1890 if (temp_status == FILE_SKIP)
1891 return_status = FILE_CONT;
1892 break;
1895 else if (!dst_exists)
1897 src_mode = umask (-1);
1898 umask (src_mode);
1899 src_mode = 0100666 & ~src_mode;
1900 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
1902 mc_utime (dst_vpath, &utb);
1906 if (return_status == FILE_CONT)
1907 return_status = progress_update_one (tctx, ctx, file_size);
1909 ret_fast:
1910 vfs_path_free (src_vpath);
1911 vfs_path_free (dst_vpath);
1912 return return_status;
1915 /* --------------------------------------------------------------------------------------------- */
1917 * I think these copy_*_* functions should have a return type.
1918 * anyway, this function *must* have two directories as arguments.
1920 /* FIXME: This function needs to check the return values of the
1921 function calls */
1923 FileProgressStatus
1924 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1925 gboolean toplevel, gboolean move_over, gboolean do_delete, struct link * parent_dirs)
1927 struct dirent *next;
1928 struct stat buf, cbuf;
1929 DIR *reading;
1930 char *dest_dir = NULL;
1931 FileProgressStatus return_status = FILE_CONT;
1932 struct utimbuf utb;
1933 struct link *lp;
1934 char *d;
1935 vfs_path_t *src_vpath, *dst_vpath, *dest_dir_vpath = NULL;
1937 d = g_strdup (_d);
1939 src_vpath = vfs_path_from_str (s);
1940 dst_vpath = vfs_path_from_str (_d);
1942 /* First get the mode of the source dir */
1944 retry_src_stat:
1945 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
1947 if (ctx->skip_all)
1948 return_status = FILE_SKIPALL;
1949 else
1951 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1952 if (return_status == FILE_RETRY)
1953 goto retry_src_stat;
1954 if (return_status == FILE_SKIPALL)
1955 ctx->skip_all = TRUE;
1957 goto ret_fast;
1960 if (is_in_linklist (dest_dirs, s, &cbuf))
1962 /* Don't copy a directory we created before (we don't want to copy
1963 infinitely if a directory is copied into itself) */
1964 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1965 return_status = FILE_CONT;
1966 goto ret_fast;
1969 /* Hmm, hardlink to directory??? - Norbert */
1970 /* FIXME: In this step we should do something
1971 in case the destination already exist */
1972 /* Check the hardlinks */
1973 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf))
1975 /* We have made a hardlink - no more processing is necessary */
1976 goto ret_fast;
1979 if (!S_ISDIR (cbuf.st_mode))
1981 if (ctx->skip_all)
1982 return_status = FILE_SKIPALL;
1983 else
1985 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1986 if (return_status == FILE_RETRY)
1987 goto retry_src_stat;
1988 if (return_status == FILE_SKIPALL)
1989 ctx->skip_all = TRUE;
1991 goto ret_fast;
1994 if (is_in_linklist (parent_dirs, s, &cbuf))
1996 /* we found a cyclic symbolic link */
1997 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
1998 return_status = FILE_SKIP;
1999 goto ret_fast;
2002 lp = g_new (struct link, 1);
2003 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
2004 lp->ino = cbuf.st_ino;
2005 lp->dev = cbuf.st_dev;
2006 lp->next = parent_dirs;
2007 parent_dirs = lp;
2009 retry_dst_stat:
2010 /* Now, check if the dest dir exists, if not, create it. */
2011 if (mc_stat (dst_vpath, &buf) != 0)
2013 /* Here the dir doesn't exist : make it ! */
2014 if (move_over)
2016 if (mc_rename (src_vpath, dst_vpath) == 0)
2018 return_status = FILE_CONT;
2019 goto ret;
2022 dest_dir = d;
2023 d = NULL;
2025 else
2028 * If the destination directory exists, we want to copy the whole
2029 * directory, but we only want this to happen once.
2031 * Escape sequences added to the * to compiler warnings.
2032 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2033 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2035 if (!S_ISDIR (buf.st_mode))
2037 if (ctx->skip_all)
2038 return_status = FILE_SKIPALL;
2039 else
2041 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2042 if (return_status == FILE_SKIPALL)
2043 ctx->skip_all = TRUE;
2044 if (return_status == FILE_RETRY)
2045 goto retry_dst_stat;
2047 goto ret;
2049 /* Dive into subdir if exists */
2050 if (toplevel && ctx->dive_into_subdirs)
2052 dest_dir = concat_dir_and_file (d, x_basename (s));
2054 else
2056 dest_dir = d;
2057 d = NULL;
2058 goto dont_mkdir;
2061 dest_dir_vpath = vfs_path_from_str (dest_dir);
2062 while (my_mkdir (dest_dir_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2064 if (ctx->skip_all)
2065 return_status = FILE_SKIPALL;
2066 else
2068 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
2069 if (return_status == FILE_SKIPALL)
2070 ctx->skip_all = TRUE;
2072 if (return_status != FILE_RETRY)
2073 goto ret;
2076 lp = g_new0 (struct link, 1);
2077 mc_stat (dest_dir_vpath, &buf);
2078 lp->vfs = vfs_path_get_by_index (dest_dir_vpath, -1)->class;
2079 lp->ino = buf.st_ino;
2080 lp->dev = buf.st_dev;
2081 lp->next = dest_dirs;
2082 dest_dirs = lp;
2084 if (ctx->preserve_uidgid)
2086 while (mc_chown (dest_dir_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2088 if (ctx->skip_all)
2089 return_status = FILE_SKIPALL;
2090 else
2092 return_status =
2093 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
2094 if (return_status == FILE_SKIPALL)
2095 ctx->skip_all = TRUE;
2097 if (return_status != FILE_RETRY)
2098 goto ret;
2102 dont_mkdir:
2103 /* open the source dir for reading */
2104 reading = mc_opendir (src_vpath);
2105 if (reading == NULL)
2106 goto ret;
2108 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2110 char *path;
2111 vfs_path_t *tmp_vpath;
2113 * Now, we don't want '.' and '..' to be created / copied at any time
2115 if (!strcmp (next->d_name, "."))
2116 continue;
2117 if (!strcmp (next->d_name, ".."))
2118 continue;
2120 /* get the filename and add it to the src directory */
2121 path = concat_dir_and_file (s, next->d_name);
2122 tmp_vpath = vfs_path_from_str (path);
2124 (*ctx->stat_func) (tmp_vpath, &buf);
2125 if (S_ISDIR (buf.st_mode))
2127 char *mdpath;
2129 mdpath = concat_dir_and_file (dest_dir, next->d_name);
2131 * From here, we just intend to recursively copy subdirs, not
2132 * the double functionality of copying different when the target
2133 * dir already exists. So, we give the recursive call the flag 0
2134 * meaning no toplevel.
2136 return_status =
2137 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2138 g_free (mdpath);
2140 else
2142 char *dest_file;
2144 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
2145 return_status = copy_file_file (tctx, ctx, path, dest_file);
2146 g_free (dest_file);
2148 if (do_delete && return_status == FILE_CONT)
2150 if (ctx->erase_at_end)
2152 static struct link *tail;
2153 size_t len = strlen (path);
2154 lp = g_malloc (sizeof (struct link) + len);
2155 strncpy (lp->name, path, len + 1);
2156 lp->st_mode = buf.st_mode;
2157 lp->next = NULL;
2158 if (erase_list != NULL)
2160 tail->next = lp;
2161 tail = lp;
2163 else
2164 erase_list = tail = lp;
2166 else
2168 if (S_ISDIR (buf.st_mode))
2170 return_status = erase_dir_iff_empty (ctx, path);
2172 else
2173 return_status = erase_file (tctx, ctx, path);
2176 g_free (path);
2177 vfs_path_free (tmp_vpath);
2179 mc_closedir (reading);
2181 if (ctx->preserve)
2183 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2184 utb.actime = cbuf.st_atime;
2185 utb.modtime = cbuf.st_mtime;
2186 mc_utime (dest_dir_vpath, &utb);
2188 else
2190 cbuf.st_mode = umask (-1);
2191 umask (cbuf.st_mode);
2192 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2193 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2196 ret:
2197 g_free (dest_dir);
2198 vfs_path_free (dest_dir_vpath);
2199 g_free (parent_dirs);
2200 ret_fast:
2201 g_free (d);
2202 vfs_path_free (src_vpath);
2203 vfs_path_free (dst_vpath);
2204 return return_status;
2207 /* }}} */
2209 /* --------------------------------------------------------------------------------------------- */
2210 /* {{{ Move routines */
2212 FileProgressStatus
2213 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2215 struct stat sbuf, dbuf, destbuf;
2216 struct link *lp;
2217 char *destdir;
2218 FileProgressStatus return_status;
2219 gboolean move_over = FALSE;
2220 gboolean dstat_ok;
2221 vfs_path_t *src_vpath, *dst_vpath, *destdir_vpath;
2223 src_vpath = vfs_path_from_str (s);
2224 dst_vpath = vfs_path_from_str (d);
2226 file_progress_show_source (ctx, s);
2227 file_progress_show_target (ctx, d);
2228 if (check_progress_buttons (ctx) == FILE_ABORT)
2229 return FILE_ABORT;
2231 mc_refresh ();
2233 mc_stat (src_vpath, &sbuf);
2235 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2237 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2238 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2240 if (!dstat_ok)
2241 destdir = g_strdup (d); /* destination doesn't exist */
2242 else if (!ctx->dive_into_subdirs)
2244 destdir = g_strdup (d);
2245 move_over = TRUE;
2247 else
2248 destdir = concat_dir_and_file (d, x_basename (s));
2250 destdir_vpath = vfs_path_from_str (destdir);
2252 /* Check if the user inputted an existing dir */
2253 retry_dst_stat:
2254 if (mc_stat (destdir_vpath, &destbuf) == 0)
2256 if (move_over)
2258 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2260 if (return_status != FILE_CONT)
2261 goto ret;
2262 goto oktoret;
2264 else if (ctx->skip_all)
2265 return_status = FILE_SKIPALL;
2266 else
2268 if (S_ISDIR (destbuf.st_mode))
2269 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2270 else
2271 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2272 if (return_status == FILE_SKIPALL)
2273 ctx->skip_all = TRUE;
2274 if (return_status == FILE_RETRY)
2275 goto retry_dst_stat;
2277 g_free (destdir);
2278 vfs_path_free (destdir_vpath);
2279 vfs_path_free (src_vpath);
2280 vfs_path_free (dst_vpath);
2281 return return_status;
2284 retry_rename:
2285 if (mc_rename (src_vpath, destdir_vpath) == 0)
2287 return_status = FILE_CONT;
2288 goto ret;
2291 if (errno != EXDEV)
2293 if (!ctx->skip_all)
2295 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2296 if (return_status == FILE_SKIPALL)
2297 ctx->skip_all = TRUE;
2298 if (return_status == FILE_RETRY)
2299 goto retry_rename;
2301 goto ret;
2303 /* Failed because of filesystem boundary -> copy dir instead */
2304 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2306 if (return_status != FILE_CONT)
2307 goto ret;
2308 oktoret:
2309 file_progress_show_source (ctx, NULL);
2310 file_progress_show (ctx, 0, 0, "", FALSE);
2312 return_status = check_progress_buttons (ctx);
2313 if (return_status != FILE_CONT)
2314 goto ret;
2316 mc_refresh ();
2317 if (ctx->erase_at_end)
2319 for (; erase_list && return_status != FILE_ABORT;)
2321 if (S_ISDIR (erase_list->st_mode))
2323 return_status = erase_dir_iff_empty (ctx, erase_list->name);
2325 else
2326 return_status = erase_file (tctx, ctx, erase_list->name);
2327 lp = erase_list;
2328 erase_list = erase_list->next;
2329 g_free (lp);
2332 erase_dir_iff_empty (ctx, s);
2334 ret:
2335 g_free (destdir);
2336 vfs_path_free (destdir_vpath);
2337 while (erase_list)
2339 lp = erase_list;
2340 erase_list = erase_list->next;
2341 g_free (lp);
2343 vfs_path_free (src_vpath);
2344 vfs_path_free (dst_vpath);
2345 return return_status;
2348 /* }}} */
2350 /* --------------------------------------------------------------------------------------------- */
2351 /* {{{ Erase routines */
2353 FileProgressStatus
2354 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
2356 FileProgressStatus error;
2358 if (strcmp (s, "..") == 0)
2359 return FILE_SKIP;
2361 if (strcmp (s, ".") == 0)
2362 return FILE_SKIP;
2364 file_progress_show_deleting (ctx, s);
2365 if (check_progress_buttons (ctx) == FILE_ABORT)
2366 return FILE_ABORT;
2367 mc_refresh ();
2369 /* The old way to detect a non empty directory was:
2370 error = my_rmdir (s);
2371 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2372 For the linux user space nfs server (nfs-server-2.2beta29-2)
2373 we would have to check also for EIO. I hope the new way is
2374 fool proof. (Norbert)
2376 error = check_dir_is_empty (s);
2377 if (error == 0)
2378 { /* not empty */
2379 error = query_recursive (ctx, s);
2380 if (error == FILE_CONT)
2381 return recursive_erase (tctx, ctx, s);
2382 else
2383 return error;
2386 while (my_rmdir (s) == -1 && !ctx->skip_all)
2388 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2389 if (error != FILE_RETRY)
2390 return error;
2393 return FILE_CONT;
2396 /* }}} */
2398 /* --------------------------------------------------------------------------------------------- */
2399 /* {{{ Panel operate routines */
2401 ComputeDirSizeUI *
2402 compute_dir_size_create_ui (void)
2404 ComputeDirSizeUI *ui;
2406 const char *b_name = N_("&Abort");
2408 #ifdef ENABLE_NLS
2409 b_name = _(b_name);
2410 #endif
2412 ui = g_new (ComputeDirSizeUI, 1);
2414 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
2415 NULL, _("Directory scanning"), DLG_CENTER);
2416 ui->dirname = label_new (3, 3, "");
2417 add_widget (ui->dlg, ui->dirname);
2419 add_widget (ui->dlg,
2420 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
2421 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2423 /* We will manage the dialog without any help,
2424 that's why we have to call init_dlg */
2425 init_dlg (ui->dlg);
2427 return ui;
2430 /* --------------------------------------------------------------------------------------------- */
2432 void
2433 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2435 if (ui != NULL)
2437 /* schedule to update passive panel */
2438 other_panel->dirty = 1;
2440 /* close and destroy dialog */
2441 dlg_run_done (ui->dlg);
2442 destroy_dlg (ui->dlg);
2443 g_free (ui);
2447 /* --------------------------------------------------------------------------------------------- */
2449 FileProgressStatus
2450 compute_dir_size_update_ui (const void *ui, const vfs_path_t * dirname_vpath)
2452 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2453 int c;
2454 Gpm_Event event;
2455 char *dirname;
2457 if (ui == NULL)
2458 return FILE_CONT;
2460 dirname = vfs_path_to_str (dirname_vpath);
2461 label_set_text (this->dirname, str_trunc (dirname, this->dlg->cols - 6));
2462 g_free (dirname);
2464 event.x = -1; /* Don't show the GPM cursor */
2465 c = tty_get_event (&event, FALSE, FALSE);
2466 if (c == EV_NONE)
2467 return FILE_CONT;
2469 /* Reinitialize to avoid old values after events other than
2470 selecting a button */
2471 this->dlg->ret_value = FILE_CONT;
2473 dlg_process_event (this->dlg, c, &event);
2475 switch (this->dlg->ret_value)
2477 case B_CANCEL:
2478 case FILE_ABORT:
2479 return FILE_ABORT;
2480 default:
2481 return FILE_CONT;
2485 /* --------------------------------------------------------------------------------------------- */
2487 * compute_dir_size:
2489 * Computes the number of bytes used by the files in a directory
2492 FileProgressStatus
2493 compute_dir_size (const vfs_path_t * dirname_vpath, const void *ui,
2494 compute_dir_size_callback cback,
2495 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2497 int res;
2498 struct stat s;
2499 DIR *dir;
2500 struct dirent *dirent;
2501 FileProgressStatus ret = FILE_CONT;
2503 if (!compute_symlinks)
2505 res = mc_lstat (dirname_vpath, &s);
2506 if (res != 0)
2507 return ret;
2509 /* don't scan symlink to directory */
2510 if (S_ISLNK (s.st_mode))
2512 (*ret_marked)++;
2513 *ret_total += (uintmax_t) s.st_size;
2514 return ret;
2518 dir = mc_opendir (dirname_vpath);
2520 if (dir == NULL)
2521 return ret;
2523 while ((dirent = mc_readdir (dir)) != NULL)
2525 vfs_path_t *tmp_vpath;
2527 ret = (cback != NULL) ? cback (ui, dirname_vpath) : FILE_CONT;
2529 if (ret != FILE_CONT)
2530 break;
2532 if (strcmp (dirent->d_name, ".") == 0)
2533 continue;
2534 if (strcmp (dirent->d_name, "..") == 0)
2535 continue;
2537 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, NULL);
2538 res = mc_lstat (tmp_vpath, &s);
2539 if (res == 0)
2541 if (S_ISDIR (s.st_mode))
2543 size_t subdir_count = 0;
2544 uintmax_t subdir_bytes = 0;
2546 ret =
2547 compute_dir_size (tmp_vpath, ui, cback, &subdir_count, &subdir_bytes,
2548 compute_symlinks);
2550 if (ret != FILE_CONT)
2552 vfs_path_free (tmp_vpath);
2553 break;
2556 *ret_marked += subdir_count;
2557 *ret_total += subdir_bytes;
2559 else
2561 (*ret_marked)++;
2562 *ret_total += (uintmax_t) s.st_size;
2565 vfs_path_free (tmp_vpath);
2568 mc_closedir (dir);
2569 return ret;
2572 /* --------------------------------------------------------------------------------------------- */
2574 * panel_operate:
2576 * Performs one of the operations on the selection on the source_panel
2577 * (copy, delete, move).
2579 * Returns TRUE if did change the directory
2580 * structure, Returns FALSE if user aborted
2582 * force_single forces operation on the current entry and affects
2583 * default destination. Current filename is used as default.
2586 gboolean
2587 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2589 WPanel *panel = (WPanel *) source_panel;
2590 const gboolean single_entry = force_single || (panel->marked <= 1)
2591 || (get_current_type () == view_tree);
2593 char *source = NULL;
2594 #ifdef WITH_FULL_PATHS
2595 vfs_path_t *source_with_vpath = NULL;
2596 char *source_with_path_str = 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 = g_strdup (selection (panel)->fname);
2631 else
2632 source = panel_get_file (panel);
2634 if (strcmp (source, "..") == 0)
2636 g_free (source);
2637 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2638 return FALSE;
2641 source_vpath = vfs_path_from_str (source);
2642 /* Update stat to get actual info */
2643 if (mc_lstat (source_vpath, &src_stat) != 0)
2645 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2646 path_trunc (source, 30), unix_error_string (errno));
2648 /* Directory was changed outside MC. Reload it forced */
2649 if (!panel->is_panelized)
2651 panel_update_flags_t flags = UP_RELOAD;
2653 /* don't update panelized panel */
2654 if (get_other_type () == view_listing && other_panel->is_panelized)
2655 flags |= UP_ONLY_CURRENT;
2657 update_panels (flags, UP_KEEPSEL);
2659 vfs_path_free (source_vpath);
2660 return FALSE;
2662 vfs_path_free (source_vpath);
2665 ctx = file_op_context_new (operation);
2667 /* Show confirmation dialog */
2668 if (operation != OP_DELETE)
2670 char *tmp_dest_dir, *dest_dir;
2671 char *format;
2673 /* Forced single operations default to the original name */
2674 if (force_single)
2675 tmp_dest_dir = g_strdup (source);
2676 else if (get_other_type () == view_listing)
2677 tmp_dest_dir = vfs_path_to_str (other_panel->cwd_vpath);
2678 else
2679 tmp_dest_dir = vfs_path_to_str (panel->cwd_vpath);
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 && tmp_dest_dir[0] != '\0'
2686 && tmp_dest_dir[strlen (tmp_dest_dir) - 1] != PATH_SEP)
2688 /* add trailing separator */
2689 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
2690 g_free (tmp_dest_dir);
2692 else
2694 /* just copy */
2695 dest_dir = tmp_dest_dir;
2697 if (dest_dir == NULL)
2699 ret_val = FALSE;
2700 goto ret_fast;
2703 /* Generate confirmation prompt */
2704 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2706 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2707 source != NULL ? (void *) source
2708 : (void *) &panel->marked, dest_dir, &do_bg);
2710 g_free (format);
2711 g_free (dest_dir);
2713 if (dest == NULL || dest[0] == '\0')
2715 g_free (dest);
2716 ret_val = FALSE;
2717 goto ret_fast;
2719 dest_vpath = vfs_path_from_str (dest);
2721 else if (confirm_delete)
2723 char *format;
2724 char fmd_buf[BUF_MEDIUM];
2726 /* Generate confirmation prompt */
2727 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2729 if (source == NULL)
2730 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2731 else
2733 const int fmd_xlen = 64;
2734 i = fmd_xlen - str_term_width1 (format) - 4;
2735 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2738 g_free (format);
2740 if (safe_delete)
2741 query_set_sel (1);
2743 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2745 if (i != 0)
2747 ret_val = FALSE;
2748 goto ret_fast;
2752 tctx = file_op_total_context_new ();
2753 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2756 filegui_dialog_type_t dialog_type;
2758 if (operation == OP_DELETE)
2759 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2760 else
2762 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2763 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2765 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2766 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2769 /* Background also need ctx->ui, but not full */
2770 if (do_bg)
2771 file_op_context_create_ui_without_init (ctx, TRUE, dialog_type);
2772 else
2773 file_op_context_create_ui (ctx, TRUE, dialog_type);
2776 #ifdef ENABLE_BACKGROUND
2777 /* Did the user select to do a background operation? */
2778 if (do_bg)
2780 int v;
2781 char *cwd_str;
2783 cwd_str = vfs_path_to_str (panel->cwd_vpath);
2784 v = do_background (ctx, g_strconcat (op_names[operation], ": ", cwd_str, (char *) NULL));
2785 g_free (cwd_str);
2786 if (v == -1)
2787 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2789 /* If we are the parent */
2790 if (v == 1)
2792 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
2794 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
2795 vfs_path_free (dest_vpath);
2796 g_free (dest);
2797 /* file_op_context_destroy (ctx); */
2798 return FALSE;
2801 #endif /* ENABLE_BACKGROUND */
2803 /* Initialize things */
2804 /* We do not want to trash cache every time file is
2805 created/touched. However, this will make our cache contain
2806 invalid data. */
2807 if ((dest != NULL) && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2808 save_dest = g_strdup (dest);
2810 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
2811 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2812 save_cwd = vfs_path_to_str (panel->cwd_vpath);
2814 /* Now, let's do the job */
2816 /* This code is only called by the tree and panel code */
2817 if (single_entry)
2819 /* We now have ETA in all cases */
2821 /* One file: FIXME mc_chdir will take user out of any vfs */
2822 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2824 vfs_path_t *vpath;
2825 int chdir_retcode;
2827 vpath = vfs_path_from_str (PATH_SEP_STR);
2828 chdir_retcode = mc_chdir (vpath);
2829 vfs_path_free (vpath);
2830 if (chdir_retcode < 0)
2832 ret_val = FALSE;
2833 goto clean_up;
2837 /* The source and src_stat variables have been initialized before */
2838 #ifdef WITH_FULL_PATHS
2839 if (g_path_is_absolute (source))
2840 source_with_vpath = vfs_path_from_str (source);
2841 else
2842 source_with_vpath = vfs_path_append_new (panel->cwd_vpath, source, (char *) NULL);
2843 source_with_path_str = vfs_path_to_str (source_with_vpath);
2844 #endif /* WITH_FULL_PATHS */
2845 if (panel_operate_init_totals (operation, panel, source_with_path_str, ctx) == FILE_CONT)
2847 if (operation == OP_DELETE)
2849 if (S_ISDIR (src_stat.st_mode))
2850 value = erase_dir (tctx, ctx, source_with_path_str);
2851 else
2852 value = erase_file (tctx, ctx, source_with_path_str);
2854 else
2856 temp = transform_source (ctx, source_with_path_str);
2857 if (temp == NULL)
2858 value = transform_error;
2859 else
2861 char *repl_dest, *temp2;
2863 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2864 temp2 = concat_dir_and_file (repl_dest, temp);
2865 g_free (temp);
2866 g_free (repl_dest);
2867 g_free (dest);
2868 vfs_path_free (dest_vpath);
2869 dest = temp2;
2870 dest_vpath = vfs_path_from_str (dest);
2872 switch (operation)
2874 case OP_COPY:
2875 /* we use file_mask_op_follow_links only with OP_COPY */
2876 ctx->stat_func (source_with_vpath, &src_stat);
2878 if (S_ISDIR (src_stat.st_mode))
2879 value = copy_dir_dir (tctx, ctx, source_with_path_str, dest,
2880 TRUE, FALSE, FALSE, NULL);
2881 else
2882 value = copy_file_file (tctx, ctx, source_with_path_str, dest);
2883 break;
2885 case OP_MOVE:
2886 if (S_ISDIR (src_stat.st_mode))
2887 value = move_dir_dir (tctx, ctx, source_with_path_str, dest);
2888 else
2889 value = move_file_file (tctx, ctx, source_with_path_str, dest);
2890 break;
2892 default:
2893 /* Unknown file operation */
2894 abort ();
2897 } /* Copy or move operation */
2899 if ((value == FILE_CONT) && !force_single)
2900 unmark_files (panel);
2903 else
2905 /* Many files */
2907 /* Check destination for copy or move operation */
2908 while (operation != OP_DELETE)
2910 int dst_result;
2911 struct stat dst_stat;
2913 dst_result = mc_stat (dest_vpath, &dst_stat);
2915 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2916 break;
2918 if (ctx->skip_all
2919 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2920 goto clean_up;
2923 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2925 /* Loop for every file, perform the actual copy operation */
2926 for (i = 0; i < panel->count; i++)
2928 const char *source2;
2930 if (!panel->dir.list[i].f.marked)
2931 continue; /* Skip the unmarked ones */
2933 source2 = panel->dir.list[i].fname;
2934 src_stat = panel->dir.list[i].st;
2936 #ifdef WITH_FULL_PATHS
2937 g_free (source_with_path_str);
2938 vfs_path_free (source_with_vpath);
2939 if (g_path_is_absolute (source2))
2940 source_with_vpath = vfs_path_from_str (source2);
2941 else
2942 source_with_vpath =
2943 vfs_path_append_new (panel->cwd_vpath, source2, (char *) NULL);
2944 source_with_path_str = vfs_path_to_str (source_with_vpath);
2945 #endif /* WITH_FULL_PATHS */
2947 if (operation == OP_DELETE)
2949 if (S_ISDIR (src_stat.st_mode))
2950 value = erase_dir (tctx, ctx, source_with_path_str);
2951 else
2952 value = erase_file (tctx, ctx, source_with_path_str);
2954 else
2956 temp = transform_source (ctx, source_with_path_str);
2958 if (temp == NULL)
2959 value = transform_error;
2960 else
2962 char *temp2, *temp3, *repl_dest;
2964 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2965 temp2 = concat_dir_and_file (repl_dest, temp);
2966 g_free (temp);
2967 g_free (repl_dest);
2968 temp3 = source_with_path_str;
2969 source_with_path_str = strutils_shell_unescape (source_with_path_str);
2970 g_free (temp3);
2971 temp3 = temp2;
2972 temp2 = strutils_shell_unescape (temp2);
2973 g_free (temp3);
2975 switch (operation)
2977 case OP_COPY:
2978 /* we use file_mask_op_follow_links only with OP_COPY */
2980 vfs_path_t *vpath;
2982 vpath = vfs_path_from_str (source_with_path_str);
2983 ctx->stat_func (vpath, &src_stat);
2984 vfs_path_free (vpath);
2986 if (S_ISDIR (src_stat.st_mode))
2987 value = copy_dir_dir (tctx, ctx, source_with_path_str, temp2,
2988 TRUE, FALSE, FALSE, NULL);
2989 else
2990 value = copy_file_file (tctx, ctx, source_with_path_str, temp2);
2991 free_linklist (&dest_dirs);
2992 break;
2994 case OP_MOVE:
2995 if (S_ISDIR (src_stat.st_mode))
2996 value = move_dir_dir (tctx, ctx, source_with_path_str, temp2);
2997 else
2998 value = move_file_file (tctx, ctx, source_with_path_str, temp2);
2999 break;
3001 default:
3002 /* Unknown file operation */
3003 abort ();
3006 g_free (temp2);
3008 } /* Copy or move operation */
3010 if (value == FILE_ABORT)
3011 break;
3013 if (value == FILE_CONT)
3014 do_file_mark (panel, i, 0);
3016 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3018 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3019 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3022 if (operation != OP_DELETE)
3023 file_progress_show (ctx, 0, 0, "", FALSE);
3025 if (check_progress_buttons (ctx) == FILE_ABORT)
3026 break;
3028 mc_refresh ();
3029 } /* Loop for every file */
3031 } /* Many entries */
3033 clean_up:
3034 /* Clean up */
3035 if (save_cwd != NULL)
3037 tmp_vpath = vfs_path_from_str (save_cwd);
3038 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3039 vfs_path_free (tmp_vpath);
3040 g_free (save_cwd);
3043 if (save_dest != NULL)
3045 tmp_vpath = vfs_path_from_str (save_dest);
3046 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3047 vfs_path_free (tmp_vpath);
3048 g_free (save_dest);
3051 free_linklist (&linklist);
3052 free_linklist (&dest_dirs);
3053 #ifdef WITH_FULL_PATHS
3054 g_free (source_with_path_str);
3055 vfs_path_free (source_with_vpath);
3056 #endif /* WITH_FULL_PATHS */
3057 g_free (dest);
3058 vfs_path_free (dest_vpath);
3059 g_free (ctx->dest_mask);
3060 ctx->dest_mask = NULL;
3062 #ifdef ENABLE_BACKGROUND
3063 /* Let our parent know we are saying bye bye */
3064 if (mc_global.we_are_background)
3066 int cur_pid = getpid ();
3067 /* Send pid to parent with child context, it is fork and
3068 don't modify real parent ctx */
3069 ctx->pid = cur_pid;
3070 parent_call ((void *) end_bg_process, ctx, 0);
3072 vfs_shut ();
3073 _exit (0);
3075 #endif /* ENABLE_BACKGROUND */
3077 file_op_total_context_destroy (tctx);
3078 ret_fast:
3079 file_op_context_destroy (ctx);
3080 g_free (source);
3082 return ret_val;
3085 /* }}} */
3087 /* --------------------------------------------------------------------------------------------- */
3088 /* {{{ Query/status report routines */
3089 /** Report error with one file */
3090 FileProgressStatus
3091 file_error (const char *format, const char *file)
3093 char buf[BUF_MEDIUM];
3095 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3097 return do_file_error (buf);
3100 /* --------------------------------------------------------------------------------------------- */
3103 Cause emacs to enter folding mode for this file:
3104 Local variables:
3105 end: