Reimplemented list of operated files using GSList.
[midnight-commander.git] / src / filemanager / file.c
blob65182cffcba6fc7843afa480370ee50afd126fa5
1 /*
2 File management.
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Janne Kukonlehto, 1994, 1995
10 Fred Leeflang, 1994, 1995
11 Miguel de Icaza, 1994, 1995, 1996
12 Jakub Jelinek, 1995, 1996
13 Norbert Warmuth, 1997
14 Pavel Machek, 1998
16 The copy code was based in GNU's cp, and was written by:
17 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
19 The move code was based in GNU's mv, and was written by:
20 Mike Parker and David MacKenzie.
22 Janne Kukonlehto added much error recovery to them for being used
23 in an interactive program.
25 This file is part of the Midnight Commander.
27 The Midnight Commander is free software: you can redistribute it
28 and/or modify it under the terms of the GNU General Public License as
29 published by the Free Software Foundation, either version 3 of the License,
30 or (at your option) any later version.
32 The Midnight Commander is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program. If not, see <http://www.gnu.org/licenses/>.
42 * Please note that all dialogs used here must be safe for background
43 * operations.
46 /** \file file.c
47 * \brief Source: file management
50 /* {{{ Include files */
52 #include <config.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
62 #include <fcntl.h>
64 #include "lib/global.h"
65 #include "lib/tty/tty.h"
66 #include "lib/tty/key.h"
67 #include "lib/search.h"
68 #include "lib/strescape.h"
69 #include "lib/strutil.h"
70 #include "lib/util.h"
71 #include "lib/vfs/vfs.h"
72 #include "lib/widget.h"
74 #include "src/setup.h"
75 #include "src/background.h"
77 #include "layout.h" /* rotate_dash() */
79 /* Needed for current_panel, other_panel and WTree */
80 #include "dir.h"
81 #include "filegui.h"
82 #include "filenot.h"
83 #include "tree.h"
84 #include "midnight.h" /* current_panel */
86 #include "file.h"
88 /* }}} */
90 /*** global variables ****************************************************************************/
92 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
93 const char *op_names[3] = {
94 N_("DialogTitle|Copy"),
95 N_("DialogTitle|Move"),
96 N_("DialogTitle|Delete")
99 /*** file scope macro definitions ****************************************************************/
101 /* Hack: the vfs code should not rely on this */
102 #define WITH_FULL_PATHS 1
104 #define FILEOP_UPDATE_INTERVAL 2
105 #define FILEOP_STALLING_INTERVAL 4
107 /*** file scope type declarations ****************************************************************/
109 /* This is a hard link cache */
110 struct link
112 const struct vfs_class *vfs;
113 dev_t dev;
114 ino_t ino;
115 short linkcount;
116 mode_t st_mode;
117 vfs_path_t *src_vpath;
118 vfs_path_t *dst_vpath;
121 /* Status of the destination file */
122 typedef enum
124 DEST_NONE = 0, /* Not created */
125 DEST_SHORT = 1, /* Created, not fully copied */
126 DEST_FULL = 2 /* Created, fully copied */
127 } dest_status_t;
130 * This array introduced to avoid translation problems. The former (op_names)
131 * is assumed to be nouns, suitable in dialog box titles; this one should
132 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
133 * (I don't use spaces around the words, because someday they could be
134 * dropped, when widgets get smarter)
137 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
138 static const char *op_names1[] = {
139 N_("FileOperation|Copy"),
140 N_("FileOperation|Move"),
141 N_("FileOperation|Delete")
145 * These are formats for building a prompt. Parts encoded as follows:
146 * %o - operation from op_names1
147 * %f - file/files or files/directories, as appropriate
148 * %m - "with source mask" or question mark for delete
149 * %s - source name (truncated)
150 * %d - number of marked files
151 * %e - "to:" or question mark for delete
153 * xgettext:no-c-format */
154 static const char *one_format = N_("%o %f \"%s\"%m");
155 /* xgettext:no-c-format */
156 static const char *many_format = N_("%o %d %f%m");
158 static const char *prompt_parts[] = {
159 N_("file"),
160 N_("files"),
161 N_("directory"),
162 N_("directories"),
163 N_("files/directories"),
164 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
165 N_(" with source mask:"),
166 N_("to:")
169 static const char *question_format = N_("%s?");
171 /*** file scope variables ************************************************************************/
173 /* the hard link cache */
174 static GSList *linklist = NULL;
176 /* the files-to-be-erased list */
177 static GSList *erase_list = NULL;
180 * In copy_dir_dir we use two additional single linked lists: The first -
181 * variable name `parent_dirs' - holds information about already copied
182 * directories and is used to detect cyclic symbolic links.
183 * The second (`dest_dirs' below) holds information about just created
184 * target directories and is used to detect when an directory is copied
185 * into itself (we don't want to copy infinitly).
186 * Both lists don't use the linkcount and name structure members of struct
187 * link.
189 static GSList *dest_dirs = NULL;
191 static FileProgressStatus transform_error = FILE_CONT;
193 /*** file scope functions ************************************************************************/
194 /* --------------------------------------------------------------------------------------------- */
196 static char *
197 transform_source (FileOpContext * ctx, const char *source)
199 char *s, *q;
200 char *fnsource;
202 s = g_strdup (source);
204 /* We remove \n from the filename since regex routines would use \n as an anchor */
205 /* this is just to be allowed to maniupulate file names with \n on it */
206 for (q = s; *q != '\0'; q++)
207 if (*q == '\n')
208 *q = ' ';
210 fnsource = (char *) x_basename (s);
212 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
213 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
214 else
216 q = NULL;
217 transform_error = FILE_SKIP;
220 g_free (s);
221 return q;
224 /* --------------------------------------------------------------------------------------------- */
226 static void
227 free_link (void *data)
229 struct link *lp = (struct link *) data;
231 vfs_path_free (lp->src_vpath);
232 vfs_path_free (lp->dst_vpath);
233 g_free (lp);
236 /* --------------------------------------------------------------------------------------------- */
238 static void *
239 free_linklist (GSList *lp)
241 g_slist_foreach (lp, (GFunc) free_link, NULL);
242 g_slist_free (lp);
244 return NULL;
247 /* --------------------------------------------------------------------------------------------- */
249 static gboolean
250 is_in_linklist (const GSList *lp, const vfs_path_t * vpath, const struct stat *sb)
252 const struct vfs_class *class;
253 ino_t ino = sb->st_ino;
254 dev_t dev = sb->st_dev;
256 class = vfs_path_get_last_path_vfs (vpath);
258 for (; lp != NULL; lp = g_slist_next (lp))
260 const struct link *lnk = (const struct link *) lp->data;
262 if (lnk->vfs == class && lnk->ino == ino && lnk->dev == dev)
263 return TRUE;
265 return FALSE;
268 /* --------------------------------------------------------------------------------------------- */
270 * Check and made hardlink
272 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
273 * and a hardlink was succesfully made
276 static gboolean
277 check_hardlinks (const vfs_path_t * src_vpath, const vfs_path_t * dst_vpath, struct stat *pstat)
279 GSList *lp;
280 struct link *lnk;
282 const struct vfs_class *my_vfs;
283 ino_t ino = pstat->st_ino;
284 dev_t dev = pstat->st_dev;
285 struct stat link_stat;
287 if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
288 return FALSE;
290 my_vfs = vfs_path_get_by_index (src_vpath, -1)->class;
292 for (lp = linklist; lp != NULL; lp = g_slist_next (lp))
294 lnk = (struct link *) lp->data;
296 if (lnk->vfs == my_vfs && lnk->ino == ino && lnk->dev == dev)
298 const struct vfs_class *lp_name_class;
299 int stat_result;
301 lp_name_class = vfs_path_get_last_path_vfs (lnk->src_vpath);
302 stat_result = mc_stat (lnk->src_vpath, &link_stat);
304 if (stat_result == 0 && link_stat.st_ino == ino
305 && link_stat.st_dev == dev && lp_name_class == my_vfs)
307 const struct vfs_class *p_class, *dst_name_class;
309 dst_name_class = vfs_path_get_last_path_vfs (dst_vpath);
310 p_class = vfs_path_get_last_path_vfs (lnk->dst_vpath);
312 if (dst_name_class == p_class &&
313 mc_stat (lnk->dst_vpath, &link_stat) == 0 &&
314 mc_link (lnk->dst_vpath, dst_vpath) == 0)
315 return TRUE;
318 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
319 return FALSE;
323 lnk = g_new0 (struct link, 1);
324 if (lnk != NULL)
326 lnk->vfs = my_vfs;
327 lnk->ino = ino;
328 lnk->dev = dev;
329 lnk->src_vpath = vfs_path_clone (src_vpath);
330 lnk->dst_vpath = vfs_path_clone (dst_vpath);
331 linklist = g_slist_prepend (linklist, lnk);
334 return FALSE;
337 /* --------------------------------------------------------------------------------------------- */
339 * Duplicate the contents of the symbolic link src_path in dst_path.
340 * Try to make a stable symlink if the option "stable symlink" was
341 * set in the file mask dialog.
342 * If dst_path is an existing symlink it will be deleted silently
343 * (upper levels take already care of existing files at dst_path).
346 static FileProgressStatus
347 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
349 char link_target[MC_MAXPATHLEN];
350 int len;
351 FileProgressStatus return_status;
352 struct stat sb;
353 vfs_path_t *src_vpath;
354 vfs_path_t *dst_vpath;
355 gboolean dst_is_symlink;
356 vfs_path_t *link_target_vpath = NULL;
358 src_vpath = vfs_path_from_str (src_path);
359 dst_vpath = vfs_path_from_str (dst_path);
360 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
362 retry_src_readlink:
363 len = mc_readlink (src_vpath, link_target, MC_MAXPATHLEN - 1);
364 if (len < 0)
366 if (ctx->skip_all)
367 return_status = FILE_SKIPALL;
368 else
370 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
371 if (return_status == FILE_SKIPALL)
372 ctx->skip_all = TRUE;
373 if (return_status == FILE_RETRY)
374 goto retry_src_readlink;
376 goto ret;
378 link_target[len] = 0;
380 if (ctx->stable_symlinks)
383 if (!vfs_file_is_local (src_vpath) || !vfs_file_is_local (dst_vpath))
385 message (D_ERROR, MSG_ERROR,
386 _("Cannot make stable symlinks across"
387 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
388 ctx->stable_symlinks = FALSE;
392 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
394 char *p, *s;
395 vfs_path_t *q;
397 const char *r = strrchr (src_path, PATH_SEP);
399 if (r)
401 p = g_strndup (src_path, r - src_path + 1);
402 if (g_path_is_absolute (dst_path))
403 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
404 else
405 q = vfs_path_build_filename (p, dst_path, (char *) NULL);
407 if (vfs_path_tokens_count (q) > 1)
409 vfs_path_t *tmp_vpath1, *tmp_vpath2;
411 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
412 s = g_strconcat (p, link_target, (char *) NULL);
413 g_free (p);
414 g_strlcpy (link_target, s, sizeof (link_target));
415 g_free (s);
416 tmp_vpath2 = vfs_path_from_str (link_target);
417 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
418 vfs_path_free (tmp_vpath1);
419 vfs_path_free (tmp_vpath2);
420 if (s)
422 g_strlcpy (link_target, s, sizeof (link_target));
423 g_free (s);
426 else
427 g_free (p);
428 vfs_path_free (q);
431 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
433 retry_dst_symlink:
434 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
436 /* Success */
437 return_status = FILE_CONT;
438 goto ret;
441 * if dst_exists, it is obvious that this had failed.
442 * We can delete the old symlink and try again...
444 if (dst_is_symlink)
446 if (mc_unlink (dst_vpath) == 0)
447 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
449 /* Success */
450 return_status = FILE_CONT;
451 goto ret;
454 if (ctx->skip_all)
455 return_status = FILE_SKIPALL;
456 else
458 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
459 if (return_status == FILE_SKIPALL)
460 ctx->skip_all = TRUE;
461 if (return_status == FILE_RETRY)
462 goto retry_dst_symlink;
465 ret:
466 vfs_path_free (src_vpath);
467 vfs_path_free (dst_vpath);
468 vfs_path_free (link_target_vpath);
469 return return_status;
472 /* --------------------------------------------------------------------------------------------- */
474 static FileProgressStatus
475 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add)
477 struct timeval tv_current;
478 static struct timeval tv_start = { };
480 tctx->progress_count++;
481 tctx->progress_bytes += (uintmax_t) add;
483 if (tv_start.tv_sec == 0)
485 gettimeofday (&tv_start, (struct timezone *) NULL);
487 gettimeofday (&tv_current, (struct timezone *) NULL);
488 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
490 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
492 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
493 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
495 tv_start.tv_sec = tv_current.tv_sec;
498 return check_progress_buttons (ctx);
501 /* --------------------------------------------------------------------------------------------- */
503 static FileProgressStatus
504 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
506 char *msg;
507 int result = 0;
508 const char *head_msg;
510 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
512 msg = g_strdup_printf (fmt, a, b);
513 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
514 g_free (msg);
515 do_refresh ();
517 return (result == 1) ? FILE_ABORT : FILE_SKIP;
520 /* --------------------------------------------------------------------------------------------- */
522 static FileProgressStatus
523 warn_same_file (const char *fmt, const char *a, const char *b)
525 #ifdef WITH_BACKGROUND
526 /* *INDENT-OFF* */
527 union
529 void *p;
530 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
531 const char *a, const char *b);
532 } pntr;
533 /* *INDENT-ON* */
535 pntr.f = real_warn_same_file;
537 if (mc_global.we_are_background)
538 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
539 #endif
540 return real_warn_same_file (Foreground, fmt, a, b);
543 /* --------------------------------------------------------------------------------------------- */
544 /* {{{ Query/status report routines */
546 static FileProgressStatus
547 real_do_file_error (enum OperationMode mode, const char *error)
549 int result;
550 const char *msg;
552 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
553 result =
554 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
556 switch (result)
558 case 0:
559 do_refresh ();
560 return FILE_SKIP;
562 case 1:
563 do_refresh ();
564 return FILE_SKIPALL;
566 case 2:
567 do_refresh ();
568 return FILE_RETRY;
570 case 3:
571 default:
572 return FILE_ABORT;
576 /* --------------------------------------------------------------------------------------------- */
578 static FileProgressStatus
579 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
581 gchar *text;
583 if (ctx->recursive_result < RECURSIVE_ALWAYS)
585 const char *msg = mode == Foreground
586 ? _("\nDirectory not empty.\nDelete it recursively?")
587 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
588 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
590 if (safe_delete)
591 query_set_sel (1);
593 ctx->recursive_result =
594 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
595 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
597 if (ctx->recursive_result != RECURSIVE_ABORT)
598 do_refresh ();
599 g_free (text);
602 switch (ctx->recursive_result)
604 case RECURSIVE_YES:
605 case RECURSIVE_ALWAYS:
606 return FILE_CONT;
608 case RECURSIVE_NO:
609 case RECURSIVE_NEVER:
610 return FILE_SKIP;
612 case RECURSIVE_ABORT:
613 default:
614 return FILE_ABORT;
618 /* --------------------------------------------------------------------------------------------- */
620 #ifdef WITH_BACKGROUND
621 static FileProgressStatus
622 do_file_error (const char *str)
624 union
626 void *p;
627 FileProgressStatus (*f) (enum OperationMode, const char *);
628 } pntr;
629 pntr.f = real_do_file_error;
631 if (mc_global.we_are_background)
632 return parent_call (pntr.p, NULL, 1, strlen (str), str);
633 else
634 return real_do_file_error (Foreground, str);
637 /* --------------------------------------------------------------------------------------------- */
639 static FileProgressStatus
640 query_recursive (FileOpContext * ctx, const char *s)
642 union
644 void *p;
645 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
646 } pntr;
647 pntr.f = real_query_recursive;
649 if (mc_global.we_are_background)
650 return parent_call (pntr.p, ctx, 1, strlen (s), s);
651 else
652 return real_query_recursive (ctx, Foreground, s);
655 /* --------------------------------------------------------------------------------------------- */
657 static FileProgressStatus
658 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
659 struct stat *_d_stat)
661 union
663 void *p;
664 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
665 struct stat *, struct stat *);
666 } pntr;
667 pntr.f = file_progress_real_query_replace;
669 if (mc_global.we_are_background)
670 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
671 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
672 else
673 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
676 #else
677 /* --------------------------------------------------------------------------------------------- */
679 static FileProgressStatus
680 do_file_error (const char *str)
682 return real_do_file_error (Foreground, str);
685 /* --------------------------------------------------------------------------------------------- */
687 static FileProgressStatus
688 query_recursive (FileOpContext * ctx, const char *s)
690 return real_query_recursive (ctx, Foreground, s);
693 /* --------------------------------------------------------------------------------------------- */
695 static FileProgressStatus
696 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
697 struct stat *_d_stat)
699 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
702 #endif /* !WITH_BACKGROUND */
704 /* --------------------------------------------------------------------------------------------- */
705 /** Report error with two files */
707 static FileProgressStatus
708 files_error (const char *format, const char *file1, const char *file2)
710 char buf[BUF_MEDIUM];
711 char *nfile1 = g_strdup (path_trunc (file1, 15));
712 char *nfile2 = g_strdup (path_trunc (file2, 15));
714 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
716 g_free (nfile1);
717 g_free (nfile2);
719 return do_file_error (buf);
722 /* }}} */
724 /* --------------------------------------------------------------------------------------------- */
726 static void
727 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
728 struct timeval tv_current, struct timeval tv_transfer_start,
729 off_t file_size, off_t n_read_total)
731 long dt;
733 /* 1. Update rotating dash after some time */
734 rotate_dash ();
736 /* 3. Compute ETA */
737 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
739 if (n_read_total == 0)
740 ctx->eta_secs = 0.0;
741 else
743 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
744 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
747 /* 4. Compute BPS rate */
748 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
749 if (ctx->bps_time < 1)
750 ctx->bps_time = 1;
751 ctx->bps = n_read_total / ctx->bps_time;
753 /* 5. Compute total ETA and BPS */
754 if (ctx->progress_bytes != 0)
756 uintmax_t remain_bytes;
758 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
759 #if 1
761 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
763 if (total_secs < 1)
764 total_secs = 1;
766 tctx->bps = tctx->copied_bytes / total_secs;
767 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
769 #else
770 /* broken on lot of little files */
771 tctx->bps_count++;
772 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
773 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
774 #endif
778 /* --------------------------------------------------------------------------------------------- */
780 /* {{{ Move routines */
781 static FileProgressStatus
782 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
784 struct stat src_stats, dst_stats;
785 FileProgressStatus return_status = FILE_CONT;
786 gboolean copy_done = FALSE;
787 gboolean old_ask_overwrite;
788 vfs_path_t *src_vpath, *dst_vpath;
790 src_vpath = vfs_path_from_str (s);
791 dst_vpath = vfs_path_from_str (d);
793 file_progress_show_source (ctx, src_vpath);
794 file_progress_show_target (ctx, dst_vpath);
796 if (check_progress_buttons (ctx) == FILE_ABORT)
798 return_status = FILE_ABORT;
799 goto ret;
802 mc_refresh ();
804 while (mc_lstat (src_vpath, &src_stats) != 0)
806 /* Source doesn't exist */
807 if (ctx->skip_all)
808 return_status = FILE_SKIPALL;
809 else
811 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
812 if (return_status == FILE_SKIPALL)
813 ctx->skip_all = TRUE;
816 if (return_status != FILE_RETRY)
817 goto ret;
820 if (mc_lstat (dst_vpath, &dst_stats) == 0)
822 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
824 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
825 goto ret;
828 if (S_ISDIR (dst_stats.st_mode))
830 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
831 do_refresh ();
832 return_status = FILE_SKIP;
833 goto ret;
836 if (confirm_overwrite)
838 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
839 if (return_status != FILE_CONT)
840 goto ret;
842 /* Ok to overwrite */
845 if (!ctx->do_append)
847 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
849 return_status = make_symlink (ctx, s, d);
850 if (return_status == FILE_CONT)
851 goto retry_src_remove;
852 goto ret;
855 if (mc_rename (src_vpath, dst_vpath) == 0)
857 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
858 goto ret;
861 #if 0
862 /* Comparison to EXDEV seems not to work in nfs if you're moving from
863 one nfs to the same, but on the server it is on two different
864 filesystems. Then nfs returns EIO instead of EXDEV.
865 Hope it will not hurt if we always in case of error try to copy/delete. */
866 else
867 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
869 if (errno != EXDEV)
871 if (ctx->skip_all)
872 return_status = FILE_SKIPALL;
873 else
875 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
876 if (return_status == FILE_SKIPALL)
877 ctx->skip_all = TRUE;
878 if (return_status == FILE_RETRY)
879 goto retry_rename;
882 goto ret;
884 #endif
886 /* Failed because filesystem boundary -> copy the file instead */
887 old_ask_overwrite = tctx->ask_overwrite;
888 tctx->ask_overwrite = FALSE;
889 return_status = copy_file_file (tctx, ctx, s, d);
890 tctx->ask_overwrite = old_ask_overwrite;
891 if (return_status != FILE_CONT)
892 goto ret;
894 copy_done = TRUE;
896 file_progress_show_source (ctx, NULL);
897 file_progress_show (ctx, 0, 0, "", FALSE);
899 return_status = check_progress_buttons (ctx);
900 if (return_status != FILE_CONT)
901 goto ret;
902 mc_refresh ();
904 retry_src_remove:
905 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
907 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
908 if (return_status == FILE_RETRY)
909 goto retry_src_remove;
910 if (return_status == FILE_SKIPALL)
911 ctx->skip_all = TRUE;
912 goto ret;
915 if (!copy_done)
916 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
918 ret:
919 vfs_path_free (src_vpath);
920 vfs_path_free (dst_vpath);
922 return return_status;
925 /* }}} */
927 /* --------------------------------------------------------------------------------------------- */
928 /* {{{ Erase routines */
929 /** Don't update progress status if progress_count==NULL */
931 static FileProgressStatus
932 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * vpath)
934 int return_status;
935 struct stat buf;
936 char *s;
938 s = vfs_path_to_str (vpath);
939 file_progress_show_deleting (ctx, s);
940 if (check_progress_buttons (ctx) == FILE_ABORT)
942 g_free (s);
943 return FILE_ABORT;
945 mc_refresh ();
947 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
949 /* ignore, most likely the mc_unlink fails, too */
950 buf.st_size = 0;
953 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
955 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
956 if (return_status == FILE_ABORT)
958 g_free (s);
959 return return_status;
961 if (return_status == FILE_RETRY)
962 continue;
963 if (return_status == FILE_SKIPALL)
964 ctx->skip_all = TRUE;
965 break;
967 g_free (s);
968 if (tctx->progress_count == 0)
969 return FILE_CONT;
970 return progress_update_one (tctx, ctx, buf.st_size);
973 /* --------------------------------------------------------------------------------------------- */
976 Recursive remove of files
977 abort->cancel stack
978 skip ->warn every level, gets default
979 skipall->remove as much as possible
981 static FileProgressStatus
982 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
984 struct dirent *next;
985 struct stat buf;
986 DIR *reading;
987 char *path;
988 FileProgressStatus return_status = FILE_CONT;
989 vfs_path_t *vpath;
991 if (strcmp (s, "..") == 0)
992 return FILE_RETRY;
994 vpath = vfs_path_from_str (s);
995 reading = mc_opendir (vpath);
997 if (reading == NULL)
999 return_status = FILE_RETRY;
1000 goto ret;
1003 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1005 vfs_path_t *tmp_vpath;
1007 if (!strcmp (next->d_name, "."))
1008 continue;
1009 if (!strcmp (next->d_name, ".."))
1010 continue;
1011 path = mc_build_filename (s, next->d_name, NULL);
1012 tmp_vpath = vfs_path_from_str (path);
1013 if (mc_lstat (tmp_vpath, &buf) != 0)
1015 g_free (path);
1016 mc_closedir (reading);
1017 vfs_path_free (tmp_vpath);
1018 return_status = FILE_RETRY;
1019 goto ret;
1021 if (S_ISDIR (buf.st_mode))
1022 return_status = recursive_erase (tctx, ctx, path);
1023 else
1024 return_status = erase_file (tctx, ctx, tmp_vpath);
1025 vfs_path_free (tmp_vpath);
1026 g_free (path);
1028 mc_closedir (reading);
1029 if (return_status == FILE_ABORT)
1030 goto ret;
1032 file_progress_show_deleting (ctx, s);
1033 if (check_progress_buttons (ctx) == FILE_ABORT)
1035 return_status = FILE_ABORT;
1036 goto ret;
1038 mc_refresh ();
1040 while (my_rmdir (s) != 0 && !ctx->skip_all)
1042 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1043 if (return_status == FILE_RETRY)
1044 continue;
1045 if (return_status == FILE_ABORT)
1046 goto ret;
1047 if (return_status == FILE_SKIPALL)
1048 ctx->skip_all = TRUE;
1049 break;
1052 ret:
1053 vfs_path_free (vpath);
1054 return return_status;
1057 /* --------------------------------------------------------------------------------------------- */
1058 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1059 in the directory path points to, 0 else. */
1061 static int
1062 check_dir_is_empty (const vfs_path_t * vpath)
1064 DIR *dir;
1065 struct dirent *d;
1066 int i;
1068 dir = mc_opendir (vpath);
1069 if (!dir)
1070 return -1;
1072 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1074 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1075 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1076 continue; /* "." or ".." */
1077 i = 0;
1078 break;
1081 mc_closedir (dir);
1082 return i;
1085 /* --------------------------------------------------------------------------------------------- */
1087 static FileProgressStatus
1088 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1090 FileProgressStatus error;
1091 vfs_path_t *s_vpath;
1093 if (strcmp (s, "..") == 0)
1094 return FILE_SKIP;
1096 if (strcmp (s, ".") == 0)
1097 return FILE_SKIP;
1099 file_progress_show_deleting (ctx, s);
1100 if (check_progress_buttons (ctx) == FILE_ABORT)
1101 return FILE_ABORT;
1103 mc_refresh ();
1105 s_vpath = vfs_path_from_str (s);
1107 if (check_dir_is_empty (s_vpath) == 1) /* not empty or error */
1109 while (my_rmdir (s) != 0 && !ctx->skip_all)
1111 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1112 if (error == FILE_SKIPALL)
1113 ctx->skip_all = TRUE;
1114 if (error != FILE_RETRY)
1116 vfs_path_free (s_vpath);
1117 return error;
1122 vfs_path_free (s_vpath);
1123 return FILE_CONT;
1126 /* }}} */
1128 /* --------------------------------------------------------------------------------------------- */
1129 /* {{{ Panel operate routines */
1132 * Return currently selected entry name or the name of the first marked
1133 * entry if there is one.
1136 static char *
1137 panel_get_file (WPanel * panel)
1139 if (get_current_type () == view_tree)
1141 WTree *tree;
1143 tree = (WTree *) get_panel_widget (get_current_index ());
1144 return vfs_path_to_str (tree_selected_name (tree));
1147 if (panel->marked != 0)
1149 int i;
1151 for (i = 0; i < panel->count; i++)
1152 if (panel->dir.list[i].f.marked)
1153 return g_strdup (panel->dir.list[i].fname);
1155 return g_strdup (panel->dir.list[panel->selected].fname);
1158 /* --------------------------------------------------------------------------------------------- */
1160 * panel_compute_totals:
1162 * compute the number of files and the number of bytes
1163 * used up by the whole selection, recursing directories
1164 * as required. In addition, it checks to see if it will
1165 * overwrite any files by doing the copy.
1168 static FileProgressStatus
1169 panel_compute_totals (const WPanel * panel, const void *ui,
1170 compute_dir_size_callback cback,
1171 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1173 int i;
1175 *ret_marked = 0;
1176 *ret_total = 0;
1178 for (i = 0; i < panel->count; i++)
1180 struct stat *s;
1182 if (!panel->dir.list[i].f.marked)
1183 continue;
1185 s = &panel->dir.list[i].st;
1187 if (S_ISDIR (s->st_mode))
1189 vfs_path_t *p;
1190 size_t subdir_count = 0;
1191 uintmax_t subdir_bytes = 0;
1192 FileProgressStatus status;
1194 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, NULL);
1195 status =
1196 compute_dir_size (p, ui, cback, &subdir_count, &subdir_bytes, compute_symlinks);
1197 vfs_path_free (p);
1199 if (status != FILE_CONT)
1200 return FILE_ABORT;
1202 *ret_marked += subdir_count;
1203 *ret_total += subdir_bytes;
1205 else
1207 (*ret_marked)++;
1208 *ret_total += (uintmax_t) s->st_size;
1212 return FILE_CONT;
1215 /* --------------------------------------------------------------------------------------------- */
1217 /** Initialize variables for progress bars */
1218 static FileProgressStatus
1219 panel_operate_init_totals (FileOperation operation,
1220 const WPanel * panel, const char *source, FileOpContext * ctx)
1222 FileProgressStatus status;
1224 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1226 ComputeDirSizeUI *ui;
1228 ui = compute_dir_size_create_ui ();
1230 if (source == NULL)
1231 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1232 &ctx->progress_count, &ctx->progress_bytes,
1233 ctx->follow_links);
1234 else
1236 vfs_path_t *p;
1238 p = vfs_path_from_str (source);
1239 status = compute_dir_size (p, ui, compute_dir_size_update_ui,
1240 &ctx->progress_count, &ctx->progress_bytes,
1241 ctx->follow_links);
1242 vfs_path_free (p);
1245 compute_dir_size_destroy_ui (ui);
1247 ctx->progress_totals_computed = (status == FILE_CONT);
1249 else
1251 status = FILE_CONT;
1252 ctx->progress_count = panel->marked;
1253 ctx->progress_bytes = panel->total;
1254 ctx->progress_totals_computed = FALSE;
1257 return status;
1260 /* --------------------------------------------------------------------------------------------- */
1262 * Generate user prompt for panel operation.
1263 * single_source is the name if the source entry or NULL for multiple
1264 * entries.
1265 * src_stat is only used when single_source is not NULL.
1268 static char *
1269 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1270 gboolean single_source, const struct stat *src_stat)
1272 const char *sp, *cp;
1273 char format_string[BUF_MEDIUM];
1274 char *dp = format_string;
1275 gboolean build_question = FALSE;
1277 static gboolean i18n_flag = FALSE;
1278 if (!i18n_flag)
1280 size_t i;
1282 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1283 op_names1[i] = Q_ (op_names1[i]);
1285 #ifdef ENABLE_NLS
1286 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1287 prompt_parts[i] = _(prompt_parts[i]);
1289 one_format = _(one_format);
1290 many_format = _(many_format);
1291 question_format = _(question_format);
1292 #endif /* ENABLE_NLS */
1293 i18n_flag = TRUE;
1296 sp = single_source ? one_format : many_format;
1298 while (*sp != '\0')
1300 switch (*sp)
1302 case '%':
1303 cp = NULL;
1304 switch (sp[1])
1306 case 'o':
1307 cp = op_names1[operation];
1308 break;
1309 case 'm':
1310 if (operation == OP_DELETE)
1312 cp = "";
1313 build_question = TRUE;
1315 else
1316 cp = prompt_parts[5];
1317 break;
1318 case 'e':
1319 if (operation == OP_DELETE)
1321 cp = "";
1322 build_question = TRUE;
1324 else
1325 cp = prompt_parts[6];
1326 break;
1327 case 'f':
1328 if (single_source)
1329 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1330 else
1331 cp = (panel->marked == panel->dirs_marked)
1332 ? prompt_parts[3]
1333 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1334 break;
1335 default:
1336 *dp++ = *sp++;
1339 if (cp != NULL)
1341 sp += 2;
1342 while (*cp != '\0')
1343 *dp++ = *cp++;
1345 break;
1346 default:
1347 *dp++ = *sp++;
1350 *dp = '\0';
1352 if (build_question)
1354 char tmp[BUF_MEDIUM];
1356 memmove (tmp, format_string, sizeof (tmp));
1357 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1360 return g_strdup (format_string);
1363 /* --------------------------------------------------------------------------------------------- */
1365 #ifdef WITH_BACKGROUND
1366 static int
1367 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1369 int pid = ctx->pid;
1371 (void) mode;
1372 ctx->pid = 0;
1374 unregister_task_with_pid (pid);
1375 /* file_op_context_destroy(ctx); */
1376 return 1;
1378 #endif
1379 /* }}} */
1381 /* --------------------------------------------------------------------------------------------- */
1382 /*** public functions ****************************************************************************/
1383 /* --------------------------------------------------------------------------------------------- */
1385 FileProgressStatus
1386 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1387 const char *src_path, const char *dst_path)
1389 uid_t src_uid = (uid_t) (-1);
1390 gid_t src_gid = (gid_t) (-1);
1392 int src_desc, dest_desc = -1;
1393 int n_read, n_written;
1394 mode_t src_mode = 0; /* The mode of the source file */
1395 struct stat sb, sb2;
1396 struct utimbuf utb;
1397 gboolean dst_exists = FALSE, appending = FALSE;
1398 off_t file_size = -1;
1399 FileProgressStatus return_status, temp_status;
1400 struct timeval tv_transfer_start;
1401 dest_status_t dst_status = DEST_NONE;
1402 int open_flags;
1403 gboolean is_first_time = TRUE;
1404 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1406 /* FIXME: We should not be using global variables! */
1407 ctx->do_reget = 0;
1408 return_status = FILE_RETRY;
1410 dst_vpath = vfs_path_from_str (dst_path);
1411 src_vpath = vfs_path_from_str (src_path);
1413 file_progress_show_source (ctx, src_vpath);
1414 file_progress_show_target (ctx, dst_vpath);
1416 if (check_progress_buttons (ctx) == FILE_ABORT)
1418 return_status = FILE_ABORT;
1419 goto ret_fast;
1422 mc_refresh ();
1424 while (mc_stat (dst_vpath, &sb2) == 0)
1426 if (S_ISDIR (sb2.st_mode))
1428 if (ctx->skip_all)
1429 return_status = FILE_SKIPALL;
1430 else
1432 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1433 if (return_status == FILE_SKIPALL)
1434 ctx->skip_all = TRUE;
1435 if (return_status == FILE_RETRY)
1436 continue;
1438 goto ret_fast;
1441 dst_exists = TRUE;
1442 break;
1445 while ((*ctx->stat_func) (src_vpath, &sb) != 0)
1447 if (ctx->skip_all)
1448 return_status = FILE_SKIPALL;
1449 else
1451 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1452 if (return_status == FILE_SKIPALL)
1453 ctx->skip_all = TRUE;
1456 if (return_status != FILE_RETRY)
1457 goto ret_fast;
1460 if (dst_exists)
1462 /* Destination already exists */
1463 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1465 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1466 src_path, dst_path);
1467 goto ret_fast;
1470 /* Should we replace destination? */
1471 if (tctx->ask_overwrite)
1473 ctx->do_reget = 0;
1474 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1475 if (return_status != FILE_CONT)
1476 goto ret_fast;
1480 if (!ctx->do_append)
1482 /* Check the hardlinks */
1483 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &sb))
1485 /* We have made a hardlink - no more processing is necessary */
1486 return_status = FILE_CONT;
1487 goto ret_fast;
1490 if (S_ISLNK (sb.st_mode))
1492 return_status = make_symlink (ctx, src_path, dst_path);
1493 goto ret_fast;
1496 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1497 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1499 while (mc_mknod (dst_vpath, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0
1500 && !ctx->skip_all)
1502 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1503 if (return_status == FILE_RETRY)
1504 continue;
1505 if (return_status == FILE_SKIPALL)
1506 ctx->skip_all = TRUE;
1507 goto ret_fast;
1509 /* Success */
1511 while (ctx->preserve_uidgid && mc_chown (dst_vpath, sb.st_uid, sb.st_gid) != 0
1512 && !ctx->skip_all)
1514 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1515 if (temp_status == FILE_SKIP)
1516 break;
1517 if (temp_status == FILE_SKIPALL)
1518 ctx->skip_all = TRUE;
1519 if (temp_status != FILE_RETRY)
1521 return_status = temp_status;
1522 goto ret_fast;
1526 while (ctx->preserve && mc_chmod (dst_vpath, sb.st_mode & ctx->umask_kill) != 0
1527 && !ctx->skip_all)
1529 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1530 if (temp_status == FILE_SKIP)
1531 break;
1532 if (temp_status == FILE_SKIPALL)
1533 ctx->skip_all = TRUE;
1534 if (temp_status != FILE_RETRY)
1536 return_status = temp_status;
1537 goto ret_fast;
1541 return_status = FILE_CONT;
1542 goto ret_fast;
1546 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1548 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1550 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1551 if (return_status == FILE_RETRY)
1552 continue;
1553 if (return_status == FILE_SKIPALL)
1554 ctx->skip_all = TRUE;
1555 if (return_status == FILE_SKIP)
1556 break;
1557 ctx->do_append = 0;
1558 goto ret_fast;
1561 if (ctx->do_reget != 0)
1563 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1565 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1566 ctx->do_reget = 0;
1567 ctx->do_append = FALSE;
1571 while (mc_fstat (src_desc, &sb) != 0)
1573 if (ctx->skip_all)
1574 return_status = FILE_SKIPALL;
1575 else
1577 return_status = file_error (_("Cannot fstat 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 ctx->do_append = FALSE;
1584 goto ret;
1587 src_mode = sb.st_mode;
1588 src_uid = sb.st_uid;
1589 src_gid = sb.st_gid;
1590 utb.actime = sb.st_atime;
1591 utb.modtime = sb.st_mtime;
1592 file_size = sb.st_size;
1594 open_flags = O_WRONLY;
1595 if (dst_exists)
1597 if (ctx->do_append != 0)
1598 open_flags |= O_APPEND;
1599 else
1600 open_flags |= O_CREAT | O_TRUNC;
1602 else
1604 open_flags |= O_CREAT | O_EXCL;
1607 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
1609 if (errno != EEXIST)
1611 if (ctx->skip_all)
1612 return_status = FILE_SKIPALL;
1613 else
1615 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1616 if (return_status == FILE_RETRY)
1617 continue;
1618 if (return_status == FILE_SKIPALL)
1619 ctx->skip_all = TRUE;
1620 ctx->do_append = FALSE;
1623 goto ret;
1625 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1627 appending = ctx->do_append;
1628 ctx->do_append = FALSE;
1630 /* Find out the optimal buffer size. */
1631 while (mc_fstat (dest_desc, &sb) != 0)
1633 if (ctx->skip_all)
1634 return_status = FILE_SKIPALL;
1635 else
1637 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1638 if (return_status == FILE_RETRY)
1639 continue;
1640 if (return_status == FILE_SKIPALL)
1641 ctx->skip_all = TRUE;
1643 goto ret;
1646 while (TRUE)
1648 errno = vfs_preallocate (dest_desc, file_size, (ctx->do_append != 0) ? sb.st_size : 0);
1649 if (errno == 0)
1650 break;
1652 if (ctx->skip_all)
1653 return_status = FILE_SKIPALL;
1654 else
1656 return_status =
1657 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1658 if (return_status == FILE_RETRY)
1659 continue;
1660 if (return_status == FILE_SKIPALL)
1661 ctx->skip_all = TRUE;
1663 mc_close (dest_desc);
1664 dest_desc = -1;
1665 mc_unlink (dst_vpath);
1666 dst_status = DEST_NONE;
1667 goto ret;
1670 ctx->eta_secs = 0.0;
1671 ctx->bps = 0;
1673 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1674 file_progress_show (ctx, 0, file_size, "", TRUE);
1675 else
1676 file_progress_show (ctx, 1, 1, "", TRUE);
1677 return_status = check_progress_buttons (ctx);
1678 mc_refresh ();
1680 if (return_status != FILE_CONT)
1681 goto ret;
1684 off_t n_read_total = 0;
1685 struct timeval tv_current, tv_last_update, tv_last_input;
1686 int secs, update_secs;
1687 const char *stalled_msg = "";
1689 tv_last_update = tv_transfer_start;
1691 while (TRUE)
1693 char buf[BUF_8K];
1695 /* src_read */
1696 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1697 n_read = -1;
1698 else
1699 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0 && !ctx->skip_all)
1701 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1702 if (return_status == FILE_RETRY)
1703 continue;
1704 if (return_status == FILE_SKIPALL)
1705 ctx->skip_all = TRUE;
1706 goto ret;
1708 if (n_read == 0)
1709 break;
1711 gettimeofday (&tv_current, NULL);
1713 if (n_read > 0)
1715 char *t = buf;
1716 n_read_total += n_read;
1718 /* Windows NT ftp servers report that files have no
1719 * permissions: -------, so if we happen to have actually
1720 * read something, we should fix the permissions.
1722 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1723 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1724 gettimeofday (&tv_last_input, NULL);
1726 /* dst_write */
1727 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read && !ctx->skip_all)
1729 if (n_written > 0)
1731 n_read -= n_written;
1732 t += n_written;
1733 continue;
1735 return_status = file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1736 if (return_status == FILE_SKIP)
1737 break;
1738 if (return_status == FILE_SKIPALL)
1739 ctx->skip_all = TRUE;
1740 if (return_status != FILE_RETRY)
1741 goto ret;
1745 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1747 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1748 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1750 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1752 copy_file_file_display_progress (tctx, ctx,
1753 tv_current,
1754 tv_transfer_start, file_size, n_read_total);
1755 tv_last_update = tv_current;
1757 is_first_time = FALSE;
1759 if (update_secs > FILEOP_STALLING_INTERVAL)
1761 stalled_msg = _("(stalled)");
1765 gboolean force_update;
1767 force_update =
1768 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1770 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1772 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1773 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1776 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1777 force_update);
1779 mc_refresh ();
1781 return_status = check_progress_buttons (ctx);
1783 if (return_status != FILE_CONT)
1785 mc_refresh ();
1786 goto ret;
1791 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1793 ret:
1794 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1796 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1797 if (temp_status == FILE_RETRY)
1798 continue;
1799 if (temp_status == FILE_ABORT)
1800 return_status = temp_status;
1801 if (temp_status == FILE_SKIPALL)
1802 ctx->skip_all = TRUE;
1803 break;
1806 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1808 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1809 if (temp_status == FILE_RETRY)
1810 continue;
1811 if (temp_status == FILE_SKIPALL)
1812 ctx->skip_all = TRUE;
1813 return_status = temp_status;
1814 break;
1817 if (dst_status == DEST_SHORT)
1819 /* Remove short file */
1820 int result;
1822 result = query_dialog (Q_ ("DialogTitle|Copy"),
1823 _("Incomplete file was retrieved. Keep it?"),
1824 D_ERROR, 2, _("&Delete"), _("&Keep"));
1825 if (result == 0)
1826 mc_unlink (dst_vpath);
1828 else if (dst_status == DEST_FULL)
1830 /* Copy has succeeded */
1831 if (!appending && ctx->preserve_uidgid)
1833 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1835 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1836 if (temp_status == FILE_RETRY)
1837 continue;
1838 if (temp_status == FILE_SKIPALL)
1840 ctx->skip_all = TRUE;
1841 return_status = FILE_CONT;
1843 if (temp_status == FILE_SKIP)
1844 return_status = FILE_CONT;
1845 break;
1849 if (!appending)
1851 if (ctx->preserve)
1853 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1855 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1856 if (temp_status == FILE_RETRY)
1857 continue;
1858 if (temp_status == FILE_SKIPALL)
1860 ctx->skip_all = TRUE;
1861 return_status = FILE_CONT;
1863 if (temp_status == FILE_SKIP)
1864 return_status = FILE_CONT;
1865 break;
1868 else
1870 src_mode = umask (-1);
1871 umask (src_mode);
1872 src_mode = 0100666 & ~src_mode;
1873 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
1875 mc_utime (dst_vpath, &utb);
1879 if (return_status == FILE_CONT)
1880 return_status = progress_update_one (tctx, ctx, file_size);
1882 ret_fast:
1883 vfs_path_free (src_vpath);
1884 vfs_path_free (dst_vpath);
1885 return return_status;
1888 /* --------------------------------------------------------------------------------------------- */
1890 * I think these copy_*_* functions should have a return type.
1891 * anyway, this function *must* have two directories as arguments.
1893 /* FIXME: This function needs to check the return values of the
1894 function calls */
1896 FileProgressStatus
1897 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1898 gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs)
1900 struct dirent *next;
1901 struct stat buf, cbuf;
1902 DIR *reading;
1903 char *dest_dir = NULL;
1904 FileProgressStatus return_status = FILE_CONT;
1905 struct utimbuf utb;
1906 struct link *lp;
1907 char *d;
1908 vfs_path_t *src_vpath, *dst_vpath, *dest_dir_vpath = NULL;
1910 d = g_strdup (_d);
1912 src_vpath = vfs_path_from_str (s);
1913 dst_vpath = vfs_path_from_str (_d);
1915 /* First get the mode of the source dir */
1917 retry_src_stat:
1918 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
1920 if (ctx->skip_all)
1921 return_status = FILE_SKIPALL;
1922 else
1924 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1925 if (return_status == FILE_RETRY)
1926 goto retry_src_stat;
1927 if (return_status == FILE_SKIPALL)
1928 ctx->skip_all = TRUE;
1930 goto ret_fast;
1933 if (is_in_linklist (dest_dirs, src_vpath, &cbuf))
1935 /* Don't copy a directory we created before (we don't want to copy
1936 infinitely if a directory is copied into itself) */
1937 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1938 return_status = FILE_CONT;
1939 goto ret_fast;
1942 /* Hmm, hardlink to directory??? - Norbert */
1943 /* FIXME: In this step we should do something
1944 in case the destination already exist */
1945 /* Check the hardlinks */
1946 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &cbuf))
1948 /* We have made a hardlink - no more processing is necessary */
1949 goto ret_fast;
1952 if (!S_ISDIR (cbuf.st_mode))
1954 if (ctx->skip_all)
1955 return_status = FILE_SKIPALL;
1956 else
1958 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1959 if (return_status == FILE_RETRY)
1960 goto retry_src_stat;
1961 if (return_status == FILE_SKIPALL)
1962 ctx->skip_all = TRUE;
1964 goto ret_fast;
1967 if (is_in_linklist (parent_dirs, src_vpath, &cbuf))
1969 /* we found a cyclic symbolic link */
1970 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
1971 return_status = FILE_SKIP;
1972 goto ret_fast;
1975 lp = g_new0 (struct link, 1);
1976 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
1977 lp->ino = cbuf.st_ino;
1978 lp->dev = cbuf.st_dev;
1979 parent_dirs = g_slist_prepend (parent_dirs, lp);
1981 retry_dst_stat:
1982 /* Now, check if the dest dir exists, if not, create it. */
1983 if (mc_stat (dst_vpath, &buf) != 0)
1985 /* Here the dir doesn't exist : make it ! */
1986 if (move_over)
1988 if (mc_rename (src_vpath, dst_vpath) == 0)
1990 return_status = FILE_CONT;
1991 goto ret;
1994 dest_dir = d;
1995 d = NULL;
1997 else
2000 * If the destination directory exists, we want to copy the whole
2001 * directory, but we only want this to happen once.
2003 * Escape sequences added to the * to compiler warnings.
2004 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2005 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2007 if (!S_ISDIR (buf.st_mode))
2009 if (ctx->skip_all)
2010 return_status = FILE_SKIPALL;
2011 else
2013 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2014 if (return_status == FILE_SKIPALL)
2015 ctx->skip_all = TRUE;
2016 if (return_status == FILE_RETRY)
2017 goto retry_dst_stat;
2019 goto ret;
2021 /* Dive into subdir if exists */
2022 if (toplevel && ctx->dive_into_subdirs)
2024 dest_dir = mc_build_filename (d, x_basename (s), NULL);
2026 else
2028 dest_dir = d;
2029 d = NULL;
2030 goto dont_mkdir;
2033 dest_dir_vpath = vfs_path_from_str (dest_dir);
2034 while (my_mkdir (dest_dir_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2036 if (ctx->skip_all)
2037 return_status = FILE_SKIPALL;
2038 else
2040 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
2041 if (return_status == FILE_SKIPALL)
2042 ctx->skip_all = TRUE;
2044 if (return_status != FILE_RETRY)
2045 goto ret;
2048 lp = g_new0 (struct link, 1);
2049 mc_stat (dest_dir_vpath, &buf);
2050 lp->vfs = vfs_path_get_by_index (dest_dir_vpath, -1)->class;
2051 lp->ino = buf.st_ino;
2052 lp->dev = buf.st_dev;
2053 dest_dirs = g_slist_prepend (dest_dirs, lp);
2055 if (ctx->preserve_uidgid)
2057 while (mc_chown (dest_dir_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2059 if (ctx->skip_all)
2060 return_status = FILE_SKIPALL;
2061 else
2063 return_status =
2064 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
2065 if (return_status == FILE_SKIPALL)
2066 ctx->skip_all = TRUE;
2068 if (return_status != FILE_RETRY)
2069 goto ret;
2073 dont_mkdir:
2074 /* open the source dir for reading */
2075 reading = mc_opendir (src_vpath);
2076 if (reading == NULL)
2077 goto ret;
2079 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2081 char *path;
2082 vfs_path_t *tmp_vpath;
2084 * Now, we don't want '.' and '..' to be created / copied at any time
2086 if (!strcmp (next->d_name, "."))
2087 continue;
2088 if (!strcmp (next->d_name, ".."))
2089 continue;
2091 /* get the filename and add it to the src directory */
2092 path = mc_build_filename (s, next->d_name, NULL);
2093 tmp_vpath = vfs_path_from_str (path);
2095 (*ctx->stat_func) (tmp_vpath, &buf);
2096 if (S_ISDIR (buf.st_mode))
2098 char *mdpath;
2100 mdpath = mc_build_filename (dest_dir, next->d_name, NULL);
2102 * From here, we just intend to recursively copy subdirs, not
2103 * the double functionality of copying different when the target
2104 * dir already exists. So, we give the recursive call the flag 0
2105 * meaning no toplevel.
2107 return_status =
2108 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2109 g_free (mdpath);
2111 else
2113 char *dest_file;
2115 dest_file = mc_build_filename (dest_dir, x_basename (path), NULL);
2116 return_status = copy_file_file (tctx, ctx, path, dest_file);
2117 g_free (dest_file);
2119 if (do_delete && return_status == FILE_CONT)
2121 if (ctx->erase_at_end)
2123 lp = g_new0 (struct link, 1);
2124 lp->src_vpath = vfs_path_clone (tmp_vpath);
2125 lp->st_mode = buf.st_mode;
2126 erase_list = g_slist_append (erase_list, lp);
2128 else if (S_ISDIR (buf.st_mode))
2129 return_status = erase_dir_iff_empty (ctx, path);
2130 else
2131 return_status = erase_file (tctx, ctx, tmp_vpath);
2133 g_free (path);
2134 vfs_path_free (tmp_vpath);
2136 mc_closedir (reading);
2138 if (ctx->preserve)
2140 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2141 utb.actime = cbuf.st_atime;
2142 utb.modtime = cbuf.st_mtime;
2143 mc_utime (dest_dir_vpath, &utb);
2145 else
2147 cbuf.st_mode = umask (-1);
2148 umask (cbuf.st_mode);
2149 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2150 mc_chmod (dest_dir_vpath, cbuf.st_mode & ctx->umask_kill);
2153 ret:
2154 g_free (dest_dir);
2155 vfs_path_free (dest_dir_vpath);
2156 free_link (parent_dirs->data);
2157 g_slist_free_1 (parent_dirs);
2158 ret_fast:
2159 g_free (d);
2160 vfs_path_free (src_vpath);
2161 vfs_path_free (dst_vpath);
2162 return return_status;
2165 /* }}} */
2167 /* --------------------------------------------------------------------------------------------- */
2168 /* {{{ Move routines */
2170 FileProgressStatus
2171 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
2173 struct stat sbuf, dbuf, destbuf;
2174 struct link *lp;
2175 char *destdir;
2176 FileProgressStatus return_status;
2177 gboolean move_over = FALSE;
2178 gboolean dstat_ok;
2179 vfs_path_t *src_vpath, *dst_vpath, *destdir_vpath;
2181 src_vpath = vfs_path_from_str (s);
2182 dst_vpath = vfs_path_from_str (d);
2184 file_progress_show_source (ctx, src_vpath);
2185 file_progress_show_target (ctx, dst_vpath);
2187 if (check_progress_buttons (ctx) == FILE_ABORT)
2189 return_status = FILE_ABORT;
2190 goto ret_fast;
2193 mc_refresh ();
2195 mc_stat (src_vpath, &sbuf);
2197 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2198 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2200 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2201 goto ret_fast;
2204 if (!dstat_ok)
2205 destdir = g_strdup (d); /* destination doesn't exist */
2206 else if (!ctx->dive_into_subdirs)
2208 destdir = g_strdup (d);
2209 move_over = TRUE;
2211 else
2212 destdir = mc_build_filename (d, x_basename (s), NULL);
2214 destdir_vpath = vfs_path_from_str (destdir);
2216 /* Check if the user inputted an existing dir */
2217 retry_dst_stat:
2218 if (mc_stat (destdir_vpath, &destbuf) == 0)
2220 if (move_over)
2222 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
2224 if (return_status != FILE_CONT)
2225 goto ret;
2226 goto oktoret;
2228 else if (ctx->skip_all)
2229 return_status = FILE_SKIPALL;
2230 else
2232 if (S_ISDIR (destbuf.st_mode))
2233 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
2234 else
2235 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
2236 if (return_status == FILE_SKIPALL)
2237 ctx->skip_all = TRUE;
2238 if (return_status == FILE_RETRY)
2239 goto retry_dst_stat;
2242 g_free (destdir);
2243 vfs_path_free (destdir_vpath);
2244 goto ret_fast;
2247 retry_rename:
2248 if (mc_rename (src_vpath, destdir_vpath) == 0)
2250 return_status = FILE_CONT;
2251 goto ret;
2254 if (errno != EXDEV)
2256 if (!ctx->skip_all)
2258 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2259 if (return_status == FILE_SKIPALL)
2260 ctx->skip_all = TRUE;
2261 if (return_status == FILE_RETRY)
2262 goto retry_rename;
2264 goto ret;
2266 /* Failed because of filesystem boundary -> copy dir instead */
2267 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
2269 if (return_status != FILE_CONT)
2270 goto ret;
2271 oktoret:
2272 file_progress_show_source (ctx, NULL);
2273 file_progress_show (ctx, 0, 0, "", FALSE);
2275 return_status = check_progress_buttons (ctx);
2276 if (return_status != FILE_CONT)
2277 goto ret;
2279 mc_refresh ();
2280 if (ctx->erase_at_end)
2282 for (; erase_list != NULL && return_status != FILE_ABORT;)
2284 lp = (struct link *) erase_list->data;
2286 if (S_ISDIR (lp->st_mode))
2288 char *src_path;
2290 src_path = vfs_path_to_str (lp->src_vpath);
2291 return_status = erase_dir_iff_empty (ctx, src_path);
2292 g_free (src_path);
2294 else
2295 return_status = erase_file (tctx, ctx, lp->src_vpath);
2297 erase_list = g_slist_remove (erase_list, lp);
2298 free_link (lp);
2301 erase_dir_iff_empty (ctx, s);
2303 ret:
2304 g_free (destdir);
2305 vfs_path_free (destdir_vpath);
2306 erase_list = free_linklist (erase_list);
2307 ret_fast:
2308 vfs_path_free (src_vpath);
2309 vfs_path_free (dst_vpath);
2310 return return_status;
2313 /* }}} */
2315 /* --------------------------------------------------------------------------------------------- */
2316 /* {{{ Erase routines */
2318 FileProgressStatus
2319 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const vfs_path_t * s_vpath)
2321 FileProgressStatus error;
2322 char *s;
2324 s = vfs_path_to_str (s_vpath);
2327 if (strcmp (s, "..") == 0)
2328 return FILE_SKIP;
2330 if (strcmp (s, ".") == 0)
2331 return FILE_SKIP;
2334 file_progress_show_deleting (ctx, s);
2335 if (check_progress_buttons (ctx) == FILE_ABORT)
2337 g_free (s);
2338 return FILE_ABORT;
2340 mc_refresh ();
2342 /* The old way to detect a non empty directory was:
2343 error = my_rmdir (s);
2344 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2345 For the linux user space nfs server (nfs-server-2.2beta29-2)
2346 we would have to check also for EIO. I hope the new way is
2347 fool proof. (Norbert)
2349 error = check_dir_is_empty (s_vpath);
2350 if (error == 0)
2351 { /* not empty */
2352 error = query_recursive (ctx, s);
2353 if (error == FILE_CONT)
2354 error = recursive_erase (tctx, ctx, s);
2355 g_free (s);
2356 return error;
2359 while (my_rmdir (s) == -1 && !ctx->skip_all)
2361 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2362 if (error != FILE_RETRY)
2364 g_free (s);
2365 return error;
2369 g_free (s);
2370 return FILE_CONT;
2373 /* }}} */
2375 /* --------------------------------------------------------------------------------------------- */
2376 /* {{{ Panel operate routines */
2378 ComputeDirSizeUI *
2379 compute_dir_size_create_ui (void)
2381 ComputeDirSizeUI *ui;
2383 const char *b_name = N_("&Abort");
2385 #ifdef ENABLE_NLS
2386 b_name = _(b_name);
2387 #endif
2389 ui = g_new (ComputeDirSizeUI, 1);
2391 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
2392 NULL, _("Directory scanning"), DLG_CENTER);
2393 ui->dirname = label_new (3, 3, "");
2394 add_widget (ui->dlg, ui->dirname);
2396 add_widget (ui->dlg,
2397 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
2398 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2400 /* We will manage the dialog without any help,
2401 that's why we have to call init_dlg */
2402 init_dlg (ui->dlg);
2404 return ui;
2407 /* --------------------------------------------------------------------------------------------- */
2409 void
2410 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2412 if (ui != NULL)
2414 /* schedule to update passive panel */
2415 other_panel->dirty = 1;
2417 /* close and destroy dialog */
2418 dlg_run_done (ui->dlg);
2419 destroy_dlg (ui->dlg);
2420 g_free (ui);
2424 /* --------------------------------------------------------------------------------------------- */
2426 FileProgressStatus
2427 compute_dir_size_update_ui (const void *ui, const vfs_path_t * dirname_vpath)
2429 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2430 int c;
2431 Gpm_Event event;
2432 char *dirname;
2434 if (ui == NULL)
2435 return FILE_CONT;
2437 dirname = vfs_path_to_str (dirname_vpath);
2438 label_set_text (this->dirname, str_trunc (dirname, this->dlg->cols - 6));
2439 g_free (dirname);
2441 event.x = -1; /* Don't show the GPM cursor */
2442 c = tty_get_event (&event, FALSE, FALSE);
2443 if (c == EV_NONE)
2444 return FILE_CONT;
2446 /* Reinitialize to avoid old values after events other than
2447 selecting a button */
2448 this->dlg->ret_value = FILE_CONT;
2450 dlg_process_event (this->dlg, c, &event);
2452 switch (this->dlg->ret_value)
2454 case B_CANCEL:
2455 case FILE_ABORT:
2456 return FILE_ABORT;
2457 default:
2458 return FILE_CONT;
2462 /* --------------------------------------------------------------------------------------------- */
2464 * compute_dir_size:
2466 * Computes the number of bytes used by the files in a directory
2469 FileProgressStatus
2470 compute_dir_size (const vfs_path_t * dirname_vpath, const void *ui,
2471 compute_dir_size_callback cback,
2472 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2474 int res;
2475 struct stat s;
2476 DIR *dir;
2477 struct dirent *dirent;
2478 FileProgressStatus ret = FILE_CONT;
2480 if (!compute_symlinks)
2482 res = mc_lstat (dirname_vpath, &s);
2483 if (res != 0)
2484 return ret;
2486 /* don't scan symlink to directory */
2487 if (S_ISLNK (s.st_mode))
2489 (*ret_marked)++;
2490 *ret_total += (uintmax_t) s.st_size;
2491 return ret;
2495 dir = mc_opendir (dirname_vpath);
2497 if (dir == NULL)
2498 return ret;
2500 while ((dirent = mc_readdir (dir)) != NULL)
2502 vfs_path_t *tmp_vpath;
2504 ret = (cback != NULL) ? cback (ui, dirname_vpath) : FILE_CONT;
2506 if (ret != FILE_CONT)
2507 break;
2509 if (strcmp (dirent->d_name, ".") == 0)
2510 continue;
2511 if (strcmp (dirent->d_name, "..") == 0)
2512 continue;
2514 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, NULL);
2515 res = mc_lstat (tmp_vpath, &s);
2516 if (res == 0)
2518 if (S_ISDIR (s.st_mode))
2520 size_t subdir_count = 0;
2521 uintmax_t subdir_bytes = 0;
2523 ret =
2524 compute_dir_size (tmp_vpath, ui, cback, &subdir_count, &subdir_bytes,
2525 compute_symlinks);
2527 if (ret != FILE_CONT)
2529 vfs_path_free (tmp_vpath);
2530 break;
2533 *ret_marked += subdir_count;
2534 *ret_total += subdir_bytes;
2536 else
2538 (*ret_marked)++;
2539 *ret_total += (uintmax_t) s.st_size;
2542 vfs_path_free (tmp_vpath);
2545 mc_closedir (dir);
2546 return ret;
2549 /* --------------------------------------------------------------------------------------------- */
2551 * panel_operate:
2553 * Performs one of the operations on the selection on the source_panel
2554 * (copy, delete, move).
2556 * Returns TRUE if did change the directory
2557 * structure, Returns FALSE if user aborted
2559 * force_single forces operation on the current entry and affects
2560 * default destination. Current filename is used as default.
2563 gboolean
2564 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2566 WPanel *panel = (WPanel *) source_panel;
2567 const gboolean single_entry = force_single || (panel->marked <= 1)
2568 || (get_current_type () == view_tree);
2570 char *source = NULL;
2571 #ifdef WITH_FULL_PATHS
2572 vfs_path_t *source_with_vpath = NULL;
2573 char *source_with_path_str = NULL;
2574 #else
2575 #define source_with_path source
2576 #endif /* !WITH_FULL_PATHS */
2577 char *dest = NULL;
2578 vfs_path_t *dest_vpath = NULL;
2579 char *temp = NULL;
2580 char *save_cwd = NULL, *save_dest = NULL;
2581 struct stat src_stat;
2582 gboolean ret_val = TRUE;
2583 int i;
2584 FileProgressStatus value;
2585 FileOpContext *ctx;
2586 FileOpTotalContext *tctx;
2587 vfs_path_t *tmp_vpath;
2589 gboolean do_bg = FALSE; /* do background operation? */
2591 static gboolean i18n_flag = FALSE;
2592 if (!i18n_flag)
2594 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2595 op_names[i] = Q_ (op_names[i]);
2596 i18n_flag = TRUE;
2599 linklist = free_linklist (linklist);
2600 dest_dirs = free_linklist (dest_dirs);
2602 if (single_entry)
2604 vfs_path_t *source_vpath;
2606 if (force_single)
2607 source = g_strdup (selection (panel)->fname);
2608 else
2609 source = panel_get_file (panel);
2611 if (strcmp (source, "..") == 0)
2613 g_free (source);
2614 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2615 return FALSE;
2618 source_vpath = vfs_path_from_str (source);
2619 /* Update stat to get actual info */
2620 if (mc_stat (source_vpath, &src_stat) != 0)
2622 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2623 path_trunc (source, 30), unix_error_string (errno));
2625 /* Directory was changed outside MC. Reload it forced */
2626 if (!panel->is_panelized)
2628 panel_update_flags_t flags = UP_RELOAD;
2630 /* don't update panelized panel */
2631 if (get_other_type () == view_listing && other_panel->is_panelized)
2632 flags |= UP_ONLY_CURRENT;
2634 update_panels (flags, UP_KEEPSEL);
2636 vfs_path_free (source_vpath);
2637 return FALSE;
2639 vfs_path_free (source_vpath);
2642 ctx = file_op_context_new (operation);
2644 /* Show confirmation dialog */
2645 if (operation != OP_DELETE)
2647 char *tmp_dest_dir, *dest_dir;
2648 char *format;
2650 /* Forced single operations default to the original name */
2651 if (force_single)
2652 tmp_dest_dir = g_strdup (source);
2653 else if (get_other_type () == view_listing)
2654 tmp_dest_dir = vfs_path_to_str (other_panel->cwd_vpath);
2655 else
2656 tmp_dest_dir = vfs_path_to_str (panel->cwd_vpath);
2658 * Add trailing backslash only when do non-local ops.
2659 * It saves user from occasional file renames (when destination
2660 * dir is deleted)
2662 if (!force_single && tmp_dest_dir[0] != '\0'
2663 && tmp_dest_dir[strlen (tmp_dest_dir) - 1] != PATH_SEP)
2665 /* add trailing separator */
2666 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
2667 g_free (tmp_dest_dir);
2669 else
2671 /* just copy */
2672 dest_dir = tmp_dest_dir;
2674 if (dest_dir == NULL)
2676 ret_val = FALSE;
2677 goto ret_fast;
2680 /* Generate confirmation prompt */
2681 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2683 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2684 source != NULL ? (void *) source
2685 : (void *) &panel->marked, dest_dir, &do_bg);
2687 g_free (format);
2688 g_free (dest_dir);
2690 if (dest == NULL || dest[0] == '\0')
2692 g_free (dest);
2693 ret_val = FALSE;
2694 goto ret_fast;
2696 dest_vpath = vfs_path_from_str (dest);
2698 else if (confirm_delete)
2700 char *format;
2701 char fmd_buf[BUF_MEDIUM];
2703 /* Generate confirmation prompt */
2704 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2706 if (source == NULL)
2707 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2708 else
2710 const int fmd_xlen = 64;
2711 i = fmd_xlen - str_term_width1 (format) - 4;
2712 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2715 g_free (format);
2717 if (safe_delete)
2718 query_set_sel (1);
2720 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2722 if (i != 0)
2724 ret_val = FALSE;
2725 goto ret_fast;
2729 tctx = file_op_total_context_new ();
2730 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2733 filegui_dialog_type_t dialog_type;
2735 if (operation == OP_DELETE)
2736 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2737 else
2739 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2740 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2742 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2743 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2746 /* Background also need ctx->ui, but not full */
2747 if (do_bg)
2748 file_op_context_create_ui_without_init (ctx, TRUE, dialog_type);
2749 else
2750 file_op_context_create_ui (ctx, TRUE, dialog_type);
2753 #ifdef WITH_BACKGROUND
2754 /* Did the user select to do a background operation? */
2755 if (do_bg)
2757 int v;
2758 char *cwd_str;
2760 cwd_str = vfs_path_to_str (panel->cwd_vpath);
2761 v = do_background (ctx, g_strconcat (op_names[operation], ": ", cwd_str, (char *) NULL));
2762 g_free (cwd_str);
2763 if (v == -1)
2764 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2766 /* If we are the parent */
2767 if (v == 1)
2769 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
2771 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
2772 vfs_path_free (dest_vpath);
2773 g_free (dest);
2774 /* file_op_context_destroy (ctx); */
2775 return FALSE;
2778 #endif /* WITH_BACKGROUND */
2780 /* Initialize things */
2781 /* We do not want to trash cache every time file is
2782 created/touched. However, this will make our cache contain
2783 invalid data. */
2784 if ((dest != NULL) && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2785 save_dest = g_strdup (dest);
2787 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
2788 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, (void *) 1)))
2789 save_cwd = vfs_path_to_str (panel->cwd_vpath);
2791 /* Now, let's do the job */
2793 /* This code is only called by the tree and panel code */
2794 if (single_entry)
2796 /* We now have ETA in all cases */
2798 /* One file: FIXME mc_chdir will take user out of any vfs */
2799 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2801 vfs_path_t *vpath;
2802 int chdir_retcode;
2804 vpath = vfs_path_from_str (PATH_SEP_STR);
2805 chdir_retcode = mc_chdir (vpath);
2806 vfs_path_free (vpath);
2807 if (chdir_retcode < 0)
2809 ret_val = FALSE;
2810 goto clean_up;
2814 /* The source and src_stat variables have been initialized before */
2815 #ifdef WITH_FULL_PATHS
2816 if (g_path_is_absolute (source))
2817 source_with_vpath = vfs_path_from_str (source);
2818 else
2819 source_with_vpath = vfs_path_append_new (panel->cwd_vpath, source, (char *) NULL);
2820 source_with_path_str = vfs_path_to_str (source_with_vpath);
2821 #endif /* WITH_FULL_PATHS */
2822 if (panel_operate_init_totals (operation, panel, source_with_path_str, ctx) == FILE_CONT)
2824 if (operation == OP_DELETE)
2826 if (S_ISDIR (src_stat.st_mode))
2827 value = erase_dir (tctx, ctx, source_with_vpath);
2828 else
2829 value = erase_file (tctx, ctx, source_with_vpath);
2831 else
2833 temp = transform_source (ctx, source_with_path_str);
2834 if (temp == NULL)
2835 value = transform_error;
2836 else
2838 char *repl_dest, *temp2;
2840 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2841 temp2 = mc_build_filename (repl_dest, temp, NULL);
2842 g_free (temp);
2843 g_free (repl_dest);
2844 g_free (dest);
2845 vfs_path_free (dest_vpath);
2846 dest = temp2;
2847 dest_vpath = vfs_path_from_str (dest);
2849 switch (operation)
2851 case OP_COPY:
2852 /* we use file_mask_op_follow_links only with OP_COPY */
2853 ctx->stat_func (source_with_vpath, &src_stat);
2855 if (S_ISDIR (src_stat.st_mode))
2856 value = copy_dir_dir (tctx, ctx, source_with_path_str, dest,
2857 TRUE, FALSE, FALSE, NULL);
2858 else
2859 value = copy_file_file (tctx, ctx, source_with_path_str, dest);
2860 break;
2862 case OP_MOVE:
2863 if (S_ISDIR (src_stat.st_mode))
2864 value = move_dir_dir (tctx, ctx, source_with_path_str, dest);
2865 else
2866 value = move_file_file (tctx, ctx, source_with_path_str, dest);
2867 break;
2869 default:
2870 /* Unknown file operation */
2871 abort ();
2874 } /* Copy or move operation */
2876 if ((value == FILE_CONT) && !force_single)
2877 unmark_files (panel);
2880 else
2882 /* Many files */
2884 /* Check destination for copy or move operation */
2885 while (operation != OP_DELETE)
2887 int dst_result;
2888 struct stat dst_stat;
2890 dst_result = mc_stat (dest_vpath, &dst_stat);
2892 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2893 break;
2895 if (ctx->skip_all
2896 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2897 goto clean_up;
2900 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2902 /* Loop for every file, perform the actual copy operation */
2903 for (i = 0; i < panel->count; i++)
2905 const char *source2;
2907 if (!panel->dir.list[i].f.marked)
2908 continue; /* Skip the unmarked ones */
2910 source2 = panel->dir.list[i].fname;
2911 src_stat = panel->dir.list[i].st;
2913 #ifdef WITH_FULL_PATHS
2914 g_free (source_with_path_str);
2915 vfs_path_free (source_with_vpath);
2916 if (g_path_is_absolute (source2))
2917 source_with_vpath = vfs_path_from_str (source2);
2918 else
2919 source_with_vpath =
2920 vfs_path_append_new (panel->cwd_vpath, source2, (char *) NULL);
2921 source_with_path_str = vfs_path_to_str (source_with_vpath);
2922 #endif /* WITH_FULL_PATHS */
2924 if (operation == OP_DELETE)
2926 if (S_ISDIR (src_stat.st_mode))
2927 value = erase_dir (tctx, ctx, source_with_vpath);
2928 else
2929 value = erase_file (tctx, ctx, source_with_vpath);
2931 else
2933 temp = transform_source (ctx, source_with_path_str);
2935 if (temp == NULL)
2936 value = transform_error;
2937 else
2939 char *temp2, *temp3, *repl_dest;
2941 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2942 temp2 = mc_build_filename (repl_dest, temp, NULL);
2943 g_free (temp);
2944 g_free (repl_dest);
2945 temp3 = source_with_path_str;
2946 source_with_path_str = strutils_shell_unescape (source_with_path_str);
2947 g_free (temp3);
2948 temp3 = temp2;
2949 temp2 = strutils_shell_unescape (temp2);
2950 g_free (temp3);
2952 switch (operation)
2954 case OP_COPY:
2955 /* we use file_mask_op_follow_links only with OP_COPY */
2957 vfs_path_t *vpath;
2959 vpath = vfs_path_from_str (source_with_path_str);
2960 ctx->stat_func (vpath, &src_stat);
2961 vfs_path_free (vpath);
2963 if (S_ISDIR (src_stat.st_mode))
2964 value = copy_dir_dir (tctx, ctx, source_with_path_str, temp2,
2965 TRUE, FALSE, FALSE, NULL);
2966 else
2967 value = copy_file_file (tctx, ctx, source_with_path_str, temp2);
2968 dest_dirs = free_linklist (dest_dirs);
2969 break;
2971 case OP_MOVE:
2972 if (S_ISDIR (src_stat.st_mode))
2973 value = move_dir_dir (tctx, ctx, source_with_path_str, temp2);
2974 else
2975 value = move_file_file (tctx, ctx, source_with_path_str, temp2);
2976 break;
2978 default:
2979 /* Unknown file operation */
2980 abort ();
2983 g_free (temp2);
2985 } /* Copy or move operation */
2987 if (value == FILE_ABORT)
2988 break;
2990 if (value == FILE_CONT)
2991 do_file_mark (panel, i, 0);
2993 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2995 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2996 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
2999 if (operation != OP_DELETE)
3000 file_progress_show (ctx, 0, 0, "", FALSE);
3002 if (check_progress_buttons (ctx) == FILE_ABORT)
3003 break;
3005 mc_refresh ();
3006 } /* Loop for every file */
3008 } /* Many entries */
3010 clean_up:
3011 /* Clean up */
3012 if (save_cwd != NULL)
3014 tmp_vpath = vfs_path_from_str (save_cwd);
3015 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3016 vfs_path_free (tmp_vpath);
3017 g_free (save_cwd);
3020 if (save_dest != NULL)
3022 tmp_vpath = vfs_path_from_str (save_dest);
3023 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3024 vfs_path_free (tmp_vpath);
3025 g_free (save_dest);
3028 linklist = free_linklist (linklist);
3029 dest_dirs = free_linklist (dest_dirs);
3030 #ifdef WITH_FULL_PATHS
3031 g_free (source_with_path_str);
3032 vfs_path_free (source_with_vpath);
3033 #endif /* WITH_FULL_PATHS */
3034 g_free (dest);
3035 vfs_path_free (dest_vpath);
3036 g_free (ctx->dest_mask);
3037 ctx->dest_mask = NULL;
3039 #ifdef WITH_BACKGROUND
3040 /* Let our parent know we are saying bye bye */
3041 if (mc_global.we_are_background)
3043 int cur_pid = getpid ();
3044 /* Send pid to parent with child context, it is fork and
3045 don't modify real parent ctx */
3046 ctx->pid = cur_pid;
3047 parent_call ((void *) end_bg_process, ctx, 0);
3049 vfs_shut ();
3050 _exit (0);
3052 #endif /* WITH_BACKGROUND */
3054 file_op_total_context_destroy (tctx);
3055 ret_fast:
3056 file_op_context_destroy (ctx);
3057 g_free (source);
3059 return ret_val;
3062 /* }}} */
3064 /* --------------------------------------------------------------------------------------------- */
3065 /* {{{ Query/status report routines */
3066 /** Report error with one file */
3067 FileProgressStatus
3068 file_error (const char *format, const char *file)
3070 char buf[BUF_MEDIUM];
3072 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3074 return do_file_error (buf);
3077 /* --------------------------------------------------------------------------------------------- */
3080 Cause emacs to enter folding mode for this file:
3081 Local variables:
3082 end: