Changed treestore functions to handle vfs_path_t objects.
[midnight-commander.git] / src / filemanager / file.c
blob4d9f938dfc833d2e712398dd832ecabae753a658
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, *s;
417 vfs_path_t *q;
419 const char *r = strrchr (src_path, PATH_SEP);
421 if (r)
423 p = g_strndup (src_path, r - src_path + 1);
424 if (g_path_is_absolute (dst_path))
425 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
426 else
427 q = vfs_path_build_filename (p, dst_path, (char *) NULL);
429 if (vfs_path_tokens_count (q) > 1)
431 vfs_path_t *tmp_vpath1, *tmp_vpath2;
433 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
434 s = g_strconcat (p, link_target, (char *) NULL);
435 g_free (p);
436 g_strlcpy (link_target, s, sizeof (link_target));
437 g_free (s);
438 tmp_vpath2 = vfs_path_from_str (link_target);
439 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
440 vfs_path_free (tmp_vpath1);
441 vfs_path_free (tmp_vpath2);
442 if (s)
444 g_strlcpy (link_target, s, sizeof (link_target));
445 g_free (s);
448 else
449 g_free (p);
450 vfs_path_free (q);
453 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
455 retry_dst_symlink:
456 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
458 /* Success */
459 return_status = FILE_CONT;
460 goto ret;
463 * if dst_exists, it is obvious that this had failed.
464 * We can delete the old symlink and try again...
466 if (dst_is_symlink)
468 if (mc_unlink (dst_vpath) == 0)
469 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
471 /* Success */
472 return_status = FILE_CONT;
473 goto ret;
476 if (ctx->skip_all)
477 return_status = FILE_SKIPALL;
478 else
480 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
481 if (return_status == FILE_SKIPALL)
482 ctx->skip_all = TRUE;
483 if (return_status == FILE_RETRY)
484 goto retry_dst_symlink;
487 ret:
488 vfs_path_free (src_vpath);
489 vfs_path_free (dst_vpath);
490 vfs_path_free (link_target_vpath);
491 return return_status;
494 /* --------------------------------------------------------------------------------------------- */
496 static FileProgressStatus
497 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add)
499 struct timeval tv_current;
500 static struct timeval tv_start = { };
502 tctx->progress_count++;
503 tctx->progress_bytes += (uintmax_t) add;
505 if (tv_start.tv_sec == 0)
507 gettimeofday (&tv_start, (struct timezone *) NULL);
509 gettimeofday (&tv_current, (struct timezone *) NULL);
510 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
512 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
514 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
515 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
517 tv_start.tv_sec = tv_current.tv_sec;
520 return check_progress_buttons (ctx);
523 /* --------------------------------------------------------------------------------------------- */
525 static FileProgressStatus
526 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
528 char *msg;
529 int result = 0;
530 const char *head_msg;
532 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
534 msg = g_strdup_printf (fmt, a, b);
535 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
536 g_free (msg);
537 do_refresh ();
539 return (result == 1) ? FILE_ABORT : FILE_SKIP;
542 /* --------------------------------------------------------------------------------------------- */
544 static FileProgressStatus
545 warn_same_file (const char *fmt, const char *a, const char *b)
547 #ifdef ENABLE_BACKGROUND
548 /* *INDENT-OFF* */
549 union
551 void *p;
552 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
553 const char *a, const char *b);
554 } pntr;
555 /* *INDENT-ON* */
557 pntr.f = real_warn_same_file;
559 if (mc_global.we_are_background)
560 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
561 #endif
562 return real_warn_same_file (Foreground, fmt, a, b);
565 /* --------------------------------------------------------------------------------------------- */
566 /* {{{ Query/status report routines */
568 static FileProgressStatus
569 real_do_file_error (enum OperationMode mode, const char *error)
571 int result;
572 const char *msg;
574 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
575 result =
576 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
578 switch (result)
580 case 0:
581 do_refresh ();
582 return FILE_SKIP;
584 case 1:
585 do_refresh ();
586 return FILE_SKIPALL;
588 case 2:
589 do_refresh ();
590 return FILE_RETRY;
592 case 3:
593 default:
594 return FILE_ABORT;
598 /* --------------------------------------------------------------------------------------------- */
600 static FileProgressStatus
601 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
603 gchar *text;
605 if (ctx->recursive_result < RECURSIVE_ALWAYS)
607 const char *msg = mode == Foreground
608 ? _("\nDirectory not empty.\nDelete it recursively?")
609 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
610 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
612 if (safe_delete)
613 query_set_sel (1);
615 ctx->recursive_result =
616 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
617 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
619 if (ctx->recursive_result != RECURSIVE_ABORT)
620 do_refresh ();
621 g_free (text);
624 switch (ctx->recursive_result)
626 case RECURSIVE_YES:
627 case RECURSIVE_ALWAYS:
628 return FILE_CONT;
630 case RECURSIVE_NO:
631 case RECURSIVE_NEVER:
632 return FILE_SKIP;
634 case RECURSIVE_ABORT:
635 default:
636 return FILE_ABORT;
640 /* --------------------------------------------------------------------------------------------- */
642 #ifdef ENABLE_BACKGROUND
643 static FileProgressStatus
644 do_file_error (const char *str)
646 union
648 void *p;
649 FileProgressStatus (*f) (enum OperationMode, const char *);
650 } pntr;
651 pntr.f = real_do_file_error;
653 if (mc_global.we_are_background)
654 return parent_call (pntr.p, NULL, 1, strlen (str), str);
655 else
656 return real_do_file_error (Foreground, str);
659 /* --------------------------------------------------------------------------------------------- */
661 static FileProgressStatus
662 query_recursive (FileOpContext * ctx, const char *s)
664 union
666 void *p;
667 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
668 } pntr;
669 pntr.f = real_query_recursive;
671 if (mc_global.we_are_background)
672 return parent_call (pntr.p, ctx, 1, strlen (s), s);
673 else
674 return real_query_recursive (ctx, Foreground, s);
677 /* --------------------------------------------------------------------------------------------- */
679 static FileProgressStatus
680 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
681 struct stat *_d_stat)
683 union
685 void *p;
686 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
687 struct stat *, struct stat *);
688 } pntr;
689 pntr.f = file_progress_real_query_replace;
691 if (mc_global.we_are_background)
692 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
693 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
694 else
695 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
698 #else
699 /* --------------------------------------------------------------------------------------------- */
701 static FileProgressStatus
702 do_file_error (const char *str)
704 return real_do_file_error (Foreground, str);
707 /* --------------------------------------------------------------------------------------------- */
709 static FileProgressStatus
710 query_recursive (FileOpContext * ctx, const char *s)
712 return real_query_recursive (ctx, Foreground, s);
715 /* --------------------------------------------------------------------------------------------- */
717 static FileProgressStatus
718 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
719 struct stat *_d_stat)
721 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
724 #endif /* !ENABLE_BACKGROUND */
726 /* --------------------------------------------------------------------------------------------- */
727 /** Report error with two files */
729 static FileProgressStatus
730 files_error (const char *format, const char *file1, const char *file2)
732 char buf[BUF_MEDIUM];
733 char *nfile1 = g_strdup (path_trunc (file1, 15));
734 char *nfile2 = g_strdup (path_trunc (file2, 15));
736 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
738 g_free (nfile1);
739 g_free (nfile2);
741 return do_file_error (buf);
744 /* }}} */
746 /* --------------------------------------------------------------------------------------------- */
748 static void
749 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
750 struct timeval tv_current, struct timeval tv_transfer_start,
751 off_t file_size, off_t n_read_total)
753 long dt;
755 /* 1. Update rotating dash after some time */
756 rotate_dash ();
758 /* 3. Compute ETA */
759 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
761 if (n_read_total == 0)
762 ctx->eta_secs = 0.0;
763 else
765 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
766 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
769 /* 4. Compute BPS rate */
770 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
771 if (ctx->bps_time < 1)
772 ctx->bps_time = 1;
773 ctx->bps = n_read_total / ctx->bps_time;
775 /* 5. Compute total ETA and BPS */
776 if (ctx->progress_bytes != 0)
778 uintmax_t remain_bytes;
780 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
781 #if 1
783 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
785 if (total_secs < 1)
786 total_secs = 1;
788 tctx->bps = tctx->copied_bytes / total_secs;
789 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
791 #else
792 /* broken on lot of little files */
793 tctx->bps_count++;
794 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
795 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
796 #endif
800 /* --------------------------------------------------------------------------------------------- */
802 /* {{{ Move routines */
803 static FileProgressStatus
804 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
806 struct stat src_stats, dst_stats;
807 FileProgressStatus return_status = FILE_CONT;
808 gboolean copy_done = FALSE;
809 gboolean old_ask_overwrite;
810 vfs_path_t *src_vpath, *dst_vpath;
812 file_progress_show_source (ctx, s);
813 file_progress_show_target (ctx, d);
814 if (check_progress_buttons (ctx) == FILE_ABORT)
815 return FILE_ABORT;
817 mc_refresh ();
818 src_vpath = vfs_path_from_str (s);
819 dst_vpath = vfs_path_from_str (d);
821 while (mc_lstat (src_vpath, &src_stats) != 0)
823 /* Source doesn't exist */
824 if (ctx->skip_all)
825 return_status = FILE_SKIPALL;
826 else
828 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
829 if (return_status == FILE_SKIPALL)
830 ctx->skip_all = TRUE;
832 if (return_status != FILE_RETRY)
834 vfs_path_free (src_vpath);
835 vfs_path_free (dst_vpath);
836 return return_status;
840 if (mc_lstat (dst_vpath, &dst_stats) == 0)
842 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
843 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
845 if (S_ISDIR (dst_stats.st_mode))
847 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
848 do_refresh ();
849 vfs_path_free (src_vpath);
850 vfs_path_free (dst_vpath);
851 return FILE_SKIP;
854 if (confirm_overwrite)
856 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
857 if (return_status != FILE_CONT)
859 vfs_path_free (src_vpath);
860 vfs_path_free (dst_vpath);
861 return return_status;
864 /* Ok to overwrite */
867 if (!ctx->do_append)
869 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
871 return_status = make_symlink (ctx, s, d);
872 if (return_status == FILE_CONT)
873 goto retry_src_remove;
874 else
876 vfs_path_free (src_vpath);
877 vfs_path_free (dst_vpath);
878 return return_status;
882 if (mc_rename (src_vpath, dst_vpath) == 0)
884 vfs_path_free (src_vpath);
885 vfs_path_free (dst_vpath);
886 return progress_update_one (tctx, ctx, src_stats.st_size);
889 #if 0
890 /* Comparison to EXDEV seems not to work in nfs if you're moving from
891 one nfs to the same, but on the server it is on two different
892 filesystems. Then nfs returns EIO instead of EXDEV.
893 Hope it will not hurt if we always in case of error try to copy/delete. */
894 else
895 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
897 if (errno != EXDEV)
899 if (ctx->skip_all)
900 return_status = FILE_SKIPALL;
901 else
903 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
904 if (return_status == FILE_SKIPALL)
905 ctx->skip_all = TRUE;
906 if (return_status == FILE_RETRY)
907 goto retry_rename;
909 vfs_path_free (src_vpath);
910 vfs_path_free (dst_vpath);
912 return return_status;
914 #endif
916 /* Failed because filesystem boundary -> copy the file instead */
917 old_ask_overwrite = tctx->ask_overwrite;
918 tctx->ask_overwrite = FALSE;
919 return_status = copy_file_file (tctx, ctx, s, d);
920 tctx->ask_overwrite = old_ask_overwrite;
921 if (return_status != FILE_CONT)
923 vfs_path_free (src_vpath);
924 vfs_path_free (dst_vpath);
925 return return_status;
928 copy_done = TRUE;
930 file_progress_show_source (ctx, NULL);
931 file_progress_show (ctx, 0, 0, "", FALSE);
933 return_status = check_progress_buttons (ctx);
934 if (return_status != FILE_CONT)
936 vfs_path_free (src_vpath);
937 vfs_path_free (dst_vpath);
938 return return_status;
941 mc_refresh ();
943 retry_src_remove:
944 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
946 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
947 if (return_status == FILE_RETRY)
948 goto retry_src_remove;
949 if (return_status == FILE_SKIPALL)
950 ctx->skip_all = TRUE;
952 vfs_path_free (src_vpath);
953 vfs_path_free (dst_vpath);
954 return return_status;
957 if (!copy_done)
958 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
960 vfs_path_free (src_vpath);
961 vfs_path_free (dst_vpath);
963 return return_status;
966 /* }}} */
968 /* --------------------------------------------------------------------------------------------- */
969 /* {{{ Erase routines */
970 /** Don't update progress status if progress_count==NULL */
972 static FileProgressStatus
973 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
975 int return_status;
976 struct stat buf;
977 vfs_path_t *vpath = vfs_path_from_str (s);
979 file_progress_show_deleting (ctx, s);
980 if (check_progress_buttons (ctx) == FILE_ABORT)
981 return FILE_ABORT;
982 mc_refresh ();
984 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
986 /* ignore, most likely the mc_unlink fails, too */
987 buf.st_size = 0;
990 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
992 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
993 if (return_status == FILE_ABORT)
995 vfs_path_free (vpath);
996 return return_status;
998 if (return_status == FILE_RETRY)
999 continue;
1000 if (return_status == FILE_SKIPALL)
1001 ctx->skip_all = TRUE;
1002 break;
1005 vfs_path_free (vpath);
1006 if (tctx->progress_count == 0)
1007 return FILE_CONT;
1008 return progress_update_one (tctx, ctx, buf.st_size);
1011 /* --------------------------------------------------------------------------------------------- */
1014 Recursive remove of files
1015 abort->cancel stack
1016 skip ->warn every level, gets default
1017 skipall->remove as much as possible
1019 static FileProgressStatus
1020 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
1022 struct dirent *next;
1023 struct stat buf;
1024 DIR *reading;
1025 char *path;
1026 FileProgressStatus return_status = FILE_CONT;
1027 vfs_path_t *vpath;
1029 if (strcmp (s, "..") == 0)
1030 return FILE_RETRY;
1032 vpath = vfs_path_from_str (s);
1033 reading = mc_opendir (vpath);
1035 if (reading == NULL)
1037 return_status = FILE_RETRY;
1038 goto ret;
1041 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1043 vfs_path_t *tmp_vpath;
1045 if (!strcmp (next->d_name, "."))
1046 continue;
1047 if (!strcmp (next->d_name, ".."))
1048 continue;
1049 path = concat_dir_and_file (s, next->d_name);
1050 tmp_vpath = vfs_path_from_str (path);
1051 if (mc_lstat (tmp_vpath, &buf) != 0)
1053 g_free (path);
1054 mc_closedir (reading);
1055 vfs_path_free (tmp_vpath);
1056 return_status = FILE_RETRY;
1057 goto ret;
1059 if (S_ISDIR (buf.st_mode))
1060 return_status = recursive_erase (tctx, ctx, path);
1061 else
1062 return_status = erase_file (tctx, ctx, path);
1063 vfs_path_free (tmp_vpath);
1064 g_free (path);
1066 mc_closedir (reading);
1067 if (return_status == FILE_ABORT)
1068 goto ret;
1070 file_progress_show_deleting (ctx, s);
1071 if (check_progress_buttons (ctx) == FILE_ABORT)
1073 return_status = FILE_ABORT;
1074 goto ret;
1076 mc_refresh ();
1078 while (my_rmdir (s) != 0 && !ctx->skip_all)
1080 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1081 if (return_status == FILE_RETRY)
1082 continue;
1083 if (return_status == FILE_ABORT)
1084 goto ret;
1085 if (return_status == FILE_SKIPALL)
1086 ctx->skip_all = TRUE;
1087 break;
1090 ret:
1091 vfs_path_free (vpath);
1092 return return_status;
1095 /* --------------------------------------------------------------------------------------------- */
1096 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1097 in the directory path points to, 0 else. */
1099 static int
1100 check_dir_is_empty (const char *path)
1102 DIR *dir;
1103 struct dirent *d;
1104 int i;
1105 vfs_path_t *vpath = vfs_path_from_str (path);
1107 dir = mc_opendir (vpath);
1108 if (!dir)
1110 vfs_path_free (vpath);
1111 return -1;
1114 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1116 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1117 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1118 continue; /* "." or ".." */
1119 i = 0;
1120 break;
1123 mc_closedir (dir);
1124 vfs_path_free (vpath);
1125 return i;
1128 /* --------------------------------------------------------------------------------------------- */
1130 static FileProgressStatus
1131 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1133 FileProgressStatus error;
1135 if (strcmp (s, "..") == 0)
1136 return FILE_SKIP;
1138 if (strcmp (s, ".") == 0)
1139 return FILE_SKIP;
1141 file_progress_show_deleting (ctx, s);
1142 if (check_progress_buttons (ctx) == FILE_ABORT)
1143 return FILE_ABORT;
1144 mc_refresh ();
1146 if (1 != check_dir_is_empty (s)) /* not empty or error */
1147 return FILE_CONT;
1149 while (my_rmdir (s) != 0 && !ctx->skip_all)
1151 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1152 if (error == FILE_SKIPALL)
1153 ctx->skip_all = TRUE;
1154 if (error != FILE_RETRY)
1155 return error;
1158 return FILE_CONT;
1161 /* }}} */
1163 /* --------------------------------------------------------------------------------------------- */
1164 /* {{{ Panel operate routines */
1167 * Return currently selected entry name or the name of the first marked
1168 * entry if there is one.
1171 static char *
1172 panel_get_file (WPanel * panel)
1174 if (get_current_type () == view_tree)
1176 WTree *tree;
1178 tree = (WTree *) get_panel_widget (get_current_index ());
1179 return vfs_path_to_str (tree_selected_name (tree));
1182 if (panel->marked != 0)
1184 int i;
1186 for (i = 0; i < panel->count; i++)
1187 if (panel->dir.list[i].f.marked)
1188 return g_strdup (panel->dir.list[i].fname);
1190 return g_strdup (panel->dir.list[panel->selected].fname);
1193 /* --------------------------------------------------------------------------------------------- */
1195 * panel_compute_totals:
1197 * compute the number of files and the number of bytes
1198 * used up by the whole selection, recursing directories
1199 * as required. In addition, it checks to see if it will
1200 * overwrite any files by doing the copy.
1203 static FileProgressStatus
1204 panel_compute_totals (const WPanel * panel, const void *ui,
1205 compute_dir_size_callback cback,
1206 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1208 int i;
1210 *ret_marked = 0;
1211 *ret_total = 0;
1213 for (i = 0; i < panel->count; i++)
1215 struct stat *s;
1217 if (!panel->dir.list[i].f.marked)
1218 continue;
1220 s = &panel->dir.list[i].st;
1222 if (S_ISDIR (s->st_mode))
1224 vfs_path_t *p;
1225 size_t subdir_count = 0;
1226 uintmax_t subdir_bytes = 0;
1227 FileProgressStatus status;
1229 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, NULL);
1230 status =
1231 compute_dir_size (p, ui, cback, &subdir_count, &subdir_bytes, compute_symlinks);
1232 vfs_path_free (p);
1234 if (status != FILE_CONT)
1235 return FILE_ABORT;
1237 *ret_marked += subdir_count;
1238 *ret_total += subdir_bytes;
1240 else
1242 (*ret_marked)++;
1243 *ret_total += (uintmax_t) s->st_size;
1247 return FILE_CONT;
1250 /* --------------------------------------------------------------------------------------------- */
1252 /** Initialize variables for progress bars */
1253 static FileProgressStatus
1254 panel_operate_init_totals (FileOperation operation,
1255 const WPanel * panel, const char *source, FileOpContext * ctx)
1257 FileProgressStatus status;
1259 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1261 ComputeDirSizeUI *ui;
1263 ui = compute_dir_size_create_ui ();
1265 if (source == NULL)
1266 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1267 &ctx->progress_count, &ctx->progress_bytes,
1268 ctx->follow_links);
1269 else
1271 vfs_path_t *p;
1273 p = vfs_path_from_str (source);
1274 status = compute_dir_size (p, ui, compute_dir_size_update_ui,
1275 &ctx->progress_count, &ctx->progress_bytes,
1276 ctx->follow_links);
1277 vfs_path_free (p);
1280 compute_dir_size_destroy_ui (ui);
1282 ctx->progress_totals_computed = (status == FILE_CONT);
1284 else
1286 status = FILE_CONT;
1287 ctx->progress_count = panel->marked;
1288 ctx->progress_bytes = panel->total;
1289 ctx->progress_totals_computed = FALSE;
1292 return status;
1295 /* --------------------------------------------------------------------------------------------- */
1297 * Generate user prompt for panel operation.
1298 * single_source is the name if the source entry or NULL for multiple
1299 * entries.
1300 * src_stat is only used when single_source is not NULL.
1303 static char *
1304 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1305 gboolean single_source, const struct stat *src_stat)
1307 const char *sp, *cp;
1308 char format_string[BUF_MEDIUM];
1309 char *dp = format_string;
1310 gboolean build_question = FALSE;
1312 static gboolean i18n_flag = FALSE;
1313 if (!i18n_flag)
1315 size_t i;
1317 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1318 op_names1[i] = Q_ (op_names1[i]);
1320 #ifdef ENABLE_NLS
1321 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1322 prompt_parts[i] = _(prompt_parts[i]);
1324 one_format = _(one_format);
1325 many_format = _(many_format);
1326 question_format = _(question_format);
1327 #endif /* ENABLE_NLS */
1328 i18n_flag = TRUE;
1331 sp = single_source ? one_format : many_format;
1333 while (*sp != '\0')
1335 switch (*sp)
1337 case '%':
1338 cp = NULL;
1339 switch (sp[1])
1341 case 'o':
1342 cp = op_names1[operation];
1343 break;
1344 case 'm':
1345 if (operation == OP_DELETE)
1347 cp = "";
1348 build_question = TRUE;
1350 else
1351 cp = prompt_parts[5];
1352 break;
1353 case 'e':
1354 if (operation == OP_DELETE)
1356 cp = "";
1357 build_question = TRUE;
1359 else
1360 cp = prompt_parts[6];
1361 break;
1362 case 'f':
1363 if (single_source)
1364 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1365 else
1366 cp = (panel->marked == panel->dirs_marked)
1367 ? prompt_parts[3]
1368 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1369 break;
1370 default:
1371 *dp++ = *sp++;
1374 if (cp != NULL)
1376 sp += 2;
1377 while (*cp != '\0')
1378 *dp++ = *cp++;
1380 break;
1381 default:
1382 *dp++ = *sp++;
1385 *dp = '\0';
1387 if (build_question)
1389 char tmp[BUF_MEDIUM];
1391 memmove (tmp, format_string, sizeof (tmp));
1392 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1395 return g_strdup (format_string);
1398 /* --------------------------------------------------------------------------------------------- */
1400 #ifdef ENABLE_BACKGROUND
1401 static int
1402 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1404 int pid = ctx->pid;
1406 (void) mode;
1407 ctx->pid = 0;
1409 unregister_task_with_pid (pid);
1410 /* file_op_context_destroy(ctx); */
1411 return 1;
1413 #endif
1414 /* }}} */
1416 /* --------------------------------------------------------------------------------------------- */
1417 /*** public functions ****************************************************************************/
1418 /* --------------------------------------------------------------------------------------------- */
1420 FileProgressStatus
1421 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1422 const char *src_path, const char *dst_path)
1424 uid_t src_uid = (uid_t) - 1;
1425 gid_t src_gid = (gid_t) - 1;
1427 int src_desc, dest_desc = -1;
1428 int n_read, n_written;
1429 mode_t src_mode = 0; /* The mode of the source file */
1430 struct stat sb, sb2;
1431 struct utimbuf utb;
1432 gboolean dst_exists = FALSE, appending = FALSE;
1433 off_t file_size = -1;
1434 FileProgressStatus return_status, temp_status;
1435 struct timeval tv_transfer_start;
1436 dest_status_t dst_status = DEST_NONE;
1437 int open_flags;
1438 gboolean is_first_time = TRUE;
1439 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1441 /* FIXME: We should not be using global variables! */
1442 ctx->do_reget = 0;
1443 return_status = FILE_RETRY;
1445 file_progress_show_source (ctx, src_path);
1446 file_progress_show_target (ctx, dst_path);
1447 if (check_progress_buttons (ctx) == FILE_ABORT)
1448 return FILE_ABORT;
1450 mc_refresh ();
1452 dst_vpath = vfs_path_from_str (dst_path);
1453 while (mc_stat (dst_vpath, &sb2) == 0)
1455 if (S_ISDIR (sb2.st_mode))
1457 if (ctx->skip_all)
1458 return_status = FILE_SKIPALL;
1459 else
1461 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1462 if (return_status == FILE_SKIPALL)
1463 ctx->skip_all = TRUE;
1464 if (return_status == FILE_RETRY)
1465 continue;
1467 goto ret_fast;
1469 dst_exists = TRUE;
1470 break;
1473 src_vpath = vfs_path_from_str (src_path);
1474 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1476 if (ctx->skip_all)
1477 return_status = FILE_SKIPALL;
1478 else
1480 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1481 if (return_status == FILE_SKIPALL)
1482 ctx->skip_all = TRUE;
1484 if (return_status != FILE_RETRY)
1485 goto ret_fast;
1488 if (dst_exists)
1490 /* Destination already exists */
1491 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1493 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1494 src_path, dst_path);
1495 goto ret_fast;
1497 /* Should we replace destination? */
1498 if (tctx->ask_overwrite)
1500 ctx->do_reget = 0;
1501 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1502 if (return_status != FILE_CONT)
1503 goto ret_fast;
1507 if (!ctx->do_append)
1509 /* Check the hardlinks */
1510 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb))
1512 /* We have made a hardlink - no more processing is necessary */
1513 return_status = FILE_CONT;
1514 goto ret_fast;
1517 if (S_ISLNK (sb.st_mode))
1519 return_status = make_symlink (ctx, src_path, dst_path);
1520 goto ret_fast;
1523 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1524 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1526 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1527 && !ctx->skip_all)
1529 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1530 if (return_status == FILE_RETRY)
1531 continue;
1532 if (return_status == FILE_SKIPALL)
1533 ctx->skip_all = TRUE;
1534 goto ret_fast;
1536 /* Success */
1538 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1539 && !ctx->skip_all)
1541 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1542 if (temp_status == FILE_SKIP)
1543 break;
1544 if (temp_status == FILE_SKIPALL)
1545 ctx->skip_all = TRUE;
1546 if (temp_status != FILE_RETRY)
1548 return_status = temp_status;
1549 goto ret_fast;
1553 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1554 && !ctx->skip_all)
1556 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1557 if (temp_status == FILE_SKIP)
1558 break;
1559 if (temp_status == FILE_SKIPALL)
1560 ctx->skip_all = TRUE;
1561 if (temp_status != FILE_RETRY)
1563 return_status = temp_status;
1564 goto ret_fast;
1568 return_status = FILE_CONT;
1569 goto ret_fast;
1573 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1575 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1577 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1578 if (return_status == FILE_RETRY)
1579 continue;
1580 if (return_status == FILE_SKIPALL)
1581 ctx->skip_all = TRUE;
1582 if (return_status == FILE_SKIP)
1583 break;
1584 ctx->do_append = 0;
1585 goto ret_fast;
1588 if (ctx->do_reget != 0)
1590 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1592 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1593 ctx->do_reget = 0;
1594 ctx->do_append = FALSE;
1598 while (mc_fstat (src_desc, &sb) != 0)
1600 if (ctx->skip_all)
1601 return_status = FILE_SKIPALL;
1602 else
1604 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1605 if (return_status == FILE_RETRY)
1606 continue;
1607 if (return_status == FILE_SKIPALL)
1608 ctx->skip_all = TRUE;
1609 ctx->do_append = FALSE;
1611 goto ret;
1613 src_mode = sb.st_mode;
1614 src_uid = sb.st_uid;
1615 src_gid = sb.st_gid;
1616 utb.actime = sb.st_atime;
1617 utb.modtime = sb.st_mtime;
1618 file_size = sb.st_size;
1620 open_flags = O_WRONLY;
1621 if (dst_exists)
1623 if (ctx->do_append != 0)
1624 open_flags |= O_APPEND;
1625 else
1626 open_flags |= O_CREAT | O_TRUNC;
1628 else
1630 open_flags |= O_CREAT | O_EXCL;
1633 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
1635 if (errno != EEXIST)
1637 if (ctx->skip_all)
1638 return_status = FILE_SKIPALL;
1639 else
1641 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1642 if (return_status == FILE_RETRY)
1643 continue;
1644 if (return_status == FILE_SKIPALL)
1645 ctx->skip_all = TRUE;
1646 ctx->do_append = FALSE;
1649 goto ret;
1651 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1653 appending = ctx->do_append;
1654 ctx->do_append = FALSE;
1656 /* Find out the optimal buffer size. */
1657 while (mc_fstat (dest_desc, &sb) != 0)
1659 if (ctx->skip_all)
1660 return_status = FILE_SKIPALL;
1661 else
1663 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1664 if (return_status == FILE_RETRY)
1665 continue;
1666 if (return_status == FILE_SKIPALL)
1667 ctx->skip_all = TRUE;
1669 goto ret;
1672 while (TRUE)
1674 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1675 if (errno == 0)
1676 break;
1678 if (ctx->skip_all)
1679 return_status = FILE_SKIPALL;
1680 else
1682 return_status =
1683 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1684 if (return_status == FILE_RETRY)
1685 continue;
1686 if (return_status == FILE_SKIPALL)
1687 ctx->skip_all = TRUE;
1689 mc_close (dest_desc);
1690 dest_desc = -1;
1691 mc_unlink (dst_vpath);
1692 dst_status = DEST_NONE;
1693 goto ret;
1696 ctx->eta_secs = 0.0;
1697 ctx->bps = 0;
1699 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1700 file_progress_show (ctx, 0, file_size, "", TRUE);
1701 else
1702 file_progress_show (ctx, 1, 1, "", TRUE);
1703 return_status = check_progress_buttons (ctx);
1704 mc_refresh ();
1706 if (return_status != FILE_CONT)
1707 goto ret;
1710 off_t n_read_total = 0;
1711 struct timeval tv_current, tv_last_update, tv_last_input;
1712 int secs, update_secs;
1713 const char *stalled_msg = "";
1715 tv_last_update = tv_transfer_start;
1717 while (TRUE)
1719 char buf[BUF_8K];
1721 /* src_read */
1722 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1723 n_read = -1;
1724 else
1725 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1727 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1728 if (return_status == FILE_RETRY)
1729 continue;
1730 if (return_status == FILE_SKIPALL)
1731 ctx->skip_all = TRUE;
1732 goto ret;
1734 if (n_read == 0)
1735 break;
1737 gettimeofday (&tv_current, NULL);
1739 if (n_read > 0)
1741 char *t = buf;
1742 n_read_total += n_read;
1744 /* Windows NT ftp servers report that files have no
1745 * permissions: -------, so if we happen to have actually
1746 * read something, we should fix the permissions.
1748 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1749 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1750 gettimeofday (&tv_last_input, NULL);
1752 /* dst_write */
1753 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read && !ctx->skip_all)
1755 if (n_written > 0)
1757 n_read -= n_written;
1758 t += n_written;
1759 continue;
1761 return_status = file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1762 if (return_status == FILE_SKIP)
1763 break;
1764 if (return_status == FILE_SKIPALL)
1765 ctx->skip_all = TRUE;
1766 if (return_status != FILE_RETRY)
1767 goto ret;
1771 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1773 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1774 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1776 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1778 copy_file_file_display_progress (tctx, ctx,
1779 tv_current,
1780 tv_transfer_start, file_size, n_read_total);
1781 tv_last_update = tv_current;
1783 is_first_time = FALSE;
1785 if (update_secs > FILEOP_STALLING_INTERVAL)
1787 stalled_msg = _("(stalled)");
1791 gboolean force_update;
1793 force_update =
1794 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1796 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1798 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1799 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1802 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1803 force_update);
1805 mc_refresh ();
1807 return_status = check_progress_buttons (ctx);
1809 if (return_status != FILE_CONT)
1811 mc_refresh ();
1812 goto ret;
1817 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1819 ret:
1820 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1822 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1823 if (temp_status == FILE_RETRY)
1824 continue;
1825 if (temp_status == FILE_ABORT)
1826 return_status = temp_status;
1827 if (temp_status == FILE_SKIPALL)
1828 ctx->skip_all = TRUE;
1829 break;
1832 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1834 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1835 if (temp_status == FILE_RETRY)
1836 continue;
1837 if (temp_status == FILE_SKIPALL)
1838 ctx->skip_all = TRUE;
1839 return_status = temp_status;
1840 break;
1843 if (dst_status == DEST_SHORT)
1845 /* Remove short file */
1846 int result;
1848 result = query_dialog (Q_ ("DialogTitle|Copy"),
1849 _("Incomplete file was retrieved. Keep it?"),
1850 D_ERROR, 2, _("&Delete"), _("&Keep"));
1851 if (result == 0)
1852 mc_unlink (dst_vpath);
1854 else if (dst_status == DEST_FULL)
1856 /* Copy has succeeded */
1857 if (!appending && ctx->preserve_uidgid)
1859 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1861 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1862 if (temp_status == FILE_RETRY)
1863 continue;
1864 if (temp_status == FILE_SKIPALL)
1866 ctx->skip_all = TRUE;
1867 return_status = FILE_CONT;
1869 if (temp_status == FILE_SKIP)
1870 return_status = FILE_CONT;
1871 break;
1875 if (!appending)
1877 if (ctx->preserve)
1879 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1881 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1882 if (temp_status == FILE_RETRY)
1883 continue;
1884 if (temp_status == FILE_SKIPALL)
1886 ctx->skip_all = TRUE;
1887 return_status = FILE_CONT;
1889 if (temp_status == FILE_SKIP)
1890 return_status = FILE_CONT;
1891 break;
1894 else if (!dst_exists)
1896 src_mode = umask (-1);
1897 umask (src_mode);
1898 src_mode = 0100666 & ~src_mode;
1899 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
1901 mc_utime (dst_vpath, &utb);
1905 if (return_status == FILE_CONT)
1906 return_status = progress_update_one (tctx, ctx, file_size);
1908 ret_fast:
1909 vfs_path_free (src_vpath);
1910 vfs_path_free (dst_vpath);
1911 return return_status;
1914 /* --------------------------------------------------------------------------------------------- */
1916 * I think these copy_*_* functions should have a return type.
1917 * anyway, this function *must* have two directories as arguments.
1919 /* FIXME: This function needs to check the return values of the
1920 function calls */
1922 FileProgressStatus
1923 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1924 gboolean toplevel, gboolean move_over, gboolean do_delete, struct link * parent_dirs)
1926 struct dirent *next;
1927 struct stat buf, cbuf;
1928 DIR *reading;
1929 char *dest_dir = NULL;
1930 FileProgressStatus return_status = FILE_CONT;
1931 struct utimbuf utb;
1932 struct link *lp;
1933 char *d;
1934 vfs_path_t *src_vpath, *dst_vpath, *dest_dir_vpath = NULL;
1936 d = g_strdup (_d);
1938 src_vpath = vfs_path_from_str (s);
1939 dst_vpath = vfs_path_from_str (_d);
1941 /* First get the mode of the source dir */
1943 retry_src_stat:
1944 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
1946 if (ctx->skip_all)
1947 return_status = FILE_SKIPALL;
1948 else
1950 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1951 if (return_status == FILE_RETRY)
1952 goto retry_src_stat;
1953 if (return_status == FILE_SKIPALL)
1954 ctx->skip_all = TRUE;
1956 goto ret_fast;
1959 if (is_in_linklist (dest_dirs, s, &cbuf))
1961 /* Don't copy a directory we created before (we don't want to copy
1962 infinitely if a directory is copied into itself) */
1963 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1964 return_status = FILE_CONT;
1965 goto ret_fast;
1968 /* Hmm, hardlink to directory??? - Norbert */
1969 /* FIXME: In this step we should do something
1970 in case the destination already exist */
1971 /* Check the hardlinks */
1972 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf))
1974 /* We have made a hardlink - no more processing is necessary */
1975 goto ret_fast;
1978 if (!S_ISDIR (cbuf.st_mode))
1980 if (ctx->skip_all)
1981 return_status = FILE_SKIPALL;
1982 else
1984 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1985 if (return_status == FILE_RETRY)
1986 goto retry_src_stat;
1987 if (return_status == FILE_SKIPALL)
1988 ctx->skip_all = TRUE;
1990 goto ret_fast;
1993 if (is_in_linklist (parent_dirs, s, &cbuf))
1995 /* we found a cyclic symbolic link */
1996 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
1997 return_status = FILE_SKIP;
1998 goto ret_fast;
2001 lp = g_new (struct link, 1);
2002 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
2003 lp->ino = cbuf.st_ino;
2004 lp->dev = cbuf.st_dev;
2005 lp->next = parent_dirs;
2006 parent_dirs = lp;
2008 retry_dst_stat:
2009 /* Now, check if the dest dir exists, if not, create it. */
2010 if (mc_stat (dst_vpath, &buf) != 0)
2012 /* Here the dir doesn't exist : make it ! */
2013 if (move_over)
2015 if (mc_rename (src_vpath, dst_vpath) == 0)
2017 return_status = FILE_CONT;
2018 goto ret;
2021 dest_dir = d;
2022 d = NULL;
2024 else
2027 * If the destination directory exists, we want to copy the whole
2028 * directory, but we only want this to happen once.
2030 * Escape sequences added to the * to compiler warnings.
2031 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2032 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2034 if (!S_ISDIR (buf.st_mode))
2036 if (ctx->skip_all)
2037 return_status = FILE_SKIPALL;
2038 else
2040 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2041 if (return_status == FILE_SKIPALL)
2042 ctx->skip_all = TRUE;
2043 if (return_status == FILE_RETRY)
2044 goto retry_dst_stat;
2046 goto ret;
2048 /* Dive into subdir if exists */
2049 if (toplevel && ctx->dive_into_subdirs)
2051 dest_dir = concat_dir_and_file (d, x_basename (s));
2053 else
2055 dest_dir = d;
2056 d = NULL;
2057 goto dont_mkdir;
2060 dest_dir_vpath = vfs_path_from_str (dest_dir);
2061 while (my_mkdir (dest_dir_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2063 if (ctx->skip_all)
2064 return_status = FILE_SKIPALL;
2065 else
2067 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
2068 if (return_status == FILE_SKIPALL)
2069 ctx->skip_all = TRUE;
2071 if (return_status != FILE_RETRY)
2072 goto ret;
2075 lp = g_new0 (struct link, 1);
2076 mc_stat (dest_dir_vpath, &buf);
2077 lp->vfs = vfs_path_get_by_index (dest_dir_vpath, -1)->class;
2078 lp->ino = buf.st_ino;
2079 lp->dev = buf.st_dev;
2080 lp->next = dest_dirs;
2081 dest_dirs = lp;
2083 if (ctx->preserve_uidgid)
2085 while (mc_chown (dest_dir_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2087 if (ctx->skip_all)
2088 return_status = FILE_SKIPALL;
2089 else
2091 return_status =
2092 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
2093 if (return_status == FILE_SKIPALL)
2094 ctx->skip_all = TRUE;
2096 if (return_status != FILE_RETRY)
2097 goto ret;
2101 dont_mkdir:
2102 /* open the source dir for reading */
2103 reading = mc_opendir (src_vpath);
2104 if (reading == NULL)
2105 goto ret;
2107 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2109 char *path;
2110 vfs_path_t *tmp_vpath;
2112 * Now, we don't want '.' and '..' to be created / copied at any time
2114 if (!strcmp (next->d_name, "."))
2115 continue;
2116 if (!strcmp (next->d_name, ".."))
2117 continue;
2119 /* get the filename and add it to the src directory */
2120 path = concat_dir_and_file (s, next->d_name);
2121 tmp_vpath = vfs_path_from_str (path);
2123 (*ctx->stat_func) (tmp_vpath, &buf);
2124 if (S_ISDIR (buf.st_mode))
2126 char *mdpath;
2128 mdpath = concat_dir_and_file (dest_dir, next->d_name);
2130 * From here, we just intend to recursively copy subdirs, not
2131 * the double functionality of copying different when the target
2132 * dir already exists. So, we give the recursive call the flag 0
2133 * meaning no toplevel.
2135 return_status =
2136 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2137 g_free (mdpath);
2139 else
2141 char *dest_file;
2143 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
2144 return_status = copy_file_file (tctx, ctx, path, dest_file);
2145 g_free (dest_file);
2147 if (do_delete && return_status == FILE_CONT)
2149 if (ctx->erase_at_end)
2151 static struct link *tail;
2152 size_t len = strlen (path);
2153 lp = g_malloc (sizeof (struct link) + len);
2154 strncpy (lp->name, path, len + 1);
2155 lp->st_mode = buf.st_mode;
2156 lp->next = NULL;
2157 if (erase_list != NULL)
2159 tail->next = lp;
2160 tail = lp;
2162 else
2163 erase_list = tail = lp;
2165 else
2167 if (S_ISDIR (buf.st_mode))
2169 return_status = erase_dir_iff_empty (ctx, path);
2171 else
2172 return_status = erase_file (tctx, ctx, path);
2175 g_free (path);
2176 vfs_path_free (tmp_vpath);
2178 mc_closedir (reading);
2180 if (ctx->preserve)
2182 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2183 utb.actime = cbuf.st_atime;
2184 utb.modtime = cbuf.st_mtime;
2185 mc_utime (dest_dir_vpath, &utb);
2187 else
2189 cbuf.st_mode = umask (-1);
2190 umask (cbuf.st_mode);
2191 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2192 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2195 ret:
2196 g_free (dest_dir);
2197 vfs_path_free (dest_dir_vpath);
2198 g_free (parent_dirs);
2199 ret_fast:
2200 g_free (d);
2201 vfs_path_free (src_vpath);
2202 vfs_path_free (dst_vpath);
2203 return return_status;
2206 /* }}} */
2208 /* --------------------------------------------------------------------------------------------- */
2209 /* {{{ Move routines */
2211 FileProgressStatus
2212 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2214 struct stat sbuf, dbuf, destbuf;
2215 struct link *lp;
2216 char *destdir;
2217 FileProgressStatus return_status;
2218 gboolean move_over = FALSE;
2219 gboolean dstat_ok;
2220 vfs_path_t *src_vpath, *dst_vpath, *destdir_vpath;
2222 src_vpath = vfs_path_from_str (s);
2223 dst_vpath = vfs_path_from_str (d);
2225 file_progress_show_source (ctx, s);
2226 file_progress_show_target (ctx, d);
2227 if (check_progress_buttons (ctx) == FILE_ABORT)
2228 return FILE_ABORT;
2230 mc_refresh ();
2232 mc_stat (src_vpath, &sbuf);
2234 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2236 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2237 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2239 if (!dstat_ok)
2240 destdir = g_strdup (d); /* destination doesn't exist */
2241 else if (!ctx->dive_into_subdirs)
2243 destdir = g_strdup (d);
2244 move_over = TRUE;
2246 else
2247 destdir = concat_dir_and_file (d, x_basename (s));
2249 destdir_vpath = vfs_path_from_str (destdir);
2251 /* Check if the user inputted an existing dir */
2252 retry_dst_stat:
2253 if (mc_stat (destdir_vpath, &destbuf) == 0)
2255 if (move_over)
2257 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2259 if (return_status != FILE_CONT)
2260 goto ret;
2261 goto oktoret;
2263 else if (ctx->skip_all)
2264 return_status = FILE_SKIPALL;
2265 else
2267 if (S_ISDIR (destbuf.st_mode))
2268 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2269 else
2270 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2271 if (return_status == FILE_SKIPALL)
2272 ctx->skip_all = TRUE;
2273 if (return_status == FILE_RETRY)
2274 goto retry_dst_stat;
2276 g_free (destdir);
2277 vfs_path_free (destdir_vpath);
2278 vfs_path_free (src_vpath);
2279 vfs_path_free (dst_vpath);
2280 return return_status;
2283 retry_rename:
2284 if (mc_rename (src_vpath, destdir_vpath) == 0)
2286 return_status = FILE_CONT;
2287 goto ret;
2290 if (errno != EXDEV)
2292 if (!ctx->skip_all)
2294 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2295 if (return_status == FILE_SKIPALL)
2296 ctx->skip_all = TRUE;
2297 if (return_status == FILE_RETRY)
2298 goto retry_rename;
2300 goto ret;
2302 /* Failed because of filesystem boundary -> copy dir instead */
2303 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2305 if (return_status != FILE_CONT)
2306 goto ret;
2307 oktoret:
2308 file_progress_show_source (ctx, NULL);
2309 file_progress_show (ctx, 0, 0, "", FALSE);
2311 return_status = check_progress_buttons (ctx);
2312 if (return_status != FILE_CONT)
2313 goto ret;
2315 mc_refresh ();
2316 if (ctx->erase_at_end)
2318 for (; erase_list && return_status != FILE_ABORT;)
2320 if (S_ISDIR (erase_list->st_mode))
2322 return_status = erase_dir_iff_empty (ctx, erase_list->name);
2324 else
2325 return_status = erase_file (tctx, ctx, erase_list->name);
2326 lp = erase_list;
2327 erase_list = erase_list->next;
2328 g_free (lp);
2331 erase_dir_iff_empty (ctx, s);
2333 ret:
2334 g_free (destdir);
2335 vfs_path_free (destdir_vpath);
2336 while (erase_list)
2338 lp = erase_list;
2339 erase_list = erase_list->next;
2340 g_free (lp);
2342 vfs_path_free (src_vpath);
2343 vfs_path_free (dst_vpath);
2344 return return_status;
2347 /* }}} */
2349 /* --------------------------------------------------------------------------------------------- */
2350 /* {{{ Erase routines */
2352 FileProgressStatus
2353 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
2355 FileProgressStatus error;
2357 if (strcmp (s, "..") == 0)
2358 return FILE_SKIP;
2360 if (strcmp (s, ".") == 0)
2361 return FILE_SKIP;
2363 file_progress_show_deleting (ctx, s);
2364 if (check_progress_buttons (ctx) == FILE_ABORT)
2365 return FILE_ABORT;
2366 mc_refresh ();
2368 /* The old way to detect a non empty directory was:
2369 error = my_rmdir (s);
2370 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2371 For the linux user space nfs server (nfs-server-2.2beta29-2)
2372 we would have to check also for EIO. I hope the new way is
2373 fool proof. (Norbert)
2375 error = check_dir_is_empty (s);
2376 if (error == 0)
2377 { /* not empty */
2378 error = query_recursive (ctx, s);
2379 if (error == FILE_CONT)
2380 return recursive_erase (tctx, ctx, s);
2381 else
2382 return error;
2385 while (my_rmdir (s) == -1 && !ctx->skip_all)
2387 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2388 if (error != FILE_RETRY)
2389 return error;
2392 return FILE_CONT;
2395 /* }}} */
2397 /* --------------------------------------------------------------------------------------------- */
2398 /* {{{ Panel operate routines */
2400 ComputeDirSizeUI *
2401 compute_dir_size_create_ui (void)
2403 ComputeDirSizeUI *ui;
2405 const char *b_name = N_("&Abort");
2407 #ifdef ENABLE_NLS
2408 b_name = _(b_name);
2409 #endif
2411 ui = g_new (ComputeDirSizeUI, 1);
2413 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
2414 NULL, _("Directory scanning"), DLG_CENTER);
2415 ui->dirname = label_new (3, 3, "");
2416 add_widget (ui->dlg, ui->dirname);
2418 add_widget (ui->dlg,
2419 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
2420 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2422 /* We will manage the dialog without any help,
2423 that's why we have to call init_dlg */
2424 init_dlg (ui->dlg);
2426 return ui;
2429 /* --------------------------------------------------------------------------------------------- */
2431 void
2432 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2434 if (ui != NULL)
2436 /* schedule to update passive panel */
2437 other_panel->dirty = 1;
2439 /* close and destroy dialog */
2440 dlg_run_done (ui->dlg);
2441 destroy_dlg (ui->dlg);
2442 g_free (ui);
2446 /* --------------------------------------------------------------------------------------------- */
2448 FileProgressStatus
2449 compute_dir_size_update_ui (const void *ui, const vfs_path_t * dirname_vpath)
2451 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2452 int c;
2453 Gpm_Event event;
2454 char *dirname;
2456 if (ui == NULL)
2457 return FILE_CONT;
2459 dirname = vfs_path_to_str (dirname_vpath);
2460 label_set_text (this->dirname, str_trunc (dirname, this->dlg->cols - 6));
2461 g_free (dirname);
2463 event.x = -1; /* Don't show the GPM cursor */
2464 c = tty_get_event (&event, FALSE, FALSE);
2465 if (c == EV_NONE)
2466 return FILE_CONT;
2468 /* Reinitialize to avoid old values after events other than
2469 selecting a button */
2470 this->dlg->ret_value = FILE_CONT;
2472 dlg_process_event (this->dlg, c, &event);
2474 switch (this->dlg->ret_value)
2476 case B_CANCEL:
2477 case FILE_ABORT:
2478 return FILE_ABORT;
2479 default:
2480 return FILE_CONT;
2484 /* --------------------------------------------------------------------------------------------- */
2486 * compute_dir_size:
2488 * Computes the number of bytes used by the files in a directory
2491 FileProgressStatus
2492 compute_dir_size (const vfs_path_t * dirname_vpath, const void *ui,
2493 compute_dir_size_callback cback,
2494 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2496 int res;
2497 struct stat s;
2498 DIR *dir;
2499 struct dirent *dirent;
2500 FileProgressStatus ret = FILE_CONT;
2502 if (!compute_symlinks)
2504 res = mc_lstat (dirname_vpath, &s);
2505 if (res != 0)
2506 return ret;
2508 /* don't scan symlink to directory */
2509 if (S_ISLNK (s.st_mode))
2511 (*ret_marked)++;
2512 *ret_total += (uintmax_t) s.st_size;
2513 return ret;
2517 dir = mc_opendir (dirname_vpath);
2519 if (dir == NULL)
2520 return ret;
2522 while ((dirent = mc_readdir (dir)) != NULL)
2524 vfs_path_t *tmp_vpath;
2526 ret = (cback != NULL) ? cback (ui, dirname_vpath) : FILE_CONT;
2528 if (ret != FILE_CONT)
2529 break;
2531 if (strcmp (dirent->d_name, ".") == 0)
2532 continue;
2533 if (strcmp (dirent->d_name, "..") == 0)
2534 continue;
2536 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, NULL);
2537 res = mc_lstat (tmp_vpath, &s);
2538 if (res == 0)
2540 if (S_ISDIR (s.st_mode))
2542 size_t subdir_count = 0;
2543 uintmax_t subdir_bytes = 0;
2545 ret =
2546 compute_dir_size (tmp_vpath, ui, cback, &subdir_count, &subdir_bytes,
2547 compute_symlinks);
2549 if (ret != FILE_CONT)
2551 vfs_path_free (tmp_vpath);
2552 break;
2555 *ret_marked += subdir_count;
2556 *ret_total += subdir_bytes;
2558 else
2560 (*ret_marked)++;
2561 *ret_total += (uintmax_t) s.st_size;
2564 vfs_path_free (tmp_vpath);
2567 mc_closedir (dir);
2568 return ret;
2571 /* --------------------------------------------------------------------------------------------- */
2573 * panel_operate:
2575 * Performs one of the operations on the selection on the source_panel
2576 * (copy, delete, move).
2578 * Returns TRUE if did change the directory
2579 * structure, Returns FALSE if user aborted
2581 * force_single forces operation on the current entry and affects
2582 * default destination. Current filename is used as default.
2585 gboolean
2586 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2588 WPanel *panel = (WPanel *) source_panel;
2589 const gboolean single_entry = force_single || (panel->marked <= 1)
2590 || (get_current_type () == view_tree);
2592 char *source = NULL;
2593 #ifdef WITH_FULL_PATHS
2594 vfs_path_t *source_with_vpath = NULL;
2595 char *source_with_path_str = NULL;
2596 #else
2597 #define source_with_path source
2598 #endif /* !WITH_FULL_PATHS */
2599 char *dest = NULL;
2600 vfs_path_t *dest_vpath = NULL;
2601 char *temp = NULL;
2602 char *save_cwd = NULL, *save_dest = NULL;
2603 struct stat src_stat;
2604 gboolean ret_val = TRUE;
2605 int i;
2606 FileProgressStatus value;
2607 FileOpContext *ctx;
2608 FileOpTotalContext *tctx;
2609 vfs_path_t *tmp_vpath;
2611 gboolean do_bg = FALSE; /* do background operation? */
2613 static gboolean i18n_flag = FALSE;
2614 if (!i18n_flag)
2616 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2617 op_names[i] = Q_ (op_names[i]);
2618 i18n_flag = TRUE;
2621 free_linklist (&linklist);
2622 free_linklist (&dest_dirs);
2624 if (single_entry)
2626 vfs_path_t *source_vpath;
2628 if (force_single)
2629 source = g_strdup (selection (panel)->fname);
2630 else
2631 source = panel_get_file (panel);
2633 if (strcmp (source, "..") == 0)
2635 g_free (source);
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 *tmp_dest_dir, *dest_dir;
2670 char *format;
2672 /* Forced single operations default to the original name */
2673 if (force_single)
2674 tmp_dest_dir = g_strdup (source);
2675 else if (get_other_type () == view_listing)
2676 tmp_dest_dir = vfs_path_to_str (other_panel->cwd_vpath);
2677 else
2678 tmp_dest_dir = vfs_path_to_str (panel->cwd_vpath);
2680 * Add trailing backslash only when do non-local ops.
2681 * It saves user from occasional file renames (when destination
2682 * dir is deleted)
2684 if (!force_single && tmp_dest_dir[0] != '\0'
2685 && tmp_dest_dir[strlen (tmp_dest_dir) - 1] != PATH_SEP)
2687 /* add trailing separator */
2688 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
2689 g_free (tmp_dest_dir);
2691 else
2693 /* just copy */
2694 dest_dir = tmp_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;
2780 char *cwd_str;
2782 cwd_str = vfs_path_to_str (panel->cwd_vpath);
2783 v = do_background (ctx, g_strconcat (op_names[operation], ": ", cwd_str, (char *) NULL));
2784 g_free (cwd_str);
2785 if (v == -1)
2786 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2788 /* If we are the parent */
2789 if (v == 1)
2791 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
2793 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
2794 vfs_path_free (dest_vpath);
2795 g_free (dest);
2796 /* file_op_context_destroy (ctx); */
2797 return FALSE;
2800 #endif /* ENABLE_BACKGROUND */
2802 /* Initialize things */
2803 /* We do not want to trash cache every time file is
2804 created/touched. However, this will make our cache contain
2805 invalid data. */
2806 if ((dest != NULL) && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2807 save_dest = g_strdup (dest);
2809 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
2810 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2811 save_cwd = vfs_path_to_str (panel->cwd_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_vpath = vfs_path_from_str (source);
2840 else
2841 source_with_vpath = vfs_path_append_new (panel->cwd_vpath, source, (char *) NULL);
2842 source_with_path_str = vfs_path_to_str (source_with_vpath);
2843 #endif /* WITH_FULL_PATHS */
2844 if (panel_operate_init_totals (operation, panel, source_with_path_str, 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_str);
2850 else
2851 value = erase_file (tctx, ctx, source_with_path_str);
2853 else
2855 temp = transform_source (ctx, source_with_path_str);
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 */
2875 ctx->stat_func (source_with_vpath, &src_stat);
2877 if (S_ISDIR (src_stat.st_mode))
2878 value = copy_dir_dir (tctx, ctx, source_with_path_str, dest,
2879 TRUE, FALSE, FALSE, NULL);
2880 else
2881 value = copy_file_file (tctx, ctx, source_with_path_str, dest);
2882 break;
2884 case OP_MOVE:
2885 if (S_ISDIR (src_stat.st_mode))
2886 value = move_dir_dir (tctx, ctx, source_with_path_str, dest);
2887 else
2888 value = move_file_file (tctx, ctx, source_with_path_str, dest);
2889 break;
2891 default:
2892 /* Unknown file operation */
2893 abort ();
2896 } /* Copy or move operation */
2898 if ((value == FILE_CONT) && !force_single)
2899 unmark_files (panel);
2902 else
2904 /* Many files */
2906 /* Check destination for copy or move operation */
2907 while (operation != OP_DELETE)
2909 int dst_result;
2910 struct stat dst_stat;
2912 dst_result = mc_stat (dest_vpath, &dst_stat);
2914 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2915 break;
2917 if (ctx->skip_all
2918 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2919 goto clean_up;
2922 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2924 /* Loop for every file, perform the actual copy operation */
2925 for (i = 0; i < panel->count; i++)
2927 const char *source2;
2929 if (!panel->dir.list[i].f.marked)
2930 continue; /* Skip the unmarked ones */
2932 source2 = panel->dir.list[i].fname;
2933 src_stat = panel->dir.list[i].st;
2935 #ifdef WITH_FULL_PATHS
2936 g_free (source_with_path_str);
2937 vfs_path_free (source_with_vpath);
2938 if (g_path_is_absolute (source2))
2939 source_with_vpath = vfs_path_from_str (source2);
2940 else
2941 source_with_vpath =
2942 vfs_path_append_new (panel->cwd_vpath, source2, (char *) NULL);
2943 source_with_path_str = vfs_path_to_str (source_with_vpath);
2944 #endif /* WITH_FULL_PATHS */
2946 if (operation == OP_DELETE)
2948 if (S_ISDIR (src_stat.st_mode))
2949 value = erase_dir (tctx, ctx, source_with_path_str);
2950 else
2951 value = erase_file (tctx, ctx, source_with_path_str);
2953 else
2955 temp = transform_source (ctx, source_with_path_str);
2957 if (temp == NULL)
2958 value = transform_error;
2959 else
2961 char *temp2, *temp3, *repl_dest;
2963 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2964 temp2 = concat_dir_and_file (repl_dest, temp);
2965 g_free (temp);
2966 g_free (repl_dest);
2967 temp3 = source_with_path_str;
2968 source_with_path_str = strutils_shell_unescape (source_with_path_str);
2969 g_free (temp3);
2970 temp3 = temp2;
2971 temp2 = strutils_shell_unescape (temp2);
2972 g_free (temp3);
2974 switch (operation)
2976 case OP_COPY:
2977 /* we use file_mask_op_follow_links only with OP_COPY */
2979 vfs_path_t *vpath;
2981 vpath = vfs_path_from_str (source_with_path_str);
2982 ctx->stat_func (vpath, &src_stat);
2983 vfs_path_free (vpath);
2985 if (S_ISDIR (src_stat.st_mode))
2986 value = copy_dir_dir (tctx, ctx, source_with_path_str, temp2,
2987 TRUE, FALSE, FALSE, NULL);
2988 else
2989 value = copy_file_file (tctx, ctx, source_with_path_str, temp2);
2990 free_linklist (&dest_dirs);
2991 break;
2993 case OP_MOVE:
2994 if (S_ISDIR (src_stat.st_mode))
2995 value = move_dir_dir (tctx, ctx, source_with_path_str, temp2);
2996 else
2997 value = move_file_file (tctx, ctx, source_with_path_str, temp2);
2998 break;
3000 default:
3001 /* Unknown file operation */
3002 abort ();
3005 g_free (temp2);
3007 } /* Copy or move operation */
3009 if (value == FILE_ABORT)
3010 break;
3012 if (value == FILE_CONT)
3013 do_file_mark (panel, i, 0);
3015 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3017 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3018 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3021 if (operation != OP_DELETE)
3022 file_progress_show (ctx, 0, 0, "", FALSE);
3024 if (check_progress_buttons (ctx) == FILE_ABORT)
3025 break;
3027 mc_refresh ();
3028 } /* Loop for every file */
3030 } /* Many entries */
3032 clean_up:
3033 /* Clean up */
3034 if (save_cwd != NULL)
3036 tmp_vpath = vfs_path_from_str (save_cwd);
3037 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3038 vfs_path_free (tmp_vpath);
3039 g_free (save_cwd);
3042 if (save_dest != NULL)
3044 tmp_vpath = vfs_path_from_str (save_dest);
3045 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3046 vfs_path_free (tmp_vpath);
3047 g_free (save_dest);
3050 free_linklist (&linklist);
3051 free_linklist (&dest_dirs);
3052 #ifdef WITH_FULL_PATHS
3053 g_free (source_with_path_str);
3054 vfs_path_free (source_with_vpath);
3055 #endif /* WITH_FULL_PATHS */
3056 g_free (dest);
3057 vfs_path_free (dest_vpath);
3058 g_free (ctx->dest_mask);
3059 ctx->dest_mask = NULL;
3061 #ifdef ENABLE_BACKGROUND
3062 /* Let our parent know we are saying bye bye */
3063 if (mc_global.we_are_background)
3065 int cur_pid = getpid ();
3066 /* Send pid to parent with child context, it is fork and
3067 don't modify real parent ctx */
3068 ctx->pid = cur_pid;
3069 parent_call ((void *) end_bg_process, ctx, 0);
3071 vfs_shut ();
3072 _exit (0);
3074 #endif /* ENABLE_BACKGROUND */
3076 file_op_total_context_destroy (tctx);
3077 ret_fast:
3078 file_op_context_destroy (ctx);
3079 g_free (source);
3081 return ret_val;
3084 /* }}} */
3086 /* --------------------------------------------------------------------------------------------- */
3087 /* {{{ Query/status report routines */
3088 /** Report error with one file */
3089 FileProgressStatus
3090 file_error (const char *format, const char *file)
3092 char buf[BUF_MEDIUM];
3094 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3096 return do_file_error (buf);
3099 /* --------------------------------------------------------------------------------------------- */
3102 Cause emacs to enter folding mode for this file:
3103 Local variables:
3104 end: