Cleanup of main.[ch].
[pantumic.git] / src / file.c
blob4d97fcd3e7c8a26f35dafc337cc47640ba628e52
1 /* File management.
2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1994, 1995 Janne Kukonlehto
6 1994, 1995 Fred Leeflang
7 1994, 1995, 1996 Miguel de Icaza
8 1995, 1996 Jakub Jelinek
9 1997 Norbert Warmuth
10 1998 Pavel Machek
12 The copy code was based in GNU's cp, and was written by:
13 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
15 The move code was based in GNU's mv, and was written by:
16 Mike Parker and David MacKenzie.
18 Janne Kukonlehto added much error recovery to them for being used
19 in an interactive program.
21 This program is free software; you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation; either version 2 of the License, or
24 (at your option) any later version.
26 This program is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
31 You should have received a copy of the GNU General Public License
32 along with this program; if not, write to the Free Software
33 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
36 * Please note that all dialogs used here must be safe for background
37 * operations.
40 /** \file file.c
41 * \brief Source: file management
44 /* {{{ Include files */
46 #include <config.h>
48 #include <ctype.h>
49 #include <errno.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <unistd.h>
56 #include <fcntl.h>
58 #include "lib/global.h"
59 #include "lib/tty/tty.h"
60 #include "lib/tty/key.h"
61 #include "lib/search.h"
62 #include "lib/strescape.h"
63 #include "lib/strutil.h"
64 #include "lib/util.h"
65 #include "lib/vfs/mc-vfs/vfs.h"
66 #include "lib/widget.h"
68 #include "setup.h"
69 #include "layout.h" /* rotate_dash() */
70 #include "background.h" /* we_are_background */
72 /* Needed for current_panel, other_panel and WTree */
73 #include "dir.h"
74 #include "filegui.h"
75 #include "tree.h"
76 #include "midnight.h" /* current_panel */
78 #include "file.h"
80 /* }}} */
82 /*** global variables ****************************************************************************/
84 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
85 const char *op_names[3] = {
86 N_("DialogTitle|Copy"),
87 N_("DialogTitle|Move"),
88 N_("DialogTitle|Delete")
91 /*** file scope macro definitions ****************************************************************/
93 /* Hack: the vfs code should not rely on this */
94 #define WITH_FULL_PATHS 1
96 #define FILEOP_UPDATE_INTERVAL 2
97 #define FILEOP_STALLING_INTERVAL 4
99 /*** file scope type declarations ****************************************************************/
101 /* This is a hard link cache */
102 struct link
104 struct link *next;
105 struct vfs_class *vfs;
106 dev_t dev;
107 ino_t ino;
108 short linkcount;
109 mode_t st_mode;
110 char name[1];
113 /* Status of the destination file */
114 typedef enum
116 DEST_NONE = 0, /* Not created */
117 DEST_SHORT = 1, /* Created, not fully copied */
118 DEST_FULL = 2 /* Created, fully copied */
119 } dest_status_t;
122 * This array introduced to avoid translation problems. The former (op_names)
123 * is assumed to be nouns, suitable in dialog box titles; this one should
124 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
125 * (I don't use spaces around the words, because someday they could be
126 * dropped, when widgets get smarter)
129 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
130 static const char *op_names1[] = {
131 N_("FileOperation|Copy"),
132 N_("FileOperation|Move"),
133 N_("FileOperation|Delete")
137 * These are formats for building a prompt. Parts encoded as follows:
138 * %o - operation from op_names1
139 * %f - file/files or files/directories, as appropriate
140 * %m - "with source mask" or question mark for delete
141 * %s - source name (truncated)
142 * %d - number of marked files
143 * %e - "to:" or question mark for delete
145 * xgettext:no-c-format */
146 static const char *one_format = N_("%o %f \"%s\"%m");
147 /* xgettext:no-c-format */
148 static const char *many_format = N_("%o %d %f%m");
150 static const char *prompt_parts[] = {
151 N_("file"),
152 N_("files"),
153 N_("directory"),
154 N_("directories"),
155 N_("files/directories"),
156 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
157 N_(" with source mask:"),
158 N_("to:")
161 static const char *question_format = N_("%s?");
163 /*** file scope variables ************************************************************************/
165 /* the hard link cache */
166 static struct link *linklist = NULL;
168 /* the files-to-be-erased list */
169 static struct link *erase_list;
172 * In copy_dir_dir we use two additional single linked lists: The first -
173 * variable name `parent_dirs' - holds information about already copied
174 * directories and is used to detect cyclic symbolic links.
175 * The second (`dest_dirs' below) holds information about just created
176 * target directories and is used to detect when an directory is copied
177 * into itself (we don't want to copy infinitly).
178 * Both lists don't use the linkcount and name structure members of struct
179 * link.
181 static struct link *dest_dirs = NULL;
183 static FileProgressStatus transform_error = FILE_CONT;
185 /*** file scope functions ************************************************************************/
186 /* --------------------------------------------------------------------------------------------- */
188 static FileProgressStatus query_replace (FileOpContext * ctx, const char *destname,
189 struct stat *_s_stat, struct stat *_d_stat);
190 static FileProgressStatus query_recursive (FileOpContext * ctx, const char *s);
191 static FileProgressStatus do_file_error (const char *str);
192 static FileProgressStatus erase_dir_iff_empty (FileOpContext * ctx, const char *s);
193 static FileProgressStatus erase_file (FileOpTotalContext * tctx, FileOpContext * ctx,
194 const char *s, gboolean is_toplevel_file);
195 static FileProgressStatus files_error (const char *format, const char *file1, const char *file2);
197 /* --------------------------------------------------------------------------------------------- */
199 static char *
200 transform_source (FileOpContext * ctx, const char *source)
202 char *s, *q;
203 char *fnsource;
205 s = g_strdup (source);
207 /* We remove \n from the filename since regex routines would use \n as an anchor */
208 /* this is just to be allowed to maniupulate file names with \n on it */
209 for (q = s; *q != '\0'; q++)
210 if (*q == '\n')
211 *q = ' ';
213 fnsource = (char *) x_basename (s);
215 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
216 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
217 else
219 q = NULL;
220 transform_error = FILE_SKIP;
223 g_free (s);
224 return q;
227 /* --------------------------------------------------------------------------------------------- */
229 static void
230 free_linklist (struct link **lc_linklist)
232 struct link *lp, *lp2;
234 for (lp = *lc_linklist; lp != NULL; lp = lp2)
236 lp2 = lp->next;
237 g_free (lp);
239 *lc_linklist = NULL;
242 /* --------------------------------------------------------------------------------------------- */
244 static int
245 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
247 ino_t ino = sb->st_ino;
248 dev_t dev = sb->st_dev;
249 struct vfs_class *vfs = vfs_get_class (path);
251 while (lp != NULL)
253 if (lp->vfs == vfs)
254 if (lp->ino == ino && lp->dev == dev)
255 return 1;
256 lp = lp->next;
258 return 0;
261 /* --------------------------------------------------------------------------------------------- */
263 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
264 * and a hardlink was succesfully made
267 static int
268 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
270 struct link *lp;
271 struct vfs_class *my_vfs = vfs_get_class (src_name);
272 ino_t ino = pstat->st_ino;
273 dev_t dev = pstat->st_dev;
274 struct stat link_stat;
275 const char *p;
277 if ((vfs_file_class_flags (src_name) & VFSF_NOLINKS) != 0)
278 return 0;
280 for (lp = linklist; lp != NULL; lp = lp->next)
281 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev)
283 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
284 && link_stat.st_dev == dev && vfs_get_class (lp->name) == my_vfs)
286 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
287 was copied to */
288 if (vfs_get_class (dst_name) == vfs_get_class (p))
290 if (!mc_stat (p, &link_stat))
292 if (!mc_link (p, dst_name))
293 return 1;
297 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
298 return 0;
300 lp = (struct link *) g_try_malloc (sizeof (struct link) + strlen (src_name)
301 + strlen (dst_name) + 1);
302 if (lp)
304 char *lpdstname;
305 lp->vfs = my_vfs;
306 lp->ino = ino;
307 lp->dev = dev;
308 strcpy (lp->name, src_name);
309 lpdstname = lp->name + strlen (lp->name) + 1;
310 strcpy (lpdstname, dst_name);
311 lp->next = linklist;
312 linklist = lp;
314 return 0;
317 /* --------------------------------------------------------------------------------------------- */
319 * Duplicate the contents of the symbolic link src_path in dst_path.
320 * Try to make a stable symlink if the option "stable symlink" was
321 * set in the file mask dialog.
322 * If dst_path is an existing symlink it will be deleted silently
323 * (upper levels take already care of existing files at dst_path).
326 static FileProgressStatus
327 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
329 char link_target[MC_MAXPATHLEN];
330 int len;
331 FileProgressStatus return_status;
332 struct stat sb;
333 gboolean dst_is_symlink;
335 dst_is_symlink = (mc_lstat (dst_path, &sb) == 0) && S_ISLNK (sb.st_mode);
337 retry_src_readlink:
338 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
339 if (len < 0)
341 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
342 if (return_status == FILE_RETRY)
343 goto retry_src_readlink;
344 return return_status;
346 link_target[len] = 0;
348 if (ctx->stable_symlinks)
349 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path))
351 message (D_ERROR, MSG_ERROR,
352 _("Cannot make stable symlinks across"
353 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
354 ctx->stable_symlinks = FALSE;
357 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
359 char *p, *q, *s;
361 const char *r = strrchr (src_path, PATH_SEP);
363 if (r)
365 p = g_strndup (src_path, r - src_path + 1);
366 if (g_path_is_absolute (dst_path))
367 q = g_strdup (dst_path);
368 else
369 q = g_strconcat (p, dst_path, (char *) NULL);
370 s = strrchr (q, PATH_SEP);
371 if (s)
373 s[1] = 0;
374 s = g_strconcat (p, link_target, (char *) NULL);
375 g_free (p);
376 g_strlcpy (link_target, s, sizeof (link_target));
377 g_free (s);
378 s = diff_two_paths (q, link_target);
379 if (s)
381 g_strlcpy (link_target, s, sizeof (link_target));
382 g_free (s);
385 else
386 g_free (p);
387 g_free (q);
390 retry_dst_symlink:
391 if (mc_symlink (link_target, dst_path) == 0)
392 /* Success */
393 return FILE_CONT;
395 * if dst_exists, it is obvious that this had failed.
396 * We can delete the old symlink and try again...
398 if (dst_is_symlink)
400 if (!mc_unlink (dst_path))
401 if (mc_symlink (link_target, dst_path) == 0)
402 /* Success */
403 return FILE_CONT;
405 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
406 if (return_status == FILE_RETRY)
407 goto retry_dst_symlink;
408 return return_status;
411 /* --------------------------------------------------------------------------------------------- */
413 static FileProgressStatus
414 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add,
415 gboolean is_toplevel_file)
417 struct timeval tv_current;
418 static struct timeval tv_start = { };
420 if (is_toplevel_file || ctx->progress_totals_computed)
422 tctx->progress_count++;
423 tctx->progress_bytes += add;
425 if (tv_start.tv_sec == 0)
427 gettimeofday (&tv_start, (struct timezone *) NULL);
429 gettimeofday (&tv_current, (struct timezone *) NULL);
430 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
432 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
433 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
434 tv_start.tv_sec = tv_current.tv_sec;
437 return check_progress_buttons (ctx);
440 /* --------------------------------------------------------------------------------------------- */
442 static FileProgressStatus
443 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
445 char *msg;
446 int result = 0;
447 const char *head_msg;
449 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
451 msg = g_strdup_printf (fmt, a, b);
452 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
453 g_free (msg);
454 do_refresh ();
456 return (result == 1) ? FILE_ABORT : FILE_SKIP;
459 /* --------------------------------------------------------------------------------------------- */
461 #ifdef WITH_BACKGROUND
462 static FileProgressStatus
463 warn_same_file (const char *fmt, const char *a, const char *b)
465 union
467 void *p;
468 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
469 const char *a, const char *b);
470 } pntr;
471 pntr.f = real_warn_same_file;
473 if (we_are_background)
474 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
475 else
476 return real_warn_same_file (Foreground, fmt, a, b);
478 #else
479 static FileProgressStatus
480 warn_same_file (const char *fmt, const char *a, const char *b)
482 return real_warn_same_file (Foreground, fmt, a, b);
484 #endif
486 /* --------------------------------------------------------------------------------------------- */
488 static void
489 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
490 struct timeval tv_current, struct timeval tv_transfer_start,
491 off_t file_size, off_t n_read_total)
493 long dt;
495 /* 1. Update rotating dash after some time */
496 rotate_dash ();
498 /* 3. Compute ETA */
499 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
501 if (n_read_total)
503 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
504 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
506 else
507 ctx->eta_secs = 0.0;
509 /* 4. Compute BPS rate */
510 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
511 if (ctx->bps_time < 1)
512 ctx->bps_time = 1;
513 ctx->bps = n_read_total / ctx->bps_time;
515 /* 5. Compute total ETA and BPS */
516 if (ctx->progress_bytes != 0)
518 double remain_bytes;
519 tctx->copyed_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
520 remain_bytes = ctx->progress_bytes - tctx->copyed_bytes;
521 #if 1
523 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
525 if (total_secs < 1)
526 total_secs = 1;
527 tctx->bps = tctx->copyed_bytes / total_secs;
528 tctx->eta_secs = remain_bytes / tctx->bps;
530 #else
531 /* broken on lot of little files */
532 tctx->bps_count++;
533 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
534 tctx->eta_secs = remain_bytes / tctx->bps;
535 #endif
539 /* --------------------------------------------------------------------------------------------- */
542 /* {{{ Move routines */
543 static FileProgressStatus
544 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
546 struct stat src_stats, dst_stats;
547 FileProgressStatus return_status = FILE_CONT;
548 gboolean copy_done = FALSE;
549 gboolean old_ask_overwrite;
551 file_progress_show_source (ctx, s);
552 file_progress_show_target (ctx, d);
553 if (check_progress_buttons (ctx) == FILE_ABORT)
554 return FILE_ABORT;
556 mc_refresh ();
558 while (mc_lstat (s, &src_stats) != 0)
560 /* Source doesn't exist */
561 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
562 if (return_status != FILE_RETRY)
563 return return_status;
566 if (mc_lstat (d, &dst_stats) == 0)
568 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
569 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
571 if (S_ISDIR (dst_stats.st_mode))
573 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
574 do_refresh ();
575 return FILE_SKIP;
578 if (confirm_overwrite)
580 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
581 if (return_status != FILE_CONT)
582 return return_status;
584 /* Ok to overwrite */
587 if (!ctx->do_append)
589 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
591 return_status = make_symlink (ctx, s, d);
592 if (return_status == FILE_CONT)
593 goto retry_src_remove;
594 else
595 return return_status;
598 if (mc_rename (s, d) == 0)
599 return progress_update_one (tctx, ctx, src_stats.st_size, TRUE);
601 #if 0
602 /* Comparison to EXDEV seems not to work in nfs if you're moving from
603 one nfs to the same, but on the server it is on two different
604 filesystems. Then nfs returns EIO instead of EXDEV.
605 Hope it will not hurt if we always in case of error try to copy/delete. */
606 else
607 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
609 if (errno != EXDEV)
611 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
612 if (return_status == FILE_RETRY)
613 goto retry_rename;
614 return return_status;
616 #endif
618 /* Failed because filesystem boundary -> copy the file instead */
619 old_ask_overwrite = tctx->ask_overwrite;
620 tctx->ask_overwrite = FALSE;
621 return_status = copy_file_file (tctx, ctx, s, d);
622 tctx->ask_overwrite = old_ask_overwrite;
623 if (return_status != FILE_CONT)
624 return return_status;
626 copy_done = TRUE;
628 file_progress_show_source (ctx, NULL);
629 file_progress_show (ctx, 0, 0, "", FALSE);
631 return_status = check_progress_buttons (ctx);
632 if (return_status != FILE_CONT)
633 return return_status;
635 mc_refresh ();
637 retry_src_remove:
638 if (mc_unlink (s))
640 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
641 if (return_status == FILE_RETRY)
642 goto retry_src_remove;
643 return return_status;
646 if (!copy_done)
647 return_status = progress_update_one (tctx, ctx, src_stats.st_size, TRUE);
649 return return_status;
652 /* }}} */
654 /* --------------------------------------------------------------------------------------------- */
655 /* {{{ Erase routines */
656 /** Don't update progress status if progress_count==NULL */
658 static FileProgressStatus
659 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s,
660 gboolean is_toplevel_file)
662 int return_status;
663 struct stat buf;
665 file_progress_show_deleting (ctx, s);
666 if (check_progress_buttons (ctx) == FILE_ABORT)
667 return FILE_ABORT;
668 mc_refresh ();
670 if (tctx->progress_count && mc_lstat (s, &buf))
672 /* ignore, most likely the mc_unlink fails, too */
673 buf.st_size = 0;
676 while (mc_unlink (s))
678 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
679 if (return_status != FILE_RETRY)
680 return return_status;
683 if (tctx->progress_count)
684 return progress_update_one (tctx, ctx, buf.st_size, is_toplevel_file);
685 else
686 return FILE_CONT;
689 /* --------------------------------------------------------------------------------------------- */
691 static FileProgressStatus
692 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
694 struct dirent *next;
695 struct stat buf;
696 DIR *reading;
697 char *path;
698 FileProgressStatus return_status = FILE_CONT;
700 if (!strcmp (s, ".."))
701 return FILE_RETRY;
703 reading = mc_opendir (s);
705 if (!reading)
706 return FILE_RETRY;
708 while ((next = mc_readdir (reading)) && return_status == FILE_CONT)
710 if (!strcmp (next->d_name, "."))
711 continue;
712 if (!strcmp (next->d_name, ".."))
713 continue;
714 path = concat_dir_and_file (s, next->d_name);
715 if (mc_lstat (path, &buf))
717 g_free (path);
718 mc_closedir (reading);
719 return FILE_RETRY;
721 if (S_ISDIR (buf.st_mode))
722 return_status =
723 (recursive_erase (tctx, ctx, path) != FILE_CONT) ? FILE_RETRY : FILE_CONT;
724 else
725 return_status = erase_file (tctx, ctx, path, 0);
726 g_free (path);
728 mc_closedir (reading);
729 if (return_status != FILE_CONT)
730 return return_status;
731 file_progress_show_deleting (ctx, s);
732 if (check_progress_buttons (ctx) == FILE_ABORT)
733 return FILE_ABORT;
734 mc_refresh ();
736 while (my_rmdir (s))
738 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
739 if (return_status != FILE_RETRY)
740 return return_status;
743 return FILE_CONT;
746 /* --------------------------------------------------------------------------------------------- */
747 /** Return -1 on error, 1 if there are no entries besides "." and ".."
748 in the directory path points to, 0 else. */
750 static int
751 check_dir_is_empty (const char *path)
753 DIR *dir;
754 struct dirent *d;
755 int i;
757 dir = mc_opendir (path);
758 if (!dir)
759 return -1;
761 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
763 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
764 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
765 continue; /* "." or ".." */
766 i = 0;
767 break;
770 mc_closedir (dir);
771 return i;
774 /* --------------------------------------------------------------------------------------------- */
776 static FileProgressStatus
777 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
779 FileProgressStatus error;
781 if (strcmp (s, "..") == 0)
782 return FILE_SKIP;
784 if (strcmp (s, ".") == 0)
785 return FILE_SKIP;
787 file_progress_show_deleting (ctx, s);
788 if (check_progress_buttons (ctx) == FILE_ABORT)
789 return FILE_ABORT;
790 mc_refresh ();
792 if (1 != check_dir_is_empty (s)) /* not empty or error */
793 return FILE_CONT;
795 while (my_rmdir (s))
797 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
798 if (error != FILE_RETRY)
799 return error;
802 return FILE_CONT;
805 /* }}} */
807 /* --------------------------------------------------------------------------------------------- */
808 /* {{{ Panel operate routines */
811 * Return currently selected entry name or the name of the first marked
812 * entry if there is one.
815 static char *
816 panel_get_file (WPanel * panel, struct stat *stat_buf)
818 int i;
820 if (get_current_type () == view_tree)
822 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
823 char *tree_name = tree_selected_name (tree);
825 mc_stat (tree_name, stat_buf);
826 return tree_name;
829 if (panel->marked)
831 for (i = 0; i < panel->count; i++)
832 if (panel->dir.list[i].f.marked)
834 *stat_buf = panel->dir.list[i].st;
835 return panel->dir.list[i].fname;
838 else
840 *stat_buf = panel->dir.list[panel->selected].st;
841 return panel->dir.list[panel->selected].fname;
843 g_assert_not_reached ();
844 return NULL;
847 /* --------------------------------------------------------------------------------------------- */
849 * panel_compute_totals:
851 * compute the number of files and the number of bytes
852 * used up by the whole selection, recursing directories
853 * as required. In addition, it checks to see if it will
854 * overwrite any files by doing the copy.
857 static FileProgressStatus
858 panel_compute_totals (const WPanel * panel, const void *ui,
859 compute_dir_size_callback cback,
860 off_t * ret_marked, double *ret_total, gboolean compute_symlinks)
862 int i;
864 *ret_marked = 0;
865 *ret_total = 0.0;
867 for (i = 0; i < panel->count; i++)
869 struct stat *s;
871 if (!panel->dir.list[i].f.marked)
872 continue;
874 s = &panel->dir.list[i].st;
876 if (S_ISDIR (s->st_mode))
878 char *dir_name;
879 off_t subdir_count = 0;
880 double subdir_bytes = 0;
881 FileProgressStatus status;
883 dir_name = concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
885 status = compute_dir_size (dir_name, ui, cback,
886 &subdir_count, &subdir_bytes, compute_symlinks);
887 g_free (dir_name);
889 if (status != FILE_CONT)
890 return FILE_ABORT;
892 *ret_marked += subdir_count;
893 *ret_total += subdir_bytes;
895 else
897 (*ret_marked)++;
898 *ret_total += s->st_size;
902 return FILE_CONT;
905 /* --------------------------------------------------------------------------------------------- */
906 /** Initialize variables for progress bars */
907 static FileProgressStatus
908 panel_operate_init_totals (FileOperation operation,
909 const WPanel * panel, const char *source, FileOpContext * ctx)
911 FileProgressStatus status;
913 if (operation != OP_MOVE && verbose && file_op_compute_totals)
915 ComputeDirSizeUI *ui;
917 ui = compute_dir_size_create_ui ();
919 if (source != NULL)
920 status = compute_dir_size (source, ui, compute_dir_size_update_ui,
921 &ctx->progress_count, &ctx->progress_bytes,
922 ctx->follow_links);
923 else
924 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
925 &ctx->progress_count, &ctx->progress_bytes,
926 ctx->follow_links);
928 compute_dir_size_destroy_ui (ui);
930 ctx->progress_totals_computed = (status == FILE_CONT);
932 else
934 status = FILE_CONT;
935 ctx->progress_count = panel->marked;
936 ctx->progress_bytes = panel->total;
937 ctx->progress_totals_computed = FALSE;
940 return status;
943 /* --------------------------------------------------------------------------------------------- */
945 * Generate user prompt for panel operation.
946 * single_source is the name if the source entry or NULL for multiple
947 * entries.
948 * src_stat is only used when single_source is not NULL.
951 static char *
952 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
953 gboolean single_source, const struct stat *src_stat)
955 const char *sp, *cp;
956 char format_string[BUF_MEDIUM];
957 char *dp = format_string;
958 gboolean build_question = FALSE;
960 static gboolean i18n_flag = FALSE;
961 if (!i18n_flag)
963 size_t i;
965 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
966 op_names1[i] = Q_ (op_names1[i]);
968 #ifdef ENABLE_NLS
969 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
970 prompt_parts[i] = _(prompt_parts[i]);
972 one_format = _(one_format);
973 many_format = _(many_format);
974 question_format = _(question_format);
975 #endif /* ENABLE_NLS */
976 i18n_flag = TRUE;
979 sp = single_source ? one_format : many_format;
981 while (*sp != '\0')
983 switch (*sp)
985 case '%':
986 cp = NULL;
987 switch (sp[1])
989 case 'o':
990 cp = op_names1[operation];
991 break;
992 case 'm':
993 if (operation == OP_DELETE)
995 cp = "";
996 build_question = TRUE;
998 else
999 cp = prompt_parts[5];
1000 break;
1001 case 'e':
1002 if (operation == OP_DELETE)
1004 cp = "";
1005 build_question = TRUE;
1007 else
1008 cp = prompt_parts[6];
1009 break;
1010 case 'f':
1011 if (single_source)
1012 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1013 else
1014 cp = (panel->marked == panel->dirs_marked)
1015 ? prompt_parts[3]
1016 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1017 break;
1018 default:
1019 *dp++ = *sp++;
1022 if (cp != NULL)
1024 sp += 2;
1025 while (*cp != '\0')
1026 *dp++ = *cp++;
1028 break;
1029 default:
1030 *dp++ = *sp++;
1033 *dp = '\0';
1035 if (build_question)
1037 char tmp[BUF_MEDIUM];
1039 memmove (tmp, format_string, sizeof (tmp));
1040 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1043 return g_strdup (format_string);
1046 /* --------------------------------------------------------------------------------------------- */
1048 #ifdef WITH_BACKGROUND
1049 static int
1050 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1052 int pid = ctx->pid;
1054 (void) mode;
1055 ctx->pid = 0;
1057 unregister_task_with_pid (pid);
1058 /* file_op_context_destroy(ctx); */
1059 return 1;
1061 #endif
1062 /* }}} */
1064 /* --------------------------------------------------------------------------------------------- */
1065 /* {{{ Query/status report routines */
1067 static FileProgressStatus
1068 real_do_file_error (enum OperationMode mode, const char *error)
1070 int result;
1071 const char *msg;
1073 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
1074 result = query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"), _("&Abort"));
1076 switch (result)
1078 case 0:
1079 do_refresh ();
1080 return FILE_SKIP;
1082 case 1:
1083 do_refresh ();
1084 return FILE_RETRY;
1086 case 2:
1087 default:
1088 return FILE_ABORT;
1092 /* --------------------------------------------------------------------------------------------- */
1093 /** Report error with two files */
1095 static FileProgressStatus
1096 files_error (const char *format, const char *file1, const char *file2)
1098 char buf[BUF_MEDIUM];
1099 char *nfile1 = g_strdup (path_trunc (file1, 15));
1100 char *nfile2 = g_strdup (path_trunc (file2, 15));
1102 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
1104 g_free (nfile1);
1105 g_free (nfile2);
1107 return do_file_error (buf);
1110 /* --------------------------------------------------------------------------------------------- */
1112 static FileProgressStatus
1113 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
1115 gchar *text;
1117 if (ctx->recursive_result < RECURSIVE_ALWAYS)
1119 const char *msg = mode == Foreground
1120 ? _("\nDirectory not empty.\nDelete it recursively?")
1121 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
1122 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
1124 if (safe_delete)
1125 query_set_sel (1);
1127 ctx->recursive_result =
1128 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
1129 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
1131 if (ctx->recursive_result != RECURSIVE_ABORT)
1132 do_refresh ();
1133 g_free (text);
1136 switch (ctx->recursive_result)
1138 case RECURSIVE_YES:
1139 case RECURSIVE_ALWAYS:
1140 return FILE_CONT;
1142 case RECURSIVE_NO:
1143 case RECURSIVE_NEVER:
1144 return FILE_SKIP;
1146 case RECURSIVE_ABORT:
1147 default:
1148 return FILE_ABORT;
1152 /* --------------------------------------------------------------------------------------------- */
1154 #ifdef WITH_BACKGROUND
1155 static FileProgressStatus
1156 do_file_error (const char *str)
1158 union
1160 void *p;
1161 FileProgressStatus (*f) (enum OperationMode, const char *);
1162 } pntr;
1163 pntr.f = real_do_file_error;
1165 if (we_are_background)
1166 return parent_call (pntr.p, NULL, 1, strlen (str), str);
1167 else
1168 return real_do_file_error (Foreground, str);
1171 /* --------------------------------------------------------------------------------------------- */
1173 static FileProgressStatus
1174 query_recursive (FileOpContext * ctx, const char *s)
1176 union
1178 void *p;
1179 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
1180 } pntr;
1181 pntr.f = real_query_recursive;
1183 if (we_are_background)
1184 return parent_call (pntr.p, ctx, 1, strlen (s), s);
1185 else
1186 return real_query_recursive (ctx, Foreground, s);
1189 /* --------------------------------------------------------------------------------------------- */
1191 static FileProgressStatus
1192 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
1193 struct stat *_d_stat)
1195 union
1197 void *p;
1198 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
1199 struct stat *, struct stat *);
1200 } pntr;
1201 pntr.f = file_progress_real_query_replace;
1203 if (we_are_background)
1204 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
1205 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
1206 else
1207 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
1210 #else
1211 /* --------------------------------------------------------------------------------------------- */
1213 static FileProgressStatus
1214 do_file_error (const char *str)
1216 return real_do_file_error (Foreground, str);
1219 /* --------------------------------------------------------------------------------------------- */
1221 static FileProgressStatus
1222 query_recursive (FileOpContext * ctx, const char *s)
1224 return real_query_recursive (ctx, Foreground, s);
1227 /* --------------------------------------------------------------------------------------------- */
1229 static FileProgressStatus
1230 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
1231 struct stat *_d_stat)
1233 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
1236 #endif /* !WITH_BACKGROUND */
1237 /* }}} */
1239 /* --------------------------------------------------------------------------------------------- */
1240 /*** public functions ****************************************************************************/
1241 /* --------------------------------------------------------------------------------------------- */
1243 FileProgressStatus
1244 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1245 const char *src_path, const char *dst_path)
1247 uid_t src_uid = (uid_t) - 1;
1248 gid_t src_gid = (gid_t) - 1;
1250 int src_desc, dest_desc = -1;
1251 int n_read, n_written;
1252 mode_t src_mode = 0; /* The mode of the source file */
1253 struct stat sb, sb2;
1254 struct utimbuf utb;
1255 gboolean dst_exists = FALSE, appending = FALSE;
1256 off_t n_read_total = 0, file_size = -1;
1257 FileProgressStatus return_status, temp_status;
1258 struct timeval tv_transfer_start;
1259 dest_status_t dst_status = DEST_NONE;
1260 int open_flags;
1261 gboolean is_first_time = TRUE;
1263 /* FIXME: We should not be using global variables! */
1264 ctx->do_reget = 0;
1265 return_status = FILE_RETRY;
1267 file_progress_show_source (ctx, src_path);
1268 file_progress_show_target (ctx, dst_path);
1269 if (check_progress_buttons (ctx) == FILE_ABORT)
1270 return FILE_ABORT;
1272 mc_refresh ();
1274 while (mc_stat (dst_path, &sb2) == 0)
1276 if (S_ISDIR (sb2.st_mode))
1278 return_status = file_error (_("Cannot overwrite directory\"%s\"\n%s"), dst_path);
1279 if (return_status == FILE_RETRY)
1280 continue;
1281 return return_status;
1283 dst_exists = TRUE;
1284 break;
1287 while ((*ctx->stat_func) (src_path, &sb))
1289 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1290 if (return_status != FILE_RETRY)
1291 return return_status;
1294 if (dst_exists)
1296 /* Destination already exists */
1297 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1298 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), src_path, dst_path);
1299 /* Should we replace destination? */
1300 if (tctx->ask_overwrite)
1302 ctx->do_reget = 0;
1303 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1304 if (return_status != FILE_CONT)
1305 return return_status;
1309 if (!ctx->do_append)
1311 /* Check the hardlinks */
1312 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb) == 1)
1314 /* We have made a hardlink - no more processing is necessary */
1315 return FILE_CONT;
1318 if (S_ISLNK (sb.st_mode))
1319 return make_symlink (ctx, src_path, dst_path);
1321 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1322 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1324 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0)
1326 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1327 if (return_status == FILE_RETRY)
1328 continue;
1329 return return_status;
1331 /* Success */
1333 while (ctx->preserve_uidgid && mc_chown (dst_path, sb.st_uid, sb.st_gid))
1335 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1336 if (temp_status == FILE_RETRY)
1337 continue;
1338 return temp_status;
1340 while (ctx->preserve && mc_chmod (dst_path, sb.st_mode & ctx->umask_kill))
1342 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1343 if (temp_status == FILE_RETRY)
1344 continue;
1345 return temp_status;
1347 return FILE_CONT;
1351 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1353 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0)
1355 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1356 if (return_status == FILE_RETRY)
1357 continue;
1358 ctx->do_append = 0;
1359 return return_status;
1362 if (ctx->do_reget != 0)
1364 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1366 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1367 ctx->do_reget = 0;
1368 ctx->do_append = FALSE;
1372 while (mc_fstat (src_desc, &sb))
1374 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1375 if (return_status == FILE_RETRY)
1376 continue;
1377 ctx->do_append = FALSE;
1378 goto ret;
1380 src_mode = sb.st_mode;
1381 src_uid = sb.st_uid;
1382 src_gid = sb.st_gid;
1383 utb.actime = sb.st_atime;
1384 utb.modtime = sb.st_mtime;
1385 file_size = sb.st_size;
1387 open_flags = O_WRONLY;
1388 if (dst_exists)
1390 if (ctx->do_append != 0)
1391 open_flags |= O_APPEND;
1392 else
1393 open_flags |= O_CREAT | O_TRUNC;
1395 else
1397 open_flags |= O_CREAT | O_EXCL;
1400 while ((dest_desc = mc_open (dst_path, open_flags, src_mode)) < 0)
1402 if (errno == EEXIST)
1403 goto ret;
1405 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1406 if (return_status == FILE_RETRY)
1407 continue;
1408 ctx->do_append = FALSE;
1409 goto ret;
1411 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1413 appending = ctx->do_append;
1414 ctx->do_append = FALSE;
1416 /* Find out the optimal buffer size. */
1417 while (mc_fstat (dest_desc, &sb))
1419 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1420 if (return_status == FILE_RETRY)
1421 continue;
1422 goto ret;
1425 ctx->eta_secs = 0.0;
1426 ctx->bps = 0;
1428 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1429 file_progress_show (ctx, 0, file_size, "", TRUE);
1430 else
1431 file_progress_show (ctx, 1, 1, "", TRUE);
1432 return_status = check_progress_buttons (ctx);
1433 mc_refresh ();
1435 if (return_status != FILE_CONT)
1436 goto ret;
1439 struct timeval tv_current, tv_last_update, tv_last_input;
1440 int secs, update_secs;
1441 const char *stalled_msg = "";
1443 tv_last_update = tv_transfer_start;
1445 for (;;)
1447 char buf[BUF_8K];
1449 /* src_read */
1450 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1451 n_read = -1;
1452 else
1453 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0)
1455 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1456 if (return_status == FILE_RETRY)
1457 continue;
1458 goto ret;
1460 if (n_read == 0)
1461 break;
1463 gettimeofday (&tv_current, NULL);
1465 if (n_read > 0)
1467 char *t = buf;
1468 n_read_total += n_read;
1470 /* Windows NT ftp servers report that files have no
1471 * permissions: -------, so if we happen to have actually
1472 * read something, we should fix the permissions.
1474 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1475 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1476 gettimeofday (&tv_last_input, NULL);
1478 /* dst_write */
1479 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read)
1481 if (n_written > 0)
1483 n_read -= n_written;
1484 t += n_written;
1485 continue;
1487 return_status = file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1488 if (return_status != FILE_RETRY)
1489 goto ret;
1492 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1493 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1495 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1497 copy_file_file_display_progress (tctx, ctx,
1498 tv_current,
1499 tv_transfer_start, file_size, n_read_total);
1500 tv_last_update = tv_current;
1502 is_first_time = FALSE;
1504 if (update_secs > FILEOP_STALLING_INTERVAL)
1506 stalled_msg = _("(stalled)");
1510 gboolean force_update =
1511 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1512 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1513 file_progress_show_total (tctx, ctx,
1514 tctx->progress_bytes + n_read_total + ctx->do_reget,
1515 force_update);
1518 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1519 force_update);
1521 mc_refresh ();
1523 return_status = check_progress_buttons (ctx);
1525 if (return_status != FILE_CONT)
1527 mc_refresh ();
1528 goto ret;
1533 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1535 ret:
1536 while (src_desc != -1 && mc_close (src_desc) < 0)
1538 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1539 if (temp_status == FILE_RETRY)
1540 continue;
1541 if (temp_status == FILE_ABORT)
1542 return_status = temp_status;
1543 break;
1546 while (dest_desc != -1 && mc_close (dest_desc) < 0)
1548 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1549 if (temp_status == FILE_RETRY)
1550 continue;
1551 return_status = temp_status;
1552 break;
1555 if (dst_status == DEST_SHORT)
1557 /* Remove short file */
1558 int result;
1559 result = query_dialog (Q_ ("DialogTitle|Copy"),
1560 _("Incomplete file was retrieved. Keep it?"),
1561 D_ERROR, 2, _("&Delete"), _("&Keep"));
1562 if (result == 0)
1563 mc_unlink (dst_path);
1565 else if (dst_status == DEST_FULL)
1567 /* Copy has succeeded */
1568 if (!appending && ctx->preserve_uidgid)
1570 while (mc_chown (dst_path, src_uid, src_gid))
1572 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1573 if (temp_status == FILE_RETRY)
1574 continue;
1575 return_status = temp_status;
1576 break;
1580 if (!appending)
1582 if (ctx->preserve)
1584 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill)))
1586 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1587 if (temp_status != FILE_RETRY)
1589 return_status = temp_status;
1590 break;
1594 else
1596 src_mode = umask (-1);
1597 umask (src_mode);
1598 src_mode = 0100666 & ~src_mode;
1599 mc_chmod (dst_path, (src_mode & ctx->umask_kill));
1601 mc_utime (dst_path, &utb);
1605 if (return_status == FILE_CONT)
1606 return_status = progress_update_one (tctx, ctx, file_size, tctx->is_toplevel_file);
1608 return return_status;
1611 /* --------------------------------------------------------------------------------------------- */
1613 * I think these copy_*_* functions should have a return type.
1614 * anyway, this function *must* have two directories as arguments.
1616 /* FIXME: This function needs to check the return values of the
1617 function calls */
1619 FileProgressStatus
1620 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1621 gboolean toplevel, gboolean move_over, gboolean do_delete, struct link * parent_dirs)
1623 struct dirent *next;
1624 struct stat buf, cbuf;
1625 DIR *reading;
1626 char *dest_dir = NULL;
1627 FileProgressStatus return_status = FILE_CONT;
1628 struct utimbuf utb;
1629 struct link *lp;
1630 char *d;
1632 d = g_strdup (_d);
1634 /* First get the mode of the source dir */
1635 retry_src_stat:
1636 if ((*ctx->stat_func) (s, &cbuf))
1638 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1639 if (return_status == FILE_RETRY)
1640 goto retry_src_stat;
1641 goto ret_fast;
1644 if (is_in_linklist (dest_dirs, s, &cbuf))
1646 /* Don't copy a directory we created before (we don't want to copy
1647 infinitely if a directory is copied into itself) */
1648 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1649 return_status = FILE_CONT;
1650 goto ret_fast;
1653 /* Hmm, hardlink to directory??? - Norbert */
1654 /* FIXME: In this step we should do something
1655 in case the destination already exist */
1656 /* Check the hardlinks */
1657 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf) == 1)
1659 /* We have made a hardlink - no more processing is necessary */
1660 goto ret_fast;
1663 if (!S_ISDIR (cbuf.st_mode))
1665 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1666 if (return_status == FILE_RETRY)
1667 goto retry_src_stat;
1668 goto ret_fast;
1671 if (is_in_linklist (parent_dirs, s, &cbuf))
1673 /* we found a cyclic symbolic link */
1674 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
1675 return_status = FILE_SKIP;
1676 goto ret_fast;
1679 lp = g_new (struct link, 1);
1680 lp->vfs = vfs_get_class (s);
1681 lp->ino = cbuf.st_ino;
1682 lp->dev = cbuf.st_dev;
1683 lp->next = parent_dirs;
1684 parent_dirs = lp;
1686 retry_dst_stat:
1687 /* Now, check if the dest dir exists, if not, create it. */
1688 if (mc_stat (d, &buf))
1690 /* Here the dir doesn't exist : make it ! */
1691 if (move_over)
1693 if (mc_rename (s, d) == 0)
1695 return_status = FILE_CONT;
1696 goto ret;
1699 dest_dir = d;
1700 d = NULL;
1702 else
1705 * If the destination directory exists, we want to copy the whole
1706 * directory, but we only want this to happen once.
1708 * Escape sequences added to the * to compiler warnings.
1709 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
1710 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
1712 if (!S_ISDIR (buf.st_mode))
1714 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
1715 if (return_status == FILE_RETRY)
1716 goto retry_dst_stat;
1717 goto ret;
1719 /* Dive into subdir if exists */
1720 if (toplevel && ctx->dive_into_subdirs)
1722 dest_dir = concat_dir_and_file (d, x_basename (s));
1724 else
1726 dest_dir = d;
1727 d = NULL;
1728 goto dont_mkdir;
1731 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU))
1733 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
1734 if (return_status != FILE_RETRY)
1735 goto ret;
1738 lp = g_new (struct link, 1);
1739 mc_stat (dest_dir, &buf);
1740 lp->vfs = vfs_get_class (dest_dir);
1741 lp->ino = buf.st_ino;
1742 lp->dev = buf.st_dev;
1743 lp->next = dest_dirs;
1744 dest_dirs = lp;
1746 if (ctx->preserve_uidgid)
1748 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid))
1750 return_status = file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
1751 if (return_status != FILE_RETRY)
1752 goto ret;
1756 dont_mkdir:
1757 /* open the source dir for reading */
1758 reading = mc_opendir (s);
1759 if (reading == NULL)
1760 goto ret;
1762 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1764 char *path;
1766 * Now, we don't want '.' and '..' to be created / copied at any time
1768 if (!strcmp (next->d_name, "."))
1769 continue;
1770 if (!strcmp (next->d_name, ".."))
1771 continue;
1773 /* get the filename and add it to the src directory */
1774 path = concat_dir_and_file (s, next->d_name);
1776 (*ctx->stat_func) (path, &buf);
1777 if (S_ISDIR (buf.st_mode))
1779 char *mdpath;
1781 mdpath = concat_dir_and_file (dest_dir, next->d_name);
1783 * From here, we just intend to recursively copy subdirs, not
1784 * the double functionality of copying different when the target
1785 * dir already exists. So, we give the recursive call the flag 0
1786 * meaning no toplevel.
1788 return_status =
1789 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
1790 g_free (mdpath);
1792 else
1794 char *dest_file;
1796 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
1797 return_status = copy_file_file (tctx, ctx, path, dest_file);
1798 g_free (dest_file);
1800 if (do_delete && return_status == FILE_CONT)
1802 if (ctx->erase_at_end)
1804 static struct link *tail;
1805 size_t len = strlen (path);
1806 lp = g_malloc (sizeof (struct link) + len);
1807 strncpy (lp->name, path, len + 1);
1808 lp->st_mode = buf.st_mode;
1809 lp->next = NULL;
1810 if (erase_list != NULL)
1812 tail->next = lp;
1813 tail = lp;
1815 else
1816 erase_list = tail = lp;
1818 else
1820 if (S_ISDIR (buf.st_mode))
1822 return_status = erase_dir_iff_empty (ctx, path);
1824 else
1825 return_status = erase_file (tctx, ctx, path, FALSE);
1828 g_free (path);
1830 mc_closedir (reading);
1832 if (ctx->preserve)
1834 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1835 utb.actime = cbuf.st_atime;
1836 utb.modtime = cbuf.st_mtime;
1837 mc_utime (dest_dir, &utb);
1839 else
1841 cbuf.st_mode = umask (-1);
1842 umask (cbuf.st_mode);
1843 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
1844 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1847 ret:
1848 g_free (dest_dir);
1849 g_free (parent_dirs);
1850 ret_fast:
1851 g_free (d);
1852 return return_status;
1855 /* }}} */
1857 /* --------------------------------------------------------------------------------------------- */
1858 /* {{{ Move routines */
1860 FileProgressStatus
1861 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
1863 struct stat sbuf, dbuf, destbuf;
1864 struct link *lp;
1865 char *destdir;
1866 FileProgressStatus return_status;
1867 gboolean move_over = FALSE;
1868 gboolean dstat_ok;
1870 file_progress_show_source (ctx, s);
1871 file_progress_show_target (ctx, d);
1872 if (check_progress_buttons (ctx) == FILE_ABORT)
1873 return FILE_ABORT;
1875 mc_refresh ();
1877 mc_stat (s, &sbuf);
1878 dstat_ok = (mc_stat (d, &dbuf) == 0);
1880 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
1881 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
1883 if (!dstat_ok)
1884 destdir = g_strdup (d); /* destination doesn't exist */
1885 else if (!ctx->dive_into_subdirs)
1887 destdir = g_strdup (d);
1888 move_over = TRUE;
1890 else
1891 destdir = concat_dir_and_file (d, x_basename (s));
1893 /* Check if the user inputted an existing dir */
1894 retry_dst_stat:
1895 if (!mc_stat (destdir, &destbuf))
1897 if (move_over)
1899 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
1901 if (return_status != FILE_CONT)
1902 goto ret;
1903 goto oktoret;
1905 else
1907 if (S_ISDIR (destbuf.st_mode))
1908 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
1909 else
1910 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
1911 if (return_status == FILE_RETRY)
1912 goto retry_dst_stat;
1914 g_free (destdir);
1915 return return_status;
1918 retry_rename:
1919 if (mc_rename (s, destdir) == 0)
1921 return_status = FILE_CONT;
1922 goto ret;
1925 if (errno != EXDEV)
1927 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
1928 if (return_status == FILE_RETRY)
1929 goto retry_rename;
1930 goto ret;
1932 /* Failed because of filesystem boundary -> copy dir instead */
1933 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
1935 if (return_status != FILE_CONT)
1936 goto ret;
1937 oktoret:
1938 file_progress_show_source (ctx, NULL);
1939 file_progress_show (ctx, 0, 0, "", FALSE);
1941 return_status = check_progress_buttons (ctx);
1942 if (return_status != FILE_CONT)
1943 goto ret;
1945 mc_refresh ();
1946 if (ctx->erase_at_end)
1948 for (; erase_list && return_status != FILE_ABORT;)
1950 if (S_ISDIR (erase_list->st_mode))
1952 return_status = erase_dir_iff_empty (ctx, erase_list->name);
1954 else
1955 return_status = erase_file (tctx, ctx, erase_list->name, FALSE);
1956 lp = erase_list;
1957 erase_list = erase_list->next;
1958 g_free (lp);
1961 erase_dir_iff_empty (ctx, s);
1963 ret:
1964 g_free (destdir);
1965 while (erase_list)
1967 lp = erase_list;
1968 erase_list = erase_list->next;
1969 g_free (lp);
1971 return return_status;
1974 /* }}} */
1976 /* --------------------------------------------------------------------------------------------- */
1977 /* {{{ Erase routines */
1979 FileProgressStatus
1980 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
1982 FileProgressStatus error;
1984 if (strcmp (s, "..") == 0)
1985 return FILE_SKIP;
1987 if (strcmp (s, ".") == 0)
1988 return FILE_SKIP;
1990 file_progress_show_deleting (ctx, s);
1991 if (check_progress_buttons (ctx) == FILE_ABORT)
1992 return FILE_ABORT;
1993 mc_refresh ();
1995 /* The old way to detect a non empty directory was:
1996 error = my_rmdir (s);
1997 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1998 For the linux user space nfs server (nfs-server-2.2beta29-2)
1999 we would have to check also for EIO. I hope the new way is
2000 fool proof. (Norbert)
2002 error = check_dir_is_empty (s);
2003 if (error == 0)
2004 { /* not empty */
2005 error = query_recursive (ctx, s);
2006 if (error == FILE_CONT)
2007 return recursive_erase (tctx, ctx, s);
2008 else
2009 return error;
2012 while (my_rmdir (s) == -1)
2014 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2015 if (error != FILE_RETRY)
2016 return error;
2019 return FILE_CONT;
2022 /* }}} */
2024 /* --------------------------------------------------------------------------------------------- */
2025 /* {{{ Panel operate routines */
2027 ComputeDirSizeUI *
2028 compute_dir_size_create_ui (void)
2030 ComputeDirSizeUI *ui;
2032 const char *b_name = N_("&Abort");
2034 #ifdef ENABLE_NLS
2035 b_name = _(b_name);
2036 #endif
2038 ui = g_new (ComputeDirSizeUI, 1);
2040 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
2041 NULL, _("Directory scanning"), DLG_CENTER);
2042 ui->dirname = label_new (3, 3, "");
2043 add_widget (ui->dlg, ui->dirname);
2045 add_widget (ui->dlg,
2046 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
2047 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2049 /* We will manage the dialog without any help,
2050 that's why we have to call init_dlg */
2051 init_dlg (ui->dlg);
2053 return ui;
2056 /* --------------------------------------------------------------------------------------------- */
2058 void
2059 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2061 if (ui != NULL)
2063 /* schedule to update passive panel */
2064 other_panel->dirty = 1;
2066 /* close and destroy dialog */
2067 dlg_run_done (ui->dlg);
2068 destroy_dlg (ui->dlg);
2069 g_free (ui);
2073 /* --------------------------------------------------------------------------------------------- */
2075 FileProgressStatus
2076 compute_dir_size_update_ui (const void *ui, const char *dirname)
2078 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2079 int c;
2080 Gpm_Event event;
2082 if (ui == NULL)
2083 return FILE_CONT;
2085 label_set_text (this->dirname, str_trunc (dirname, this->dlg->cols - 6));
2087 event.x = -1; /* Don't show the GPM cursor */
2088 c = tty_get_event (&event, FALSE, FALSE);
2089 if (c == EV_NONE)
2090 return FILE_CONT;
2092 /* Reinitialize to avoid old values after events other than
2093 selecting a button */
2094 this->dlg->ret_value = FILE_CONT;
2096 dlg_process_event (this->dlg, c, &event);
2098 switch (this->dlg->ret_value)
2100 case B_CANCEL:
2101 case FILE_ABORT:
2102 return FILE_ABORT;
2103 default:
2104 return FILE_CONT;
2108 /* --------------------------------------------------------------------------------------------- */
2110 * compute_dir_size:
2112 * Computes the number of bytes used by the files in a directory
2115 FileProgressStatus
2116 compute_dir_size (const char *dirname, const void *ui,
2117 compute_dir_size_callback cback,
2118 off_t * ret_marked, double *ret_total, gboolean compute_symlinks)
2120 int res;
2121 struct stat s;
2122 DIR *dir;
2123 struct dirent *dirent;
2124 FileProgressStatus ret = FILE_CONT;
2126 if (!compute_symlinks)
2128 res = mc_lstat (dirname, &s);
2129 if (res != 0)
2130 return ret;
2132 /* don't scan symlink to directory */
2133 if (S_ISLNK (s.st_mode))
2135 (*ret_marked)++;
2136 *ret_total += s.st_size;
2137 return ret;
2141 dir = mc_opendir (dirname);
2143 if (dir == NULL)
2144 return ret;
2146 while ((dirent = mc_readdir (dir)) != NULL)
2148 char *fullname;
2150 ret = (cback != NULL) ? cback (ui, dirname) : FILE_CONT;
2152 if (ret != FILE_CONT)
2153 break;
2155 if (strcmp (dirent->d_name, ".") == 0)
2156 continue;
2157 if (strcmp (dirent->d_name, "..") == 0)
2158 continue;
2160 fullname = concat_dir_and_file (dirname, dirent->d_name);
2161 res = mc_lstat (fullname, &s);
2163 if (res != 0)
2165 g_free (fullname);
2166 continue;
2169 if (S_ISDIR (s.st_mode))
2171 off_t subdir_count = 0;
2172 double subdir_bytes = 0;
2174 ret =
2175 compute_dir_size (fullname, ui, cback, &subdir_count, &subdir_bytes,
2176 compute_symlinks);
2178 if (ret != FILE_CONT)
2180 g_free (fullname);
2181 break;
2184 *ret_marked += subdir_count;
2185 *ret_total += subdir_bytes;
2187 else
2189 (*ret_marked)++;
2190 *ret_total += s.st_size;
2193 g_free (fullname);
2196 mc_closedir (dir);
2198 return ret;
2201 /* --------------------------------------------------------------------------------------------- */
2203 * panel_operate:
2205 * Performs one of the operations on the selection on the source_panel
2206 * (copy, delete, move).
2208 * Returns TRUE if did change the directory
2209 * structure, Returns FALSE if user aborted
2211 * force_single forces operation on the current entry and affects
2212 * default destination. Current filename is used as default.
2215 gboolean
2216 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2218 WPanel *panel = (WPanel *) source_panel;
2219 const gboolean single_entry = force_single || (panel->marked <= 1)
2220 || (get_current_type () == view_tree);
2222 char *source = NULL;
2223 #ifdef WITH_FULL_PATHS
2224 char *source_with_path = NULL;
2225 #else
2226 #define source_with_path source
2227 #endif /* !WITH_FULL_PATHS */
2228 char *dest = NULL;
2229 char *temp = NULL;
2230 char *save_cwd = NULL, *save_dest = NULL;
2231 struct stat src_stat;
2232 gboolean ret_val = TRUE;
2233 int i;
2234 FileProgressStatus value;
2235 FileOpContext *ctx;
2236 FileOpTotalContext *tctx;
2238 gboolean do_bg = FALSE; /* do background operation? */
2240 static gboolean i18n_flag = FALSE;
2241 if (!i18n_flag)
2243 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2244 op_names[i] = Q_ (op_names[i]);
2245 i18n_flag = TRUE;
2248 free_linklist (&linklist);
2249 free_linklist (&dest_dirs);
2251 /* Update panel contents to avoid actions on deleted files */
2252 if (!panel->is_panelized)
2254 update_panels (UP_RELOAD, UP_KEEPSEL);
2255 repaint_screen ();
2258 if (single_entry)
2260 if (force_single)
2262 source = selection (panel)->fname;
2263 src_stat = selection (panel)->st;
2265 else
2266 source = panel_get_file (panel, &src_stat);
2268 if (!strcmp (source, ".."))
2270 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2271 return FALSE;
2275 ctx = file_op_context_new (operation);
2276 tctx = file_op_total_context_new ();
2277 gettimeofday (&(tctx->transfer_start), (struct timezone *) NULL);
2279 /* Show confirmation dialog */
2280 if (operation != OP_DELETE)
2282 char *dest_dir;
2283 char *dest_dir_;
2284 char *format;
2286 /* Forced single operations default to the original name */
2287 if (force_single)
2288 dest_dir = source;
2289 else if (get_other_type () == view_listing)
2290 dest_dir = other_panel->cwd;
2291 else
2292 dest_dir = panel->cwd;
2294 * Add trailing backslash only when do non-local ops.
2295 * It saves user from occasional file renames (when destination
2296 * dir is deleted)
2298 if (!force_single && dest_dir[0] != '\0' && dest_dir[strlen (dest_dir) - 1] != PATH_SEP)
2300 /* add trailing separator */
2301 dest_dir_ = g_strconcat (dest_dir, PATH_SEP_STR, (char *) NULL);
2303 else
2305 /* just copy */
2306 dest_dir_ = g_strdup (dest_dir);
2308 if (dest_dir_ == NULL)
2310 file_op_total_context_destroy (tctx);
2311 file_op_context_destroy (ctx);
2312 return FALSE;
2315 /* Generate confirmation prompt */
2316 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2318 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2319 source != NULL ? (void *) source
2320 : (void *) &panel->marked, dest_dir_, &do_bg);
2322 g_free (format);
2323 g_free (dest_dir_);
2325 if (dest == NULL || dest[0] == '\0')
2327 file_op_total_context_destroy (tctx);
2328 file_op_context_destroy (ctx);
2329 g_free (dest);
2330 return FALSE;
2333 else if (confirm_delete)
2335 char *format;
2336 char fmd_buf[BUF_MEDIUM];
2338 /* Generate confirmation prompt */
2339 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2341 if (source == NULL)
2342 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2343 else
2345 const int fmd_xlen = 64;
2346 i = fmd_xlen - str_term_width1 (format) - 4;
2347 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2350 g_free (format);
2352 if (safe_delete)
2353 query_set_sel (1);
2355 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2357 if (i != 0)
2359 file_op_total_context_destroy (tctx);
2360 file_op_context_destroy (ctx);
2361 return FALSE;
2366 filegui_dialog_type_t dialog_type;
2368 if (operation == OP_DELETE)
2369 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2370 else
2372 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2373 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2375 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2376 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2379 /* Background also need ctx->ui, but not full */
2380 if (do_bg)
2381 file_op_context_create_ui_without_init (ctx, 1, dialog_type);
2382 else
2383 file_op_context_create_ui (ctx, 1, dialog_type);
2386 #ifdef WITH_BACKGROUND
2387 /* Did the user select to do a background operation? */
2388 if (do_bg)
2390 int v;
2392 v = do_background (ctx, g_strconcat (op_names[operation], ": ", panel->cwd, (char *) NULL));
2393 if (v == -1)
2394 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2396 /* If we are the parent */
2397 if (v == 1)
2399 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
2400 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
2401 /* file_op_context_destroy (ctx); */
2402 return FALSE;
2405 #endif /* WITH_BACKGROUND */
2407 /* Initialize things */
2408 /* We do not want to trash cache every time file is
2409 created/touched. However, this will make our cache contain
2410 invalid data. */
2411 if ((dest != NULL) && (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1)))
2412 save_dest = g_strdup (dest);
2414 if ((panel->cwd[0] != '\0') && (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1)))
2415 save_cwd = g_strdup (panel->cwd);
2417 /* Now, let's do the job */
2419 /* This code is only called by the tree and panel code */
2420 if (single_entry)
2422 /* We now have ETA in all cases */
2424 /* One file: FIXME mc_chdir will take user out of any vfs */
2425 if ((operation != OP_COPY) && (get_current_type () == view_tree) &&
2426 (mc_chdir (PATH_SEP_STR) < 0))
2428 ret_val = FALSE;
2429 goto clean_up;
2432 /* The source and src_stat variables have been initialized before */
2433 #ifdef WITH_FULL_PATHS
2434 source_with_path = concat_dir_and_file (panel->cwd, source);
2435 #endif /* WITH_FULL_PATHS */
2437 if (panel_operate_init_totals (operation, panel, source_with_path, ctx) == FILE_CONT)
2439 if (operation == OP_DELETE)
2441 if (S_ISDIR (src_stat.st_mode))
2442 value = erase_dir (tctx, ctx, source_with_path);
2443 else
2444 value = erase_file (tctx, ctx, source_with_path, 1);
2446 else
2448 temp = transform_source (ctx, source_with_path);
2449 if (temp == NULL)
2450 value = transform_error;
2451 else
2453 char *repl_dest, *temp2;
2455 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2456 temp2 = concat_dir_and_file (repl_dest, temp);
2457 g_free (temp);
2458 g_free (repl_dest);
2459 g_free (dest);
2460 dest = temp2;
2462 switch (operation)
2464 case OP_COPY:
2465 /* we use file_mask_op_follow_links only with OP_COPY */
2466 ctx->stat_func (source_with_path, &src_stat);
2468 if (S_ISDIR (src_stat.st_mode))
2469 value = copy_dir_dir (tctx, ctx, source_with_path, dest,
2470 TRUE, FALSE, FALSE, NULL);
2471 else
2472 value = copy_file_file (tctx, ctx, source_with_path, dest);
2473 break;
2475 case OP_MOVE:
2476 if (S_ISDIR (src_stat.st_mode))
2477 value = move_dir_dir (tctx, ctx, source_with_path, dest);
2478 else
2479 value = move_file_file (tctx, ctx, source_with_path, dest);
2480 break;
2482 default:
2483 /* Unknown file operation */
2484 abort ();
2487 } /* Copy or move operation */
2489 if ((value == FILE_CONT) && !force_single)
2490 unmark_files (panel);
2493 else
2495 /* Many files */
2497 /* Check destination for copy or move operation */
2498 while (operation != OP_DELETE)
2500 int dst_result;
2501 struct stat dst_stat;
2503 dst_result = mc_stat (dest, &dst_stat);
2505 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2506 break;
2508 if (file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2509 goto clean_up;
2512 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2514 /* Loop for every file, perform the actual copy operation */
2515 for (i = 0; i < panel->count; i++)
2517 if (!panel->dir.list[i].f.marked)
2518 continue; /* Skip the unmarked ones */
2520 source = panel->dir.list[i].fname;
2521 src_stat = panel->dir.list[i].st;
2523 #ifdef WITH_FULL_PATHS
2524 g_free (source_with_path);
2525 source_with_path = concat_dir_and_file (panel->cwd, source);
2526 #endif /* WITH_FULL_PATHS */
2528 if (operation == OP_DELETE)
2530 if (S_ISDIR (src_stat.st_mode))
2531 value = erase_dir (tctx, ctx, source_with_path);
2532 else
2533 value = erase_file (tctx, ctx, source_with_path, 1);
2535 else
2537 temp = transform_source (ctx, source_with_path);
2539 if (temp == NULL)
2540 value = transform_error;
2541 else
2543 char *temp2, *temp3, *repl_dest;
2545 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2546 temp2 = concat_dir_and_file (repl_dest, temp);
2547 g_free (temp);
2548 g_free (repl_dest);
2549 temp3 = source_with_path;
2550 source_with_path = strutils_shell_unescape (source_with_path);
2551 g_free (temp3);
2552 temp3 = temp2;
2553 temp2 = strutils_shell_unescape (temp2);
2554 g_free (temp3);
2556 switch (operation)
2558 case OP_COPY:
2559 /* we use file_mask_op_follow_links only with OP_COPY */
2560 ctx->stat_func (source_with_path, &src_stat);
2561 if (S_ISDIR (src_stat.st_mode))
2562 value = copy_dir_dir (tctx, ctx, source_with_path, temp2,
2563 TRUE, FALSE, FALSE, NULL);
2564 else
2565 value = copy_file_file (tctx, ctx, source_with_path, temp2);
2566 free_linklist (&dest_dirs);
2567 break;
2569 case OP_MOVE:
2570 if (S_ISDIR (src_stat.st_mode))
2571 value = move_dir_dir (tctx, ctx, source_with_path, temp2);
2572 else
2573 value = move_file_file (tctx, ctx, source_with_path, temp2);
2574 break;
2576 default:
2577 /* Unknown file operation */
2578 abort ();
2581 g_free (temp2);
2583 } /* Copy or move operation */
2585 if (value == FILE_ABORT)
2586 break;
2588 if (value == FILE_CONT)
2589 do_file_mark (panel, i, 0);
2591 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2593 if (verbose)
2595 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
2597 if (operation != OP_DELETE)
2598 file_progress_show (ctx, 0, 0, "", FALSE);
2601 if (check_progress_buttons (ctx) == FILE_ABORT)
2602 break;
2604 mc_refresh ();
2605 } /* Loop for every file */
2607 } /* Many entries */
2609 clean_up:
2610 /* Clean up */
2611 if (save_cwd != NULL)
2613 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2614 g_free (save_cwd);
2617 if (save_dest != NULL)
2619 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2620 g_free (save_dest);
2623 free_linklist (&linklist);
2624 free_linklist (&dest_dirs);
2625 #ifdef WITH_FULL_PATHS
2626 g_free (source_with_path);
2627 #endif /* WITH_FULL_PATHS */
2628 g_free (dest);
2629 g_free (ctx->dest_mask);
2630 ctx->dest_mask = NULL;
2632 #ifdef WITH_BACKGROUND
2633 /* Let our parent know we are saying bye bye */
2634 if (we_are_background)
2636 int cur_pid = getpid ();
2637 /* Send pid to parent with child context, it is fork and
2638 don't modify real parent ctx */
2639 ctx->pid = cur_pid;
2640 parent_call ((void *) end_bg_process, ctx, 0);
2642 vfs_shut ();
2643 _exit (0);
2645 #endif /* WITH_BACKGROUND */
2647 file_op_context_destroy (ctx);
2648 file_op_total_context_destroy (tctx);
2650 return ret_val;
2653 /* }}} */
2655 /* --------------------------------------------------------------------------------------------- */
2656 /* {{{ Query/status report routines */
2657 /** Report error with one file */
2658 FileProgressStatus
2659 file_error (const char *format, const char *file)
2661 char buf[BUF_MEDIUM];
2663 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
2665 return do_file_error (buf);
2668 /* --------------------------------------------------------------------------------------------- */
2671 Cause emacs to enter folding mode for this file:
2672 Local variables:
2673 end: