(query_dialog): add horizontal line.
[midnight-commander.git] / src / filemanager / file.c
blob67670d3157040d1056fa0b0460bb75cac50348aa
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 src/filemanager/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 const struct vfs_class *vfs;
115 dev_t dev;
116 ino_t ino;
117 short linkcount;
118 mode_t st_mode;
119 vfs_path_t *src_vpath;
120 vfs_path_t *dst_vpath;
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 GSList *linklist = NULL;
178 /* the files-to-be-erased list */
179 static GSList *erase_list = NULL;
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 GSList *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_link (void *data)
231 struct link *lp = (struct link *) data;
233 vfs_path_free (lp->src_vpath);
234 vfs_path_free (lp->dst_vpath);
235 g_free (lp);
238 /* --------------------------------------------------------------------------------------------- */
240 static void *
241 free_linklist (GSList * lp)
243 g_slist_foreach (lp, (GFunc) free_link, NULL);
244 g_slist_free (lp);
246 return NULL;
249 /* --------------------------------------------------------------------------------------------- */
251 static gboolean
252 is_in_linklist (const GSList * lp, const vfs_path_t * vpath, const struct stat *sb)
254 const struct vfs_class *class;
255 ino_t ino = sb->st_ino;
256 dev_t dev = sb->st_dev;
258 class = vfs_path_get_last_path_vfs (vpath);
260 for (; lp != NULL; lp = g_slist_next (lp))
262 const struct link *lnk = (const struct link *) lp->data;
264 if (lnk->vfs == class && lnk->ino == ino && lnk->dev == dev)
265 return TRUE;
267 return FALSE;
270 /* --------------------------------------------------------------------------------------------- */
272 * Check and made hardlink
274 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
275 * and a hardlink was succesfully made
278 static gboolean
279 check_hardlinks (const vfs_path_t * src_vpath, const vfs_path_t * dst_vpath, struct stat *pstat)
281 GSList *lp;
282 struct link *lnk;
284 const struct vfs_class *my_vfs;
285 ino_t ino = pstat->st_ino;
286 dev_t dev = pstat->st_dev;
287 struct stat link_stat;
289 if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
290 return FALSE;
292 my_vfs = vfs_path_get_by_index (src_vpath, -1)->class;
294 for (lp = linklist; lp != NULL; lp = g_slist_next (lp))
296 lnk = (struct link *) lp->data;
298 if (lnk->vfs == my_vfs && lnk->ino == ino && lnk->dev == dev)
300 const struct vfs_class *lp_name_class;
301 int stat_result;
303 lp_name_class = vfs_path_get_last_path_vfs (lnk->src_vpath);
304 stat_result = mc_stat (lnk->src_vpath, &link_stat);
306 if (stat_result == 0 && link_stat.st_ino == ino
307 && link_stat.st_dev == dev && lp_name_class == my_vfs)
309 const struct vfs_class *p_class, *dst_name_class;
311 dst_name_class = vfs_path_get_last_path_vfs (dst_vpath);
312 p_class = vfs_path_get_last_path_vfs (lnk->dst_vpath);
314 if (dst_name_class == p_class &&
315 mc_stat (lnk->dst_vpath, &link_stat) == 0 &&
316 mc_link (lnk->dst_vpath, dst_vpath) == 0)
317 return TRUE;
320 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
321 return FALSE;
325 lnk = g_new0 (struct link, 1);
326 if (lnk != NULL)
328 lnk->vfs = my_vfs;
329 lnk->ino = ino;
330 lnk->dev = dev;
331 lnk->src_vpath = vfs_path_clone (src_vpath);
332 lnk->dst_vpath = vfs_path_clone (dst_vpath);
333 linklist = g_slist_prepend (linklist, lnk);
336 return FALSE;
339 /* --------------------------------------------------------------------------------------------- */
341 * Duplicate the contents of the symbolic link src_path in dst_path.
342 * Try to make a stable symlink if the option "stable symlink" was
343 * set in the file mask dialog.
344 * If dst_path is an existing symlink it will be deleted silently
345 * (upper levels take already care of existing files at dst_path).
348 static FileProgressStatus
349 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
351 char link_target[MC_MAXPATHLEN];
352 int len;
353 FileProgressStatus return_status;
354 struct stat sb;
355 vfs_path_t *src_vpath;
356 vfs_path_t *dst_vpath;
357 gboolean dst_is_symlink;
358 vfs_path_t *link_target_vpath = NULL;
360 src_vpath = vfs_path_from_str (src_path);
361 dst_vpath = vfs_path_from_str (dst_path);
362 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
364 retry_src_readlink:
365 len = mc_readlink (src_vpath, link_target, MC_MAXPATHLEN - 1);
366 if (len < 0)
368 if (ctx->skip_all)
369 return_status = FILE_SKIPALL;
370 else
372 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
373 if (return_status == FILE_SKIPALL)
374 ctx->skip_all = TRUE;
375 if (return_status == FILE_RETRY)
376 goto retry_src_readlink;
378 goto ret;
380 link_target[len] = 0;
382 if (ctx->stable_symlinks)
385 if (!vfs_file_is_local (src_vpath) || !vfs_file_is_local (dst_vpath))
387 message (D_ERROR, MSG_ERROR,
388 _("Cannot make stable symlinks across"
389 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
390 ctx->stable_symlinks = FALSE;
394 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
396 char *p, *s;
397 vfs_path_t *q;
399 const char *r = strrchr (src_path, PATH_SEP);
401 if (r)
403 p = g_strndup (src_path, r - src_path + 1);
404 if (g_path_is_absolute (dst_path))
405 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
406 else
407 q = vfs_path_build_filename (p, dst_path, (char *) NULL);
409 if (vfs_path_tokens_count (q) > 1)
411 vfs_path_t *tmp_vpath1, *tmp_vpath2;
413 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
414 s = g_strconcat (p, link_target, (char *) NULL);
415 g_free (p);
416 g_strlcpy (link_target, s, sizeof (link_target));
417 g_free (s);
418 tmp_vpath2 = vfs_path_from_str (link_target);
419 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
420 vfs_path_free (tmp_vpath1);
421 vfs_path_free (tmp_vpath2);
422 if (s)
424 g_strlcpy (link_target, s, sizeof (link_target));
425 g_free (s);
428 else
429 g_free (p);
430 vfs_path_free (q);
433 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
435 retry_dst_symlink:
436 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
438 /* Success */
439 return_status = FILE_CONT;
440 goto ret;
443 * if dst_exists, it is obvious that this had failed.
444 * We can delete the old symlink and try again...
446 if (dst_is_symlink)
448 if (mc_unlink (dst_vpath) == 0)
449 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
451 /* Success */
452 return_status = FILE_CONT;
453 goto ret;
456 if (ctx->skip_all)
457 return_status = FILE_SKIPALL;
458 else
460 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
461 if (return_status == FILE_SKIPALL)
462 ctx->skip_all = TRUE;
463 if (return_status == FILE_RETRY)
464 goto retry_dst_symlink;
467 ret:
468 vfs_path_free (src_vpath);
469 vfs_path_free (dst_vpath);
470 vfs_path_free (link_target_vpath);
471 return return_status;
474 /* --------------------------------------------------------------------------------------------- */
476 static FileProgressStatus
477 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add)
479 struct timeval tv_current;
480 static struct timeval tv_start = { };
482 tctx->progress_count++;
483 tctx->progress_bytes += (uintmax_t) add;
485 if (tv_start.tv_sec == 0)
487 gettimeofday (&tv_start, (struct timezone *) NULL);
489 gettimeofday (&tv_current, (struct timezone *) NULL);
490 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
492 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
494 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
495 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
497 tv_start.tv_sec = tv_current.tv_sec;
500 return check_progress_buttons (ctx);
503 /* --------------------------------------------------------------------------------------------- */
505 static FileProgressStatus
506 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
508 char *msg;
509 int result = 0;
510 const char *head_msg;
512 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
514 msg = g_strdup_printf (fmt, a, b);
515 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
516 g_free (msg);
517 do_refresh ();
519 return (result == 1) ? FILE_ABORT : FILE_SKIP;
522 /* --------------------------------------------------------------------------------------------- */
524 static FileProgressStatus
525 warn_same_file (const char *fmt, const char *a, const char *b)
527 #ifdef ENABLE_BACKGROUND
528 /* *INDENT-OFF* */
529 union
531 void *p;
532 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
533 const char *a, const char *b);
534 } pntr;
535 /* *INDENT-ON* */
537 pntr.f = real_warn_same_file;
539 if (mc_global.we_are_background)
540 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
541 #endif
542 return real_warn_same_file (Foreground, fmt, a, b);
545 /* --------------------------------------------------------------------------------------------- */
546 /* {{{ Query/status report routines */
548 static FileProgressStatus
549 real_do_file_error (enum OperationMode mode, const char *error)
551 int result;
552 const char *msg;
554 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
555 result =
556 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
558 switch (result)
560 case 0:
561 do_refresh ();
562 return FILE_SKIP;
564 case 1:
565 do_refresh ();
566 return FILE_SKIPALL;
568 case 2:
569 do_refresh ();
570 return FILE_RETRY;
572 case 3:
573 default:
574 return FILE_ABORT;
578 /* --------------------------------------------------------------------------------------------- */
580 static FileProgressStatus
581 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
583 gchar *text;
585 if (ctx->recursive_result < RECURSIVE_ALWAYS)
587 const char *msg = mode == Foreground
588 ? _("\nDirectory not empty.\nDelete it recursively?")
589 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
590 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
592 if (safe_delete)
593 query_set_sel (1);
595 ctx->recursive_result =
596 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
597 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
599 if (ctx->recursive_result != RECURSIVE_ABORT)
600 do_refresh ();
601 g_free (text);
604 switch (ctx->recursive_result)
606 case RECURSIVE_YES:
607 case RECURSIVE_ALWAYS:
608 return FILE_CONT;
610 case RECURSIVE_NO:
611 case RECURSIVE_NEVER:
612 return FILE_SKIP;
614 case RECURSIVE_ABORT:
615 default:
616 return FILE_ABORT;
620 /* --------------------------------------------------------------------------------------------- */
622 #ifdef ENABLE_BACKGROUND
623 static FileProgressStatus
624 do_file_error (const char *str)
626 union
628 void *p;
629 FileProgressStatus (*f) (enum OperationMode, const char *);
630 } pntr;
631 pntr.f = real_do_file_error;
633 if (mc_global.we_are_background)
634 return parent_call (pntr.p, NULL, 1, strlen (str), str);
635 else
636 return real_do_file_error (Foreground, str);
639 /* --------------------------------------------------------------------------------------------- */
641 static FileProgressStatus
642 query_recursive (FileOpContext * ctx, const char *s)
644 union
646 void *p;
647 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
648 } pntr;
649 pntr.f = real_query_recursive;
651 if (mc_global.we_are_background)
652 return parent_call (pntr.p, ctx, 1, strlen (s), s);
653 else
654 return real_query_recursive (ctx, Foreground, s);
657 /* --------------------------------------------------------------------------------------------- */
659 static FileProgressStatus
660 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
661 struct stat *_d_stat)
663 union
665 void *p;
666 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
667 struct stat *, struct stat *);
668 } pntr;
669 pntr.f = file_progress_real_query_replace;
671 if (mc_global.we_are_background)
672 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
673 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
674 else
675 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
678 #else
679 /* --------------------------------------------------------------------------------------------- */
681 static FileProgressStatus
682 do_file_error (const char *str)
684 return real_do_file_error (Foreground, str);
687 /* --------------------------------------------------------------------------------------------- */
689 static FileProgressStatus
690 query_recursive (FileOpContext * ctx, const char *s)
692 return real_query_recursive (ctx, Foreground, s);
695 /* --------------------------------------------------------------------------------------------- */
697 static FileProgressStatus
698 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
699 struct stat *_d_stat)
701 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
704 #endif /* !ENABLE_BACKGROUND */
706 /* --------------------------------------------------------------------------------------------- */
707 /** Report error with two files */
709 static FileProgressStatus
710 files_error (const char *format, const char *file1, const char *file2)
712 char buf[BUF_MEDIUM];
713 char *nfile1 = g_strdup (path_trunc (file1, 15));
714 char *nfile2 = g_strdup (path_trunc (file2, 15));
716 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
718 g_free (nfile1);
719 g_free (nfile2);
721 return do_file_error (buf);
724 /* }}} */
726 /* --------------------------------------------------------------------------------------------- */
728 static void
729 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
730 struct timeval tv_current, struct timeval tv_transfer_start,
731 off_t file_size, off_t n_read_total)
733 long dt;
735 /* 1. Update rotating dash after some time */
736 rotate_dash ();
738 /* 3. Compute ETA */
739 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
741 if (n_read_total == 0)
742 ctx->eta_secs = 0.0;
743 else
745 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
746 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
749 /* 4. Compute BPS rate */
750 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
751 if (ctx->bps_time < 1)
752 ctx->bps_time = 1;
753 ctx->bps = n_read_total / ctx->bps_time;
755 /* 5. Compute total ETA and BPS */
756 if (ctx->progress_bytes != 0)
758 uintmax_t remain_bytes;
760 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
761 #if 1
763 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
765 if (total_secs < 1)
766 total_secs = 1;
768 tctx->bps = tctx->copied_bytes / total_secs;
769 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
771 #else
772 /* broken on lot of little files */
773 tctx->bps_count++;
774 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
775 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
776 #endif
780 /* --------------------------------------------------------------------------------------------- */
782 /* {{{ Move routines */
783 static FileProgressStatus
784 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
786 struct stat src_stats, dst_stats;
787 FileProgressStatus return_status = FILE_CONT;
788 gboolean copy_done = FALSE;
789 gboolean old_ask_overwrite;
790 vfs_path_t *src_vpath, *dst_vpath;
792 src_vpath = vfs_path_from_str (s);
793 dst_vpath = vfs_path_from_str (d);
795 file_progress_show_source (ctx, src_vpath);
796 file_progress_show_target (ctx, dst_vpath);
798 if (check_progress_buttons (ctx) == FILE_ABORT)
800 return_status = FILE_ABORT;
801 goto ret;
804 mc_refresh ();
806 while (mc_lstat (src_vpath, &src_stats) != 0)
808 /* Source doesn't exist */
809 if (ctx->skip_all)
810 return_status = FILE_SKIPALL;
811 else
813 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
814 if (return_status == FILE_SKIPALL)
815 ctx->skip_all = TRUE;
818 if (return_status != FILE_RETRY)
819 goto ret;
822 if (mc_lstat (dst_vpath, &dst_stats) == 0)
824 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
826 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
827 goto ret;
830 if (S_ISDIR (dst_stats.st_mode))
832 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
833 do_refresh ();
834 return_status = FILE_SKIP;
835 goto ret;
838 if (confirm_overwrite)
840 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
841 if (return_status != FILE_CONT)
842 goto ret;
844 /* Ok to overwrite */
847 if (!ctx->do_append)
849 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
851 return_status = make_symlink (ctx, s, d);
852 if (return_status == FILE_CONT)
853 goto retry_src_remove;
854 goto ret;
857 if (mc_rename (src_vpath, dst_vpath) == 0)
859 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
860 goto ret;
863 #if 0
864 /* Comparison to EXDEV seems not to work in nfs if you're moving from
865 one nfs to the same, but on the server it is on two different
866 filesystems. Then nfs returns EIO instead of EXDEV.
867 Hope it will not hurt if we always in case of error try to copy/delete. */
868 else
869 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
871 if (errno != EXDEV)
873 if (ctx->skip_all)
874 return_status = FILE_SKIPALL;
875 else
877 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
878 if (return_status == FILE_SKIPALL)
879 ctx->skip_all = TRUE;
880 if (return_status == FILE_RETRY)
881 goto retry_rename;
884 goto ret;
886 #endif
888 /* Failed because filesystem boundary -> copy the file instead */
889 old_ask_overwrite = tctx->ask_overwrite;
890 tctx->ask_overwrite = FALSE;
891 return_status = copy_file_file (tctx, ctx, s, d);
892 tctx->ask_overwrite = old_ask_overwrite;
893 if (return_status != FILE_CONT)
894 goto ret;
896 copy_done = TRUE;
898 file_progress_show_source (ctx, NULL);
899 file_progress_show (ctx, 0, 0, "", FALSE);
901 return_status = check_progress_buttons (ctx);
902 if (return_status != FILE_CONT)
903 goto ret;
904 mc_refresh ();
906 retry_src_remove:
907 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
909 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
910 if (return_status == FILE_RETRY)
911 goto retry_src_remove;
912 if (return_status == FILE_SKIPALL)
913 ctx->skip_all = TRUE;
914 goto ret;
917 if (!copy_done)
918 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
920 ret:
921 vfs_path_free (src_vpath);
922 vfs_path_free (dst_vpath);
924 return return_status;
927 /* }}} */
929 /* --------------------------------------------------------------------------------------------- */
930 /* {{{ Erase routines */
931 /** Don't update progress status if progress_count==NULL */
933 static FileProgressStatus
934 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * vpath)
936 int return_status;
937 struct stat buf;
938 char *s;
940 s = vfs_path_to_str (vpath);
941 file_progress_show_deleting (ctx, s);
942 if (check_progress_buttons (ctx) == FILE_ABORT)
944 g_free (s);
945 return FILE_ABORT;
947 mc_refresh ();
949 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
951 /* ignore, most likely the mc_unlink fails, too */
952 buf.st_size = 0;
955 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
957 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
958 if (return_status == FILE_ABORT)
960 g_free (s);
961 return return_status;
963 if (return_status == FILE_RETRY)
964 continue;
965 if (return_status == FILE_SKIPALL)
966 ctx->skip_all = TRUE;
967 break;
969 g_free (s);
970 if (tctx->progress_count == 0)
971 return FILE_CONT;
972 return progress_update_one (tctx, ctx, buf.st_size);
975 /* --------------------------------------------------------------------------------------------- */
978 Recursive remove of files
979 abort->cancel stack
980 skip ->warn every level, gets default
981 skipall->remove as much as possible
983 static FileProgressStatus
984 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
986 struct dirent *next;
987 struct stat buf;
988 DIR *reading;
989 char *path;
990 FileProgressStatus return_status = FILE_CONT;
991 vfs_path_t *vpath;
993 if (strcmp (s, "..") == 0)
994 return FILE_RETRY;
996 vpath = vfs_path_from_str (s);
997 reading = mc_opendir (vpath);
999 if (reading == NULL)
1001 return_status = FILE_RETRY;
1002 goto ret;
1005 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1007 vfs_path_t *tmp_vpath;
1009 if (!strcmp (next->d_name, "."))
1010 continue;
1011 if (!strcmp (next->d_name, ".."))
1012 continue;
1013 path = mc_build_filename (s, next->d_name, NULL);
1014 tmp_vpath = vfs_path_from_str (path);
1015 if (mc_lstat (tmp_vpath, &buf) != 0)
1017 g_free (path);
1018 mc_closedir (reading);
1019 vfs_path_free (tmp_vpath);
1020 return_status = FILE_RETRY;
1021 goto ret;
1023 if (S_ISDIR (buf.st_mode))
1024 return_status = recursive_erase (tctx, ctx, path);
1025 else
1026 return_status = erase_file (tctx, ctx, tmp_vpath);
1027 vfs_path_free (tmp_vpath);
1028 g_free (path);
1030 mc_closedir (reading);
1031 if (return_status == FILE_ABORT)
1032 goto ret;
1034 file_progress_show_deleting (ctx, s);
1035 if (check_progress_buttons (ctx) == FILE_ABORT)
1037 return_status = FILE_ABORT;
1038 goto ret;
1040 mc_refresh ();
1042 while (my_rmdir (s) != 0 && !ctx->skip_all)
1044 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1045 if (return_status == FILE_RETRY)
1046 continue;
1047 if (return_status == FILE_ABORT)
1048 goto ret;
1049 if (return_status == FILE_SKIPALL)
1050 ctx->skip_all = TRUE;
1051 break;
1054 ret:
1055 vfs_path_free (vpath);
1056 return return_status;
1059 /* --------------------------------------------------------------------------------------------- */
1060 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1061 in the directory path points to, 0 else. */
1063 static int
1064 check_dir_is_empty (const vfs_path_t * vpath)
1066 DIR *dir;
1067 struct dirent *d;
1068 int i;
1070 dir = mc_opendir (vpath);
1071 if (!dir)
1072 return -1;
1074 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1076 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1077 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1078 continue; /* "." or ".." */
1079 i = 0;
1080 break;
1083 mc_closedir (dir);
1084 return i;
1087 /* --------------------------------------------------------------------------------------------- */
1089 static FileProgressStatus
1090 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1092 FileProgressStatus error;
1093 vfs_path_t *s_vpath;
1095 if (strcmp (s, "..") == 0)
1096 return FILE_SKIP;
1098 if (strcmp (s, ".") == 0)
1099 return FILE_SKIP;
1101 file_progress_show_deleting (ctx, s);
1102 if (check_progress_buttons (ctx) == FILE_ABORT)
1103 return FILE_ABORT;
1105 mc_refresh ();
1107 s_vpath = vfs_path_from_str (s);
1109 if (check_dir_is_empty (s_vpath) == 1) /* not empty or error */
1111 while (my_rmdir (s) != 0 && !ctx->skip_all)
1113 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1114 if (error == FILE_SKIPALL)
1115 ctx->skip_all = TRUE;
1116 if (error != FILE_RETRY)
1118 vfs_path_free (s_vpath);
1119 return error;
1124 vfs_path_free (s_vpath);
1125 return FILE_CONT;
1128 /* }}} */
1130 /* --------------------------------------------------------------------------------------------- */
1131 /* {{{ Panel operate routines */
1134 * Return currently selected entry name or the name of the first marked
1135 * entry if there is one.
1138 static char *
1139 panel_get_file (WPanel * panel)
1141 if (get_current_type () == view_tree)
1143 WTree *tree;
1145 tree = (WTree *) get_panel_widget (get_current_index ());
1146 return vfs_path_to_str (tree_selected_name (tree));
1149 if (panel->marked != 0)
1151 int i;
1153 for (i = 0; i < panel->count; i++)
1154 if (panel->dir.list[i].f.marked)
1155 return g_strdup (panel->dir.list[i].fname);
1157 return g_strdup (panel->dir.list[panel->selected].fname);
1160 /* --------------------------------------------------------------------------------------------- */
1162 * panel_compute_totals:
1164 * compute the number of files and the number of bytes
1165 * used up by the whole selection, recursing directories
1166 * as required. In addition, it checks to see if it will
1167 * overwrite any files by doing the copy.
1170 static FileProgressStatus
1171 panel_compute_totals (const WPanel * panel, const void *ui,
1172 compute_dir_size_callback cback,
1173 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1175 int i;
1177 *ret_marked = 0;
1178 *ret_total = 0;
1180 for (i = 0; i < panel->count; i++)
1182 struct stat *s;
1184 if (!panel->dir.list[i].f.marked)
1185 continue;
1187 s = &panel->dir.list[i].st;
1189 if (S_ISDIR (s->st_mode))
1191 vfs_path_t *p;
1192 size_t subdir_count = 0;
1193 uintmax_t subdir_bytes = 0;
1194 FileProgressStatus status;
1196 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, NULL);
1197 status =
1198 compute_dir_size (p, ui, cback, &subdir_count, &subdir_bytes, compute_symlinks);
1199 vfs_path_free (p);
1201 if (status != FILE_CONT)
1202 return FILE_ABORT;
1204 *ret_marked += subdir_count;
1205 *ret_total += subdir_bytes;
1207 else
1209 (*ret_marked)++;
1210 *ret_total += (uintmax_t) s->st_size;
1214 return FILE_CONT;
1217 /* --------------------------------------------------------------------------------------------- */
1219 /** Initialize variables for progress bars */
1220 static FileProgressStatus
1221 panel_operate_init_totals (FileOperation operation,
1222 const WPanel * panel, const char *source, FileOpContext * ctx)
1224 FileProgressStatus status;
1226 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1228 ComputeDirSizeUI *ui;
1230 ui = compute_dir_size_create_ui ();
1232 if (source == NULL)
1233 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1234 &ctx->progress_count, &ctx->progress_bytes,
1235 ctx->follow_links);
1236 else
1238 vfs_path_t *p;
1240 p = vfs_path_from_str (source);
1241 status = compute_dir_size (p, ui, compute_dir_size_update_ui,
1242 &ctx->progress_count, &ctx->progress_bytes,
1243 ctx->follow_links);
1244 vfs_path_free (p);
1247 compute_dir_size_destroy_ui (ui);
1249 ctx->progress_totals_computed = (status == FILE_CONT);
1251 else
1253 status = FILE_CONT;
1254 ctx->progress_count = panel->marked;
1255 ctx->progress_bytes = panel->total;
1256 ctx->progress_totals_computed = FALSE;
1259 return status;
1262 /* --------------------------------------------------------------------------------------------- */
1264 * Generate user prompt for panel operation.
1265 * single_source is the name if the source entry or NULL for multiple
1266 * entries.
1267 * src_stat is only used when single_source is not NULL.
1270 static char *
1271 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1272 gboolean single_source, const struct stat *src_stat)
1274 const char *sp, *cp;
1275 char format_string[BUF_MEDIUM];
1276 char *dp = format_string;
1277 gboolean build_question = FALSE;
1279 static gboolean i18n_flag = FALSE;
1280 if (!i18n_flag)
1282 size_t i;
1284 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1285 op_names1[i] = Q_ (op_names1[i]);
1287 #ifdef ENABLE_NLS
1288 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1289 prompt_parts[i] = _(prompt_parts[i]);
1291 one_format = _(one_format);
1292 many_format = _(many_format);
1293 question_format = _(question_format);
1294 #endif /* ENABLE_NLS */
1295 i18n_flag = TRUE;
1298 sp = single_source ? one_format : many_format;
1300 while (*sp != '\0')
1302 switch (*sp)
1304 case '%':
1305 cp = NULL;
1306 switch (sp[1])
1308 case 'o':
1309 cp = op_names1[operation];
1310 break;
1311 case 'm':
1312 if (operation == OP_DELETE)
1314 cp = "";
1315 build_question = TRUE;
1317 else
1318 cp = prompt_parts[5];
1319 break;
1320 case 'e':
1321 if (operation == OP_DELETE)
1323 cp = "";
1324 build_question = TRUE;
1326 else
1327 cp = prompt_parts[6];
1328 break;
1329 case 'f':
1330 if (single_source)
1331 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1332 else
1333 cp = (panel->marked == panel->dirs_marked)
1334 ? prompt_parts[3]
1335 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1336 break;
1337 default:
1338 *dp++ = *sp++;
1341 if (cp != NULL)
1343 sp += 2;
1344 while (*cp != '\0')
1345 *dp++ = *cp++;
1347 break;
1348 default:
1349 *dp++ = *sp++;
1352 *dp = '\0';
1354 if (build_question)
1356 char tmp[BUF_MEDIUM];
1358 memmove (tmp, format_string, sizeof (tmp));
1359 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1362 return g_strdup (format_string);
1365 /* --------------------------------------------------------------------------------------------- */
1367 #ifdef ENABLE_BACKGROUND
1368 static int
1369 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1371 int pid = ctx->pid;
1373 (void) mode;
1374 ctx->pid = 0;
1376 unregister_task_with_pid (pid);
1377 /* file_op_context_destroy(ctx); */
1378 return 1;
1380 #endif
1381 /* }}} */
1383 /* --------------------------------------------------------------------------------------------- */
1384 /*** public functions ****************************************************************************/
1385 /* --------------------------------------------------------------------------------------------- */
1387 FileProgressStatus
1388 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1389 const char *src_path, const char *dst_path)
1391 uid_t src_uid = (uid_t) (-1);
1392 gid_t src_gid = (gid_t) (-1);
1394 int src_desc, dest_desc = -1;
1395 int n_read, n_written;
1396 mode_t src_mode = 0; /* The mode of the source file */
1397 struct stat sb, sb2;
1398 struct utimbuf utb;
1399 gboolean dst_exists = FALSE, appending = FALSE;
1400 off_t file_size = -1;
1401 FileProgressStatus return_status, temp_status;
1402 struct timeval tv_transfer_start;
1403 dest_status_t dst_status = DEST_NONE;
1404 int open_flags;
1405 gboolean is_first_time = TRUE;
1406 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1407 gboolean write_errno_nospace = FALSE;
1409 /* FIXME: We should not be using global variables! */
1410 ctx->do_reget = 0;
1411 return_status = FILE_RETRY;
1413 dst_vpath = vfs_path_from_str (dst_path);
1414 src_vpath = vfs_path_from_str (src_path);
1416 file_progress_show_source (ctx, src_vpath);
1417 file_progress_show_target (ctx, dst_vpath);
1419 if (check_progress_buttons (ctx) == FILE_ABORT)
1421 return_status = FILE_ABORT;
1422 goto ret_fast;
1425 mc_refresh ();
1427 while (mc_stat (dst_vpath, &sb2) == 0)
1429 if (S_ISDIR (sb2.st_mode))
1431 if (ctx->skip_all)
1432 return_status = FILE_SKIPALL;
1433 else
1435 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1436 if (return_status == FILE_SKIPALL)
1437 ctx->skip_all = TRUE;
1438 if (return_status == FILE_RETRY)
1439 continue;
1441 goto ret_fast;
1444 dst_exists = TRUE;
1445 break;
1448 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1450 if (ctx->skip_all)
1451 return_status = FILE_SKIPALL;
1452 else
1454 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1455 if (return_status == FILE_SKIPALL)
1456 ctx->skip_all = TRUE;
1459 if (return_status != FILE_RETRY)
1460 goto ret_fast;
1463 if (dst_exists)
1465 /* Destination already exists */
1466 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1468 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1469 src_path, dst_path);
1470 goto ret_fast;
1473 /* Should we replace destination? */
1474 if (tctx->ask_overwrite)
1476 ctx->do_reget = 0;
1477 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1478 if (return_status != FILE_CONT)
1479 goto ret_fast;
1483 if (!ctx->do_append)
1485 /* Check the hardlinks */
1486 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &sb))
1488 /* We have made a hardlink - no more processing is necessary */
1489 return_status = FILE_CONT;
1490 goto ret_fast;
1493 if (S_ISLNK (sb.st_mode))
1495 return_status = make_symlink (ctx, src_path, dst_path);
1496 goto ret_fast;
1499 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1500 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1502 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1503 && !ctx->skip_all)
1505 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1506 if (return_status == FILE_RETRY)
1507 continue;
1508 if (return_status == FILE_SKIPALL)
1509 ctx->skip_all = TRUE;
1510 goto ret_fast;
1512 /* Success */
1514 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1515 && !ctx->skip_all)
1517 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1518 if (temp_status == FILE_SKIP)
1519 break;
1520 if (temp_status == FILE_SKIPALL)
1521 ctx->skip_all = TRUE;
1522 if (temp_status != FILE_RETRY)
1524 return_status = temp_status;
1525 goto ret_fast;
1529 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1530 && !ctx->skip_all)
1532 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1533 if (temp_status == FILE_SKIP)
1534 break;
1535 if (temp_status == FILE_SKIPALL)
1536 ctx->skip_all = TRUE;
1537 if (temp_status != FILE_RETRY)
1539 return_status = temp_status;
1540 goto ret_fast;
1544 return_status = FILE_CONT;
1545 goto ret_fast;
1549 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1551 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1553 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1554 if (return_status == FILE_RETRY)
1555 continue;
1556 if (return_status == FILE_SKIPALL)
1557 ctx->skip_all = TRUE;
1558 if (return_status == FILE_SKIP)
1559 break;
1560 ctx->do_append = 0;
1561 goto ret_fast;
1564 if (ctx->do_reget != 0)
1566 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1568 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1569 ctx->do_reget = 0;
1570 ctx->do_append = FALSE;
1574 while (mc_fstat (src_desc, &sb) != 0)
1576 if (ctx->skip_all)
1577 return_status = FILE_SKIPALL;
1578 else
1580 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1581 if (return_status == FILE_RETRY)
1582 continue;
1583 if (return_status == FILE_SKIPALL)
1584 ctx->skip_all = TRUE;
1585 ctx->do_append = FALSE;
1587 goto ret;
1590 src_mode = sb.st_mode;
1591 src_uid = sb.st_uid;
1592 src_gid = sb.st_gid;
1593 utb.actime = sb.st_atime;
1594 utb.modtime = sb.st_mtime;
1595 file_size = sb.st_size;
1597 open_flags = O_WRONLY;
1598 if (dst_exists)
1600 if (ctx->do_append != 0)
1601 open_flags |= O_APPEND;
1602 else
1603 open_flags |= O_CREAT | O_TRUNC;
1605 else
1607 open_flags |= O_CREAT | O_EXCL;
1610 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
1612 if (errno != EEXIST)
1614 if (ctx->skip_all)
1615 return_status = FILE_SKIPALL;
1616 else
1618 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1619 if (return_status == FILE_RETRY)
1620 continue;
1621 if (return_status == FILE_SKIPALL)
1622 ctx->skip_all = TRUE;
1623 ctx->do_append = FALSE;
1626 goto ret;
1628 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1630 appending = ctx->do_append;
1631 ctx->do_append = FALSE;
1633 /* Find out the optimal buffer size. */
1634 while (mc_fstat (dest_desc, &sb) != 0)
1636 if (ctx->skip_all)
1637 return_status = FILE_SKIPALL;
1638 else
1640 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1641 if (return_status == FILE_RETRY)
1642 continue;
1643 if (return_status == FILE_SKIPALL)
1644 ctx->skip_all = TRUE;
1646 goto ret;
1649 while (TRUE)
1651 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1652 if (errno == 0)
1653 break;
1655 if (ctx->skip_all)
1656 return_status = FILE_SKIPALL;
1657 else
1659 return_status =
1660 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1661 if (return_status == FILE_RETRY)
1662 continue;
1663 if (return_status == FILE_SKIPALL)
1664 ctx->skip_all = TRUE;
1666 mc_close (dest_desc);
1667 dest_desc = -1;
1668 mc_unlink (dst_vpath);
1669 dst_status = DEST_NONE;
1670 goto ret;
1673 ctx->eta_secs = 0.0;
1674 ctx->bps = 0;
1676 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1677 file_progress_show (ctx, 0, file_size, "", TRUE);
1678 else
1679 file_progress_show (ctx, 1, 1, "", TRUE);
1680 return_status = check_progress_buttons (ctx);
1681 mc_refresh ();
1683 if (return_status != FILE_CONT)
1684 goto ret;
1687 off_t n_read_total = 0;
1688 struct timeval tv_current, tv_last_update, tv_last_input;
1689 int secs, update_secs;
1690 const char *stalled_msg = "";
1692 tv_last_update = tv_transfer_start;
1694 while (TRUE)
1696 char buf[BUF_8K];
1698 /* src_read */
1699 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1700 n_read = -1;
1701 else
1702 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1704 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1705 if (return_status == FILE_RETRY)
1706 continue;
1707 if (return_status == FILE_SKIPALL)
1708 ctx->skip_all = TRUE;
1709 goto ret;
1711 if (n_read == 0)
1712 break;
1714 gettimeofday (&tv_current, NULL);
1716 if (n_read > 0)
1718 char *t = buf;
1719 n_read_total += n_read;
1721 /* Windows NT ftp servers report that files have no
1722 * permissions: -------, so if we happen to have actually
1723 * read something, we should fix the permissions.
1725 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1726 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1727 gettimeofday (&tv_last_input, NULL);
1729 /* dst_write */
1730 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read)
1732 if (n_written > 0)
1734 n_read -= n_written;
1735 t += n_written;
1736 continue;
1739 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
1741 if (ctx->skip_all)
1742 return_status = FILE_SKIPALL;
1743 else
1744 return_status =
1745 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1747 if (return_status == FILE_SKIP)
1749 if (write_errno_nospace)
1750 goto ret;
1751 break;
1753 if (return_status == FILE_SKIPALL)
1755 ctx->skip_all = TRUE;
1756 if (write_errno_nospace)
1757 goto ret;
1759 if (return_status != FILE_RETRY)
1760 goto ret;
1762 /* User pressed "Retry". Will the next mc_write() call be succesful?
1763 * Reset error flag to be ready for that. */
1764 write_errno_nospace = FALSE;
1768 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1770 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1771 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1773 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1775 copy_file_file_display_progress (tctx, ctx,
1776 tv_current,
1777 tv_transfer_start, file_size, n_read_total);
1778 tv_last_update = tv_current;
1780 is_first_time = FALSE;
1782 if (update_secs > FILEOP_STALLING_INTERVAL)
1784 stalled_msg = _("(stalled)");
1788 gboolean force_update;
1790 force_update =
1791 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1793 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1795 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1796 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1799 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1800 force_update);
1802 mc_refresh ();
1804 return_status = check_progress_buttons (ctx);
1806 if (return_status != FILE_CONT)
1808 mc_refresh ();
1809 goto ret;
1814 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1816 ret:
1817 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1819 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1820 if (temp_status == FILE_RETRY)
1821 continue;
1822 if (temp_status == FILE_ABORT)
1823 return_status = temp_status;
1824 if (temp_status == FILE_SKIPALL)
1825 ctx->skip_all = TRUE;
1826 break;
1829 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1831 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1832 if (temp_status == FILE_RETRY)
1833 continue;
1834 if (temp_status == FILE_SKIPALL)
1835 ctx->skip_all = TRUE;
1836 return_status = temp_status;
1837 break;
1840 if (dst_status == DEST_SHORT)
1842 /* Remove short file */
1843 int result = 0;
1845 /* In case of copy/move to full partition, keep source file
1846 * and remove incomplete destination one */
1847 if (!write_errno_nospace)
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, GSList * 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, src_vpath, &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 (src_vpath, dst_vpath, &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, src_vpath, &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_new0 (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 parent_dirs = g_slist_prepend (parent_dirs, lp);
2007 retry_dst_stat:
2008 /* Now, check if the dest dir exists, if not, create it. */
2009 if (mc_stat (dst_vpath, &buf) != 0)
2011 /* Here the dir doesn't exist : make it ! */
2012 if (move_over)
2014 if (mc_rename (src_vpath, dst_vpath) == 0)
2016 return_status = FILE_CONT;
2017 goto ret;
2020 dest_dir = d;
2021 d = NULL;
2023 else
2026 * If the destination directory exists, we want to copy the whole
2027 * directory, but we only want this to happen once.
2029 * Escape sequences added to the * to compiler warnings.
2030 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2031 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2033 if (!S_ISDIR (buf.st_mode))
2035 if (ctx->skip_all)
2036 return_status = FILE_SKIPALL;
2037 else
2039 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2040 if (return_status == FILE_SKIPALL)
2041 ctx->skip_all = TRUE;
2042 if (return_status == FILE_RETRY)
2043 goto retry_dst_stat;
2045 goto ret;
2047 /* Dive into subdir if exists */
2048 if (toplevel && ctx->dive_into_subdirs)
2050 dest_dir = mc_build_filename (d, x_basename (s), NULL);
2052 else
2054 dest_dir = d;
2055 d = NULL;
2056 goto dont_mkdir;
2059 dest_dir_vpath = vfs_path_from_str (dest_dir);
2060 while (my_mkdir (dest_dir_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2062 if (ctx->skip_all)
2063 return_status = FILE_SKIPALL;
2064 else
2066 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
2067 if (return_status == FILE_SKIPALL)
2068 ctx->skip_all = TRUE;
2070 if (return_status != FILE_RETRY)
2071 goto ret;
2074 lp = g_new0 (struct link, 1);
2075 mc_stat (dest_dir_vpath, &buf);
2076 lp->vfs = vfs_path_get_by_index (dest_dir_vpath, -1)->class;
2077 lp->ino = buf.st_ino;
2078 lp->dev = buf.st_dev;
2079 dest_dirs = g_slist_prepend (dest_dirs, lp);
2081 if (ctx->preserve_uidgid)
2083 while (mc_chown (dest_dir_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2085 if (ctx->skip_all)
2086 return_status = FILE_SKIPALL;
2087 else
2089 return_status =
2090 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
2091 if (return_status == FILE_SKIPALL)
2092 ctx->skip_all = TRUE;
2094 if (return_status != FILE_RETRY)
2095 goto ret;
2099 dont_mkdir:
2100 /* open the source dir for reading */
2101 reading = mc_opendir (src_vpath);
2102 if (reading == NULL)
2103 goto ret;
2105 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2107 char *path;
2108 vfs_path_t *tmp_vpath;
2110 * Now, we don't want '.' and '..' to be created / copied at any time
2112 if (!strcmp (next->d_name, "."))
2113 continue;
2114 if (!strcmp (next->d_name, ".."))
2115 continue;
2117 /* get the filename and add it to the src directory */
2118 path = mc_build_filename (s, next->d_name, NULL);
2119 tmp_vpath = vfs_path_from_str (path);
2121 (*ctx->stat_func) (tmp_vpath, &buf);
2122 if (S_ISDIR (buf.st_mode))
2124 char *mdpath;
2126 mdpath = mc_build_filename (dest_dir, next->d_name, NULL);
2128 * From here, we just intend to recursively copy subdirs, not
2129 * the double functionality of copying different when the target
2130 * dir already exists. So, we give the recursive call the flag 0
2131 * meaning no toplevel.
2133 return_status =
2134 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2135 g_free (mdpath);
2137 else
2139 char *dest_file;
2141 dest_file = mc_build_filename (dest_dir, x_basename (path), NULL);
2142 return_status = copy_file_file (tctx, ctx, path, dest_file);
2143 g_free (dest_file);
2145 if (do_delete && return_status == FILE_CONT)
2147 if (ctx->erase_at_end)
2149 lp = g_new0 (struct link, 1);
2150 lp->src_vpath = vfs_path_clone (tmp_vpath);
2151 lp->st_mode = buf.st_mode;
2152 erase_list = g_slist_append (erase_list, lp);
2154 else if (S_ISDIR (buf.st_mode))
2155 return_status = erase_dir_iff_empty (ctx, path);
2156 else
2157 return_status = erase_file (tctx, ctx, tmp_vpath);
2159 g_free (path);
2160 vfs_path_free (tmp_vpath);
2162 mc_closedir (reading);
2164 if (ctx->preserve)
2166 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2167 utb.actime = cbuf.st_atime;
2168 utb.modtime = cbuf.st_mtime;
2169 mc_utime (dest_dir_vpath, &utb);
2171 else
2173 cbuf.st_mode = umask (-1);
2174 umask (cbuf.st_mode);
2175 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2176 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2179 ret:
2180 g_free (dest_dir);
2181 vfs_path_free (dest_dir_vpath);
2182 free_link (parent_dirs->data);
2183 g_slist_free_1 (parent_dirs);
2184 ret_fast:
2185 g_free (d);
2186 vfs_path_free (src_vpath);
2187 vfs_path_free (dst_vpath);
2188 return return_status;
2191 /* }}} */
2193 /* --------------------------------------------------------------------------------------------- */
2194 /* {{{ Move routines */
2196 FileProgressStatus
2197 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2199 struct stat sbuf, dbuf, destbuf;
2200 struct link *lp;
2201 char *destdir;
2202 FileProgressStatus return_status;
2203 gboolean move_over = FALSE;
2204 gboolean dstat_ok;
2205 vfs_path_t *src_vpath, *dst_vpath, *destdir_vpath;
2207 src_vpath = vfs_path_from_str (s);
2208 dst_vpath = vfs_path_from_str (d);
2210 file_progress_show_source (ctx, src_vpath);
2211 file_progress_show_target (ctx, dst_vpath);
2213 if (check_progress_buttons (ctx) == FILE_ABORT)
2215 return_status = FILE_ABORT;
2216 goto ret_fast;
2219 mc_refresh ();
2221 mc_stat (src_vpath, &sbuf);
2223 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2224 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2226 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2227 goto ret_fast;
2230 if (!dstat_ok)
2231 destdir = g_strdup (d); /* destination doesn't exist */
2232 else if (!ctx->dive_into_subdirs)
2234 destdir = g_strdup (d);
2235 move_over = TRUE;
2237 else
2238 destdir = mc_build_filename (d, x_basename (s), NULL);
2240 destdir_vpath = vfs_path_from_str (destdir);
2242 /* Check if the user inputted an existing dir */
2243 retry_dst_stat:
2244 if (mc_stat (destdir_vpath, &destbuf) == 0)
2246 if (move_over)
2248 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2250 if (return_status != FILE_CONT)
2251 goto ret;
2252 goto oktoret;
2254 else if (ctx->skip_all)
2255 return_status = FILE_SKIPALL;
2256 else
2258 if (S_ISDIR (destbuf.st_mode))
2259 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2260 else
2261 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2262 if (return_status == FILE_SKIPALL)
2263 ctx->skip_all = TRUE;
2264 if (return_status == FILE_RETRY)
2265 goto retry_dst_stat;
2268 g_free (destdir);
2269 vfs_path_free (destdir_vpath);
2270 goto ret_fast;
2273 retry_rename:
2274 if (mc_rename (src_vpath, destdir_vpath) == 0)
2276 return_status = FILE_CONT;
2277 goto ret;
2280 if (errno != EXDEV)
2282 if (!ctx->skip_all)
2284 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2285 if (return_status == FILE_SKIPALL)
2286 ctx->skip_all = TRUE;
2287 if (return_status == FILE_RETRY)
2288 goto retry_rename;
2290 goto ret;
2292 /* Failed because of filesystem boundary -> copy dir instead */
2293 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2295 if (return_status != FILE_CONT)
2296 goto ret;
2297 oktoret:
2298 file_progress_show_source (ctx, NULL);
2299 file_progress_show (ctx, 0, 0, "", FALSE);
2301 return_status = check_progress_buttons (ctx);
2302 if (return_status != FILE_CONT)
2303 goto ret;
2305 mc_refresh ();
2306 if (ctx->erase_at_end)
2308 for (; erase_list != NULL && return_status != FILE_ABORT;)
2310 lp = (struct link *) erase_list->data;
2312 if (S_ISDIR (lp->st_mode))
2314 char *src_path;
2316 src_path = vfs_path_to_str (lp->src_vpath);
2317 return_status = erase_dir_iff_empty (ctx, src_path);
2318 g_free (src_path);
2320 else
2321 return_status = erase_file (tctx, ctx, lp->src_vpath);
2323 erase_list = g_slist_remove (erase_list, lp);
2324 free_link (lp);
2327 erase_dir_iff_empty (ctx, s);
2329 ret:
2330 g_free (destdir);
2331 vfs_path_free (destdir_vpath);
2332 erase_list = free_linklist (erase_list);
2333 ret_fast:
2334 vfs_path_free (src_vpath);
2335 vfs_path_free (dst_vpath);
2336 return return_status;
2339 /* }}} */
2341 /* --------------------------------------------------------------------------------------------- */
2342 /* {{{ Erase routines */
2344 FileProgressStatus
2345 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * s_vpath)
2347 FileProgressStatus error;
2348 char *s;
2350 s = vfs_path_to_str (s_vpath);
2353 if (strcmp (s, "..") == 0)
2354 return FILE_SKIP;
2356 if (strcmp (s, ".") == 0)
2357 return FILE_SKIP;
2360 file_progress_show_deleting (ctx, s);
2361 if (check_progress_buttons (ctx) == FILE_ABORT)
2363 g_free (s);
2364 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_vpath);
2376 if (error == 0)
2377 { /* not empty */
2378 error = query_recursive (ctx, s);
2379 if (error == FILE_CONT)
2380 error = recursive_erase (tctx, ctx, s);
2381 g_free (s);
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)
2390 g_free (s);
2391 return error;
2395 g_free (s);
2396 return FILE_CONT;
2399 /* }}} */
2401 /* --------------------------------------------------------------------------------------------- */
2402 /* {{{ Panel operate routines */
2404 ComputeDirSizeUI *
2405 compute_dir_size_create_ui (void)
2407 ComputeDirSizeUI *ui;
2409 const char *b_name = N_("&Abort");
2411 #ifdef ENABLE_NLS
2412 b_name = _(b_name);
2413 #endif
2415 ui = g_new (ComputeDirSizeUI, 1);
2417 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL, NULL,
2418 NULL, _("Directory scanning"), DLG_CENTER);
2419 ui->dirname = label_new (3, 3, "");
2420 add_widget (ui->dlg, ui->dirname);
2422 add_widget (ui->dlg,
2423 button_new (5, (WIDGET (ui->dlg)->cols - strlen (b_name)) / 2,
2424 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2426 /* We will manage the dialog without any help,
2427 that's why we have to call init_dlg */
2428 init_dlg (ui->dlg);
2430 return ui;
2433 /* --------------------------------------------------------------------------------------------- */
2435 void
2436 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2438 if (ui != NULL)
2440 /* schedule to update passive panel */
2441 other_panel->dirty = 1;
2443 /* close and destroy dialog */
2444 dlg_run_done (ui->dlg);
2445 destroy_dlg (ui->dlg);
2446 g_free (ui);
2450 /* --------------------------------------------------------------------------------------------- */
2452 FileProgressStatus
2453 compute_dir_size_update_ui (const void *ui, const vfs_path_t * dirname_vpath)
2455 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2456 int c;
2457 Gpm_Event event;
2458 char *dirname;
2460 if (ui == NULL)
2461 return FILE_CONT;
2463 dirname = vfs_path_to_str (dirname_vpath);
2464 label_set_text (this->dirname, str_trunc (dirname, WIDGET (this->dlg)->cols - 6));
2465 g_free (dirname);
2467 event.x = -1; /* Don't show the GPM cursor */
2468 c = tty_get_event (&event, FALSE, FALSE);
2469 if (c == EV_NONE)
2470 return FILE_CONT;
2472 /* Reinitialize to avoid old values after events other than
2473 selecting a button */
2474 this->dlg->ret_value = FILE_CONT;
2476 dlg_process_event (this->dlg, c, &event);
2478 switch (this->dlg->ret_value)
2480 case B_CANCEL:
2481 case FILE_ABORT:
2482 return FILE_ABORT;
2483 default:
2484 return FILE_CONT;
2488 /* --------------------------------------------------------------------------------------------- */
2490 * compute_dir_size:
2492 * Computes the number of bytes used by the files in a directory
2495 FileProgressStatus
2496 compute_dir_size (const vfs_path_t * dirname_vpath, const void *ui,
2497 compute_dir_size_callback cback,
2498 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2500 int res;
2501 struct stat s;
2502 DIR *dir;
2503 struct dirent *dirent;
2504 FileProgressStatus ret = FILE_CONT;
2506 if (!compute_symlinks)
2508 res = mc_lstat (dirname_vpath, &s);
2509 if (res != 0)
2510 return ret;
2512 /* don't scan symlink to directory */
2513 if (S_ISLNK (s.st_mode))
2515 (*ret_marked)++;
2516 *ret_total += (uintmax_t) s.st_size;
2517 return ret;
2521 dir = mc_opendir (dirname_vpath);
2523 if (dir == NULL)
2524 return ret;
2526 while ((dirent = mc_readdir (dir)) != NULL)
2528 vfs_path_t *tmp_vpath;
2530 ret = (cback != NULL) ? cback (ui, dirname_vpath) : FILE_CONT;
2532 if (ret != FILE_CONT)
2533 break;
2535 if (strcmp (dirent->d_name, ".") == 0)
2536 continue;
2537 if (strcmp (dirent->d_name, "..") == 0)
2538 continue;
2540 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, NULL);
2541 res = mc_lstat (tmp_vpath, &s);
2542 if (res == 0)
2544 if (S_ISDIR (s.st_mode))
2546 size_t subdir_count = 0;
2547 uintmax_t subdir_bytes = 0;
2549 ret =
2550 compute_dir_size (tmp_vpath, ui, cback, &subdir_count, &subdir_bytes,
2551 compute_symlinks);
2553 if (ret != FILE_CONT)
2555 vfs_path_free (tmp_vpath);
2556 break;
2559 *ret_marked += subdir_count;
2560 *ret_total += subdir_bytes;
2562 else
2564 (*ret_marked)++;
2565 *ret_total += (uintmax_t) s.st_size;
2568 vfs_path_free (tmp_vpath);
2571 mc_closedir (dir);
2572 return ret;
2575 /* --------------------------------------------------------------------------------------------- */
2577 * panel_operate:
2579 * Performs one of the operations on the selection on the source_panel
2580 * (copy, delete, move).
2582 * Returns TRUE if did change the directory
2583 * structure, Returns FALSE if user aborted
2585 * force_single forces operation on the current entry and affects
2586 * default destination. Current filename is used as default.
2589 gboolean
2590 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2592 WPanel *panel = (WPanel *) source_panel;
2593 const gboolean single_entry = force_single || (panel->marked <= 1)
2594 || (get_current_type () == view_tree);
2596 char *source = NULL;
2597 #ifdef WITH_FULL_PATHS
2598 vfs_path_t *source_with_vpath = NULL;
2599 char *source_with_path_str = NULL;
2600 #else
2601 #define source_with_path source
2602 #endif /* !WITH_FULL_PATHS */
2603 char *dest = NULL;
2604 vfs_path_t *dest_vpath = NULL;
2605 char *temp = NULL;
2606 char *save_cwd = NULL, *save_dest = NULL;
2607 struct stat src_stat;
2608 gboolean ret_val = TRUE;
2609 int i;
2610 FileProgressStatus value;
2611 FileOpContext *ctx;
2612 FileOpTotalContext *tctx;
2613 vfs_path_t *tmp_vpath;
2615 gboolean do_bg = FALSE; /* do background operation? */
2617 static gboolean i18n_flag = FALSE;
2618 if (!i18n_flag)
2620 for (i = sizeof (op_names) / sizeof (op_names[0]); i--;)
2621 op_names[i] = Q_ (op_names[i]);
2622 i18n_flag = TRUE;
2625 linklist = free_linklist (linklist);
2626 dest_dirs = free_linklist (dest_dirs);
2628 if (single_entry)
2630 vfs_path_t *source_vpath;
2632 if (force_single)
2633 source = g_strdup (selection (panel)->fname);
2634 else
2635 source = panel_get_file (panel);
2637 if (strcmp (source, "..") == 0)
2639 g_free (source);
2640 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2641 return FALSE;
2644 source_vpath = vfs_path_from_str (source);
2645 /* Update stat to get actual info */
2646 if (mc_lstat (source_vpath, &src_stat) != 0)
2648 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2649 path_trunc (source, 30), unix_error_string (errno));
2651 /* Directory was changed outside MC. Reload it forced */
2652 if (!panel->is_panelized)
2654 panel_update_flags_t flags = UP_RELOAD;
2656 /* don't update panelized panel */
2657 if (get_other_type () == view_listing && other_panel->is_panelized)
2658 flags |= UP_ONLY_CURRENT;
2660 update_panels (flags, UP_KEEPSEL);
2662 vfs_path_free (source_vpath);
2663 return FALSE;
2665 vfs_path_free (source_vpath);
2668 ctx = file_op_context_new (operation);
2670 /* Show confirmation dialog */
2671 if (operation != OP_DELETE)
2673 char *tmp_dest_dir, *dest_dir;
2674 char *format;
2676 /* Forced single operations default to the original name */
2677 if (force_single)
2678 tmp_dest_dir = g_strdup (source);
2679 else if (get_other_type () == view_listing)
2680 tmp_dest_dir = vfs_path_to_str (other_panel->cwd_vpath);
2681 else
2682 tmp_dest_dir = vfs_path_to_str (panel->cwd_vpath);
2684 * Add trailing backslash only when do non-local ops.
2685 * It saves user from occasional file renames (when destination
2686 * dir is deleted)
2688 if (!force_single && tmp_dest_dir[0] != '\0'
2689 && tmp_dest_dir[strlen (tmp_dest_dir) - 1] != PATH_SEP)
2691 /* add trailing separator */
2692 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
2693 g_free (tmp_dest_dir);
2695 else
2697 /* just copy */
2698 dest_dir = tmp_dest_dir;
2700 if (dest_dir == NULL)
2702 ret_val = FALSE;
2703 goto ret_fast;
2706 /* Generate confirmation prompt */
2707 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2709 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2710 source != NULL ? (void *) source
2711 : (void *) &panel->marked, dest_dir, &do_bg);
2713 g_free (format);
2714 g_free (dest_dir);
2716 if (dest == NULL || dest[0] == '\0')
2718 g_free (dest);
2719 ret_val = FALSE;
2720 goto ret_fast;
2722 dest_vpath = vfs_path_from_str (dest);
2724 else if (confirm_delete)
2726 char *format;
2727 char fmd_buf[BUF_MEDIUM];
2729 /* Generate confirmation prompt */
2730 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2732 if (source == NULL)
2733 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2734 else
2736 const int fmd_xlen = 64;
2737 i = fmd_xlen - str_term_width1 (format) - 4;
2738 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2741 g_free (format);
2743 if (safe_delete)
2744 query_set_sel (1);
2746 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2748 if (i != 0)
2750 ret_val = FALSE;
2751 goto ret_fast;
2755 tctx = file_op_total_context_new ();
2756 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2759 filegui_dialog_type_t dialog_type;
2761 if (operation == OP_DELETE)
2762 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2763 else
2765 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2766 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2768 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2769 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2772 /* Background also need ctx->ui, but not full */
2773 if (do_bg)
2774 file_op_context_create_ui_without_init (ctx, TRUE, dialog_type);
2775 else
2776 file_op_context_create_ui (ctx, TRUE, dialog_type);
2779 #ifdef ENABLE_BACKGROUND
2780 /* Did the user select to do a background operation? */
2781 if (do_bg)
2783 int v;
2784 char *cwd_str;
2786 cwd_str = vfs_path_to_str (panel->cwd_vpath);
2787 v = do_background (ctx, g_strconcat (op_names[operation], ": ", cwd_str, (char *) NULL));
2788 g_free (cwd_str);
2789 if (v == -1)
2790 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2792 /* If we are the parent */
2793 if (v == 1)
2795 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
2797 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
2798 vfs_path_free (dest_vpath);
2799 g_free (dest);
2800 /* file_op_context_destroy (ctx); */
2801 return FALSE;
2804 #endif /* ENABLE_BACKGROUND */
2806 /* Initialize things */
2807 /* We do not want to trash cache every time file is
2808 created/touched. However, this will make our cache contain
2809 invalid data. */
2810 if ((dest != NULL) && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2811 save_dest = g_strdup (dest);
2813 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
2814 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2815 save_cwd = vfs_path_to_str (panel->cwd_vpath);
2817 /* Now, let's do the job */
2819 /* This code is only called by the tree and panel code */
2820 if (single_entry)
2822 /* We now have ETA in all cases */
2824 /* One file: FIXME mc_chdir will take user out of any vfs */
2825 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2827 vfs_path_t *vpath;
2828 int chdir_retcode;
2830 vpath = vfs_path_from_str (PATH_SEP_STR);
2831 chdir_retcode = mc_chdir (vpath);
2832 vfs_path_free (vpath);
2833 if (chdir_retcode < 0)
2835 ret_val = FALSE;
2836 goto clean_up;
2840 /* The source and src_stat variables have been initialized before */
2841 #ifdef WITH_FULL_PATHS
2842 if (g_path_is_absolute (source))
2843 source_with_vpath = vfs_path_from_str (source);
2844 else
2845 source_with_vpath = vfs_path_append_new (panel->cwd_vpath, source, (char *) NULL);
2846 source_with_path_str = vfs_path_to_str (source_with_vpath);
2847 #endif /* WITH_FULL_PATHS */
2848 if (panel_operate_init_totals (operation, panel, source_with_path_str, ctx) == FILE_CONT)
2850 if (operation == OP_DELETE)
2852 if (S_ISDIR (src_stat.st_mode))
2853 value = erase_dir (tctx, ctx, source_with_vpath);
2854 else
2855 value = erase_file (tctx, ctx, source_with_vpath);
2857 else
2859 temp = transform_source (ctx, source_with_path_str);
2860 if (temp == NULL)
2861 value = transform_error;
2862 else
2864 char *repl_dest, *temp2;
2866 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2867 temp2 = mc_build_filename (repl_dest, temp, NULL);
2868 g_free (temp);
2869 g_free (repl_dest);
2870 g_free (dest);
2871 vfs_path_free (dest_vpath);
2872 dest = temp2;
2873 dest_vpath = vfs_path_from_str (dest);
2875 switch (operation)
2877 case OP_COPY:
2878 /* we use file_mask_op_follow_links only with OP_COPY */
2879 ctx->stat_func (source_with_vpath, &src_stat);
2881 if (S_ISDIR (src_stat.st_mode))
2882 value = copy_dir_dir (tctx, ctx, source_with_path_str, dest,
2883 TRUE, FALSE, FALSE, NULL);
2884 else
2885 value = copy_file_file (tctx, ctx, source_with_path_str, dest);
2886 break;
2888 case OP_MOVE:
2889 if (S_ISDIR (src_stat.st_mode))
2890 value = move_dir_dir (tctx, ctx, source_with_path_str, dest);
2891 else
2892 value = move_file_file (tctx, ctx, source_with_path_str, dest);
2893 break;
2895 default:
2896 /* Unknown file operation */
2897 abort ();
2900 } /* Copy or move operation */
2902 if ((value == FILE_CONT) && !force_single)
2903 unmark_files (panel);
2906 else
2908 /* Many files */
2910 /* Check destination for copy or move operation */
2911 while (operation != OP_DELETE)
2913 int dst_result;
2914 struct stat dst_stat;
2916 dst_result = mc_stat (dest_vpath, &dst_stat);
2918 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2919 break;
2921 if (ctx->skip_all
2922 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2923 goto clean_up;
2926 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2928 /* Loop for every file, perform the actual copy operation */
2929 for (i = 0; i < panel->count; i++)
2931 const char *source2;
2933 if (!panel->dir.list[i].f.marked)
2934 continue; /* Skip the unmarked ones */
2936 source2 = panel->dir.list[i].fname;
2937 src_stat = panel->dir.list[i].st;
2939 #ifdef WITH_FULL_PATHS
2940 g_free (source_with_path_str);
2941 vfs_path_free (source_with_vpath);
2942 if (g_path_is_absolute (source2))
2943 source_with_vpath = vfs_path_from_str (source2);
2944 else
2945 source_with_vpath =
2946 vfs_path_append_new (panel->cwd_vpath, source2, (char *) NULL);
2947 source_with_path_str = vfs_path_to_str (source_with_vpath);
2948 #endif /* WITH_FULL_PATHS */
2950 if (operation == OP_DELETE)
2952 if (S_ISDIR (src_stat.st_mode))
2953 value = erase_dir (tctx, ctx, source_with_vpath);
2954 else
2955 value = erase_file (tctx, ctx, source_with_vpath);
2957 else
2959 temp = transform_source (ctx, source_with_path_str);
2961 if (temp == NULL)
2962 value = transform_error;
2963 else
2965 char *temp2, *temp3, *repl_dest;
2967 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2968 temp2 = mc_build_filename (repl_dest, temp, NULL);
2969 g_free (temp);
2970 g_free (repl_dest);
2971 temp3 = source_with_path_str;
2972 source_with_path_str = strutils_shell_unescape (source_with_path_str);
2973 g_free (temp3);
2974 temp3 = temp2;
2975 temp2 = strutils_shell_unescape (temp2);
2976 g_free (temp3);
2978 switch (operation)
2980 case OP_COPY:
2981 /* we use file_mask_op_follow_links only with OP_COPY */
2983 vfs_path_t *vpath;
2985 vpath = vfs_path_from_str (source_with_path_str);
2986 ctx->stat_func (vpath, &src_stat);
2987 vfs_path_free (vpath);
2989 if (S_ISDIR (src_stat.st_mode))
2990 value = copy_dir_dir (tctx, ctx, source_with_path_str, temp2,
2991 TRUE, FALSE, FALSE, NULL);
2992 else
2993 value = copy_file_file (tctx, ctx, source_with_path_str, temp2);
2994 dest_dirs = free_linklist (dest_dirs);
2995 break;
2997 case OP_MOVE:
2998 if (S_ISDIR (src_stat.st_mode))
2999 value = move_dir_dir (tctx, ctx, source_with_path_str, temp2);
3000 else
3001 value = move_file_file (tctx, ctx, source_with_path_str, temp2);
3002 break;
3004 default:
3005 /* Unknown file operation */
3006 abort ();
3009 g_free (temp2);
3011 } /* Copy or move operation */
3013 if (value == FILE_ABORT)
3014 break;
3016 if (value == FILE_CONT)
3017 do_file_mark (panel, i, 0);
3019 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3021 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3022 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3025 if (operation != OP_DELETE)
3026 file_progress_show (ctx, 0, 0, "", FALSE);
3028 if (check_progress_buttons (ctx) == FILE_ABORT)
3029 break;
3031 mc_refresh ();
3032 } /* Loop for every file */
3034 } /* Many entries */
3036 clean_up:
3037 /* Clean up */
3038 if (save_cwd != NULL)
3040 tmp_vpath = vfs_path_from_str (save_cwd);
3041 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3042 vfs_path_free (tmp_vpath);
3043 g_free (save_cwd);
3046 if (save_dest != NULL)
3048 tmp_vpath = vfs_path_from_str (save_dest);
3049 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3050 vfs_path_free (tmp_vpath);
3051 g_free (save_dest);
3054 linklist = free_linklist (linklist);
3055 dest_dirs = free_linklist (dest_dirs);
3056 #ifdef WITH_FULL_PATHS
3057 g_free (source_with_path_str);
3058 vfs_path_free (source_with_vpath);
3059 #endif /* WITH_FULL_PATHS */
3060 g_free (dest);
3061 vfs_path_free (dest_vpath);
3062 g_free (ctx->dest_mask);
3063 ctx->dest_mask = NULL;
3065 #ifdef ENABLE_BACKGROUND
3066 /* Let our parent know we are saying bye bye */
3067 if (mc_global.we_are_background)
3069 int cur_pid = getpid ();
3070 /* Send pid to parent with child context, it is fork and
3071 don't modify real parent ctx */
3072 ctx->pid = cur_pid;
3073 parent_call ((void *) end_bg_process, ctx, 0);
3075 vfs_shut ();
3076 _exit (0);
3078 #endif /* ENABLE_BACKGROUND */
3080 file_op_total_context_destroy (tctx);
3081 ret_fast:
3082 file_op_context_destroy (ctx);
3083 g_free (source);
3085 return ret_val;
3088 /* }}} */
3090 /* --------------------------------------------------------------------------------------------- */
3091 /* {{{ Query/status report routines */
3092 /** Report error with one file */
3093 FileProgressStatus
3094 file_error (const char *format, const char *file)
3096 char buf[BUF_MEDIUM];
3098 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3100 return do_file_error (buf);
3103 /* --------------------------------------------------------------------------------------------- */
3106 Cause emacs to enter folding mode for this file:
3107 Local variables:
3108 end: