Get rid of some function forward declarations.
[midnight-commander.git] / src / filemanager / file.c
blob613d096124bb1103c02faa71bb73720c26306658
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 "src/setup.h"
69 #include "src/background.h" /* we_are_background */
71 #include "layout.h" /* rotate_dash() */
73 /* Needed for current_panel, other_panel and WTree */
74 #include "dir.h"
75 #include "filegui.h"
76 #include "tree.h"
77 #include "midnight.h" /* current_panel */
79 #include "file.h"
81 /* }}} */
83 /*** global variables ****************************************************************************/
85 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
86 const char *op_names[3] = {
87 N_("DialogTitle|Copy"),
88 N_("DialogTitle|Move"),
89 N_("DialogTitle|Delete")
92 /*** file scope macro definitions ****************************************************************/
94 /* Hack: the vfs code should not rely on this */
95 #define WITH_FULL_PATHS 1
97 #define FILEOP_UPDATE_INTERVAL 2
98 #define FILEOP_STALLING_INTERVAL 4
100 /*** file scope type declarations ****************************************************************/
102 /* This is a hard link cache */
103 struct link
105 struct link *next;
106 struct vfs_class *vfs;
107 dev_t dev;
108 ino_t ino;
109 short linkcount;
110 mode_t st_mode;
111 char name[1];
114 /* Status of the destination file */
115 typedef enum
117 DEST_NONE = 0, /* Not created */
118 DEST_SHORT = 1, /* Created, not fully copied */
119 DEST_FULL = 2 /* Created, fully copied */
120 } dest_status_t;
123 * This array introduced to avoid translation problems. The former (op_names)
124 * is assumed to be nouns, suitable in dialog box titles; this one should
125 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
126 * (I don't use spaces around the words, because someday they could be
127 * dropped, when widgets get smarter)
130 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
131 static const char *op_names1[] = {
132 N_("FileOperation|Copy"),
133 N_("FileOperation|Move"),
134 N_("FileOperation|Delete")
138 * These are formats for building a prompt. Parts encoded as follows:
139 * %o - operation from op_names1
140 * %f - file/files or files/directories, as appropriate
141 * %m - "with source mask" or question mark for delete
142 * %s - source name (truncated)
143 * %d - number of marked files
144 * %e - "to:" or question mark for delete
146 * xgettext:no-c-format */
147 static const char *one_format = N_("%o %f \"%s\"%m");
148 /* xgettext:no-c-format */
149 static const char *many_format = N_("%o %d %f%m");
151 static const char *prompt_parts[] = {
152 N_("file"),
153 N_("files"),
154 N_("directory"),
155 N_("directories"),
156 N_("files/directories"),
157 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
158 N_(" with source mask:"),
159 N_("to:")
162 static const char *question_format = N_("%s?");
164 /*** file scope variables ************************************************************************/
166 /* the hard link cache */
167 static struct link *linklist = NULL;
169 /* the files-to-be-erased list */
170 static struct link *erase_list;
173 * In copy_dir_dir we use two additional single linked lists: The first -
174 * variable name `parent_dirs' - holds information about already copied
175 * directories and is used to detect cyclic symbolic links.
176 * The second (`dest_dirs' below) holds information about just created
177 * target directories and is used to detect when an directory is copied
178 * into itself (we don't want to copy infinitly).
179 * Both lists don't use the linkcount and name structure members of struct
180 * link.
182 static struct link *dest_dirs = NULL;
184 static FileProgressStatus transform_error = FILE_CONT;
186 /*** file scope functions ************************************************************************/
187 /* --------------------------------------------------------------------------------------------- */
189 static char *
190 transform_source (FileOpContext * ctx, const char *source)
192 char *s, *q;
193 char *fnsource;
195 s = g_strdup (source);
197 /* We remove \n from the filename since regex routines would use \n as an anchor */
198 /* this is just to be allowed to maniupulate file names with \n on it */
199 for (q = s; *q != '\0'; q++)
200 if (*q == '\n')
201 *q = ' ';
203 fnsource = (char *) x_basename (s);
205 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
206 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
207 else
209 q = NULL;
210 transform_error = FILE_SKIP;
213 g_free (s);
214 return q;
217 /* --------------------------------------------------------------------------------------------- */
219 static void
220 free_linklist (struct link **lc_linklist)
222 struct link *lp, *lp2;
224 for (lp = *lc_linklist; lp != NULL; lp = lp2)
226 lp2 = lp->next;
227 g_free (lp);
229 *lc_linklist = NULL;
232 /* --------------------------------------------------------------------------------------------- */
234 static int
235 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
237 ino_t ino = sb->st_ino;
238 dev_t dev = sb->st_dev;
239 struct vfs_class *vfs = vfs_get_class (path);
241 while (lp != NULL)
243 if (lp->vfs == vfs)
244 if (lp->ino == ino && lp->dev == dev)
245 return 1;
246 lp = lp->next;
248 return 0;
251 /* --------------------------------------------------------------------------------------------- */
253 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
254 * and a hardlink was succesfully made
257 static int
258 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
260 struct link *lp;
261 struct vfs_class *my_vfs = vfs_get_class (src_name);
262 ino_t ino = pstat->st_ino;
263 dev_t dev = pstat->st_dev;
264 struct stat link_stat;
265 const char *p;
267 if ((vfs_file_class_flags (src_name) & VFSF_NOLINKS) != 0)
268 return 0;
270 for (lp = linklist; lp != NULL; lp = lp->next)
271 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev)
273 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
274 && link_stat.st_dev == dev && vfs_get_class (lp->name) == my_vfs)
276 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
277 was copied to */
278 if (vfs_get_class (dst_name) == vfs_get_class (p))
280 if (!mc_stat (p, &link_stat))
282 if (!mc_link (p, dst_name))
283 return 1;
287 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
288 return 0;
290 lp = (struct link *) g_try_malloc (sizeof (struct link) + strlen (src_name)
291 + strlen (dst_name) + 1);
292 if (lp)
294 char *lpdstname;
295 lp->vfs = my_vfs;
296 lp->ino = ino;
297 lp->dev = dev;
298 strcpy (lp->name, src_name);
299 lpdstname = lp->name + strlen (lp->name) + 1;
300 strcpy (lpdstname, dst_name);
301 lp->next = linklist;
302 linklist = lp;
304 return 0;
307 /* --------------------------------------------------------------------------------------------- */
309 * Duplicate the contents of the symbolic link src_path in dst_path.
310 * Try to make a stable symlink if the option "stable symlink" was
311 * set in the file mask dialog.
312 * If dst_path is an existing symlink it will be deleted silently
313 * (upper levels take already care of existing files at dst_path).
316 static FileProgressStatus
317 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
319 char link_target[MC_MAXPATHLEN];
320 int len;
321 FileProgressStatus return_status;
322 struct stat sb;
323 gboolean dst_is_symlink;
325 dst_is_symlink = (mc_lstat (dst_path, &sb) == 0) && S_ISLNK (sb.st_mode);
327 retry_src_readlink:
328 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
329 if (len < 0)
331 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
332 if (return_status == FILE_RETRY)
333 goto retry_src_readlink;
334 return return_status;
336 link_target[len] = 0;
338 if (ctx->stable_symlinks)
339 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path))
341 message (D_ERROR, MSG_ERROR,
342 _("Cannot make stable symlinks across"
343 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
344 ctx->stable_symlinks = FALSE;
347 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
349 char *p, *q, *s;
351 const char *r = strrchr (src_path, PATH_SEP);
353 if (r)
355 p = g_strndup (src_path, r - src_path + 1);
356 if (g_path_is_absolute (dst_path))
357 q = g_strdup (dst_path);
358 else
359 q = g_strconcat (p, dst_path, (char *) NULL);
360 s = strrchr (q, PATH_SEP);
361 if (s)
363 s[1] = 0;
364 s = g_strconcat (p, link_target, (char *) NULL);
365 g_free (p);
366 g_strlcpy (link_target, s, sizeof (link_target));
367 g_free (s);
368 s = diff_two_paths (q, link_target);
369 if (s)
371 g_strlcpy (link_target, s, sizeof (link_target));
372 g_free (s);
375 else
376 g_free (p);
377 g_free (q);
380 retry_dst_symlink:
381 if (mc_symlink (link_target, dst_path) == 0)
382 /* Success */
383 return FILE_CONT;
385 * if dst_exists, it is obvious that this had failed.
386 * We can delete the old symlink and try again...
388 if (dst_is_symlink)
390 if (!mc_unlink (dst_path))
391 if (mc_symlink (link_target, dst_path) == 0)
392 /* Success */
393 return FILE_CONT;
395 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
396 if (return_status == FILE_RETRY)
397 goto retry_dst_symlink;
398 return return_status;
401 /* --------------------------------------------------------------------------------------------- */
403 static FileProgressStatus
404 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add,
405 gboolean is_toplevel_file)
407 struct timeval tv_current;
408 static struct timeval tv_start = { };
410 if (is_toplevel_file || ctx->progress_totals_computed)
412 tctx->progress_count++;
413 tctx->progress_bytes += (uintmax_t) add;
415 if (tv_start.tv_sec == 0)
417 gettimeofday (&tv_start, (struct timezone *) NULL);
419 gettimeofday (&tv_current, (struct timezone *) NULL);
420 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
422 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
424 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
425 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
427 tv_start.tv_sec = tv_current.tv_sec;
430 return check_progress_buttons (ctx);
433 /* --------------------------------------------------------------------------------------------- */
435 static FileProgressStatus
436 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
438 char *msg;
439 int result = 0;
440 const char *head_msg;
442 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
444 msg = g_strdup_printf (fmt, a, b);
445 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
446 g_free (msg);
447 do_refresh ();
449 return (result == 1) ? FILE_ABORT : FILE_SKIP;
452 /* --------------------------------------------------------------------------------------------- */
454 static FileProgressStatus
455 warn_same_file (const char *fmt, const char *a, const char *b)
457 #ifdef WITH_BACKGROUND
458 union
460 void *p;
461 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
462 const char *a, const char *b);
463 } pntr;
465 pntr.f = real_warn_same_file;
467 if (we_are_background)
468 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
469 #endif
470 return real_warn_same_file (Foreground, fmt, a, b);
473 /* --------------------------------------------------------------------------------------------- */
474 /* {{{ Query/status report routines */
476 static FileProgressStatus
477 real_do_file_error (enum OperationMode mode, const char *error)
479 int result;
480 const char *msg;
482 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
483 result = query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"), _("&Abort"));
485 switch (result)
487 case 0:
488 do_refresh ();
489 return FILE_SKIP;
491 case 1:
492 do_refresh ();
493 return FILE_RETRY;
495 case 2:
496 default:
497 return FILE_ABORT;
501 /* --------------------------------------------------------------------------------------------- */
503 static FileProgressStatus
504 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
506 gchar *text;
508 if (ctx->recursive_result < RECURSIVE_ALWAYS)
510 const char *msg = mode == Foreground
511 ? _("\nDirectory not empty.\nDelete it recursively?")
512 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
513 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
515 if (safe_delete)
516 query_set_sel (1);
518 ctx->recursive_result =
519 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
520 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
522 if (ctx->recursive_result != RECURSIVE_ABORT)
523 do_refresh ();
524 g_free (text);
527 switch (ctx->recursive_result)
529 case RECURSIVE_YES:
530 case RECURSIVE_ALWAYS:
531 return FILE_CONT;
533 case RECURSIVE_NO:
534 case RECURSIVE_NEVER:
535 return FILE_SKIP;
537 case RECURSIVE_ABORT:
538 default:
539 return FILE_ABORT;
543 /* --------------------------------------------------------------------------------------------- */
545 #ifdef WITH_BACKGROUND
546 static FileProgressStatus
547 do_file_error (const char *str)
549 union
551 void *p;
552 FileProgressStatus (*f) (enum OperationMode, const char *);
553 } pntr;
554 pntr.f = real_do_file_error;
556 if (we_are_background)
557 return parent_call (pntr.p, NULL, 1, strlen (str), str);
558 else
559 return real_do_file_error (Foreground, str);
562 /* --------------------------------------------------------------------------------------------- */
564 static FileProgressStatus
565 query_recursive (FileOpContext * ctx, const char *s)
567 union
569 void *p;
570 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
571 } pntr;
572 pntr.f = real_query_recursive;
574 if (we_are_background)
575 return parent_call (pntr.p, ctx, 1, strlen (s), s);
576 else
577 return real_query_recursive (ctx, Foreground, s);
580 /* --------------------------------------------------------------------------------------------- */
582 static FileProgressStatus
583 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
584 struct stat *_d_stat)
586 union
588 void *p;
589 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
590 struct stat *, struct stat *);
591 } pntr;
592 pntr.f = file_progress_real_query_replace;
594 if (we_are_background)
595 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
596 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
597 else
598 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
601 #else
602 /* --------------------------------------------------------------------------------------------- */
604 static FileProgressStatus
605 do_file_error (const char *str)
607 return real_do_file_error (Foreground, str);
610 /* --------------------------------------------------------------------------------------------- */
612 static FileProgressStatus
613 query_recursive (FileOpContext * ctx, const char *s)
615 return real_query_recursive (ctx, Foreground, s);
618 /* --------------------------------------------------------------------------------------------- */
620 static FileProgressStatus
621 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
622 struct stat *_d_stat)
624 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
627 #endif /* !WITH_BACKGROUND */
629 /* --------------------------------------------------------------------------------------------- */
630 /** Report error with two files */
632 static FileProgressStatus
633 files_error (const char *format, const char *file1, const char *file2)
635 char buf[BUF_MEDIUM];
636 char *nfile1 = g_strdup (path_trunc (file1, 15));
637 char *nfile2 = g_strdup (path_trunc (file2, 15));
639 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
641 g_free (nfile1);
642 g_free (nfile2);
644 return do_file_error (buf);
647 /* --------------------------------------------------------------------------------------------- */
649 static void
650 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
651 struct timeval tv_current, struct timeval tv_transfer_start,
652 off_t file_size, off_t n_read_total)
654 long dt;
656 /* 1. Update rotating dash after some time */
657 rotate_dash ();
659 /* 3. Compute ETA */
660 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
662 if (n_read_total)
664 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
665 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
667 else
668 ctx->eta_secs = 0.0;
670 /* 4. Compute BPS rate */
671 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
672 if (ctx->bps_time < 1)
673 ctx->bps_time = 1;
674 ctx->bps = n_read_total / ctx->bps_time;
676 /* 5. Compute total ETA and BPS */
677 if (ctx->progress_bytes != 0)
679 uintmax_t remain_bytes;
680 tctx->copyed_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
681 remain_bytes = ctx->progress_bytes - tctx->copyed_bytes;
682 #if 1
684 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
686 if (total_secs < 1)
687 total_secs = 1;
689 tctx->bps = tctx->copyed_bytes / total_secs;
690 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
692 #else
693 /* broken on lot of little files */
694 tctx->bps_count++;
695 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
696 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
697 #endif
701 /* --------------------------------------------------------------------------------------------- */
703 /* {{{ Move routines */
704 static FileProgressStatus
705 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
707 struct stat src_stats, dst_stats;
708 FileProgressStatus return_status = FILE_CONT;
709 gboolean copy_done = FALSE;
710 gboolean old_ask_overwrite;
712 file_progress_show_source (ctx, s);
713 file_progress_show_target (ctx, d);
714 if (check_progress_buttons (ctx) == FILE_ABORT)
715 return FILE_ABORT;
717 mc_refresh ();
719 while (mc_lstat (s, &src_stats) != 0)
721 /* Source doesn't exist */
722 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
723 if (return_status != FILE_RETRY)
724 return return_status;
727 if (mc_lstat (d, &dst_stats) == 0)
729 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
730 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
732 if (S_ISDIR (dst_stats.st_mode))
734 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
735 do_refresh ();
736 return FILE_SKIP;
739 if (confirm_overwrite)
741 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
742 if (return_status != FILE_CONT)
743 return return_status;
745 /* Ok to overwrite */
748 if (!ctx->do_append)
750 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
752 return_status = make_symlink (ctx, s, d);
753 if (return_status == FILE_CONT)
754 goto retry_src_remove;
755 else
756 return return_status;
759 if (mc_rename (s, d) == 0)
760 return progress_update_one (tctx, ctx, src_stats.st_size, TRUE);
762 #if 0
763 /* Comparison to EXDEV seems not to work in nfs if you're moving from
764 one nfs to the same, but on the server it is on two different
765 filesystems. Then nfs returns EIO instead of EXDEV.
766 Hope it will not hurt if we always in case of error try to copy/delete. */
767 else
768 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
770 if (errno != EXDEV)
772 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
773 if (return_status == FILE_RETRY)
774 goto retry_rename;
775 return return_status;
777 #endif
779 /* Failed because filesystem boundary -> copy the file instead */
780 old_ask_overwrite = tctx->ask_overwrite;
781 tctx->ask_overwrite = FALSE;
782 return_status = copy_file_file (tctx, ctx, s, d);
783 tctx->ask_overwrite = old_ask_overwrite;
784 if (return_status != FILE_CONT)
785 return return_status;
787 copy_done = TRUE;
789 file_progress_show_source (ctx, NULL);
790 file_progress_show (ctx, 0, 0, "", FALSE);
792 return_status = check_progress_buttons (ctx);
793 if (return_status != FILE_CONT)
794 return return_status;
796 mc_refresh ();
798 retry_src_remove:
799 if (mc_unlink (s))
801 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
802 if (return_status == FILE_RETRY)
803 goto retry_src_remove;
804 return return_status;
807 if (!copy_done)
808 return_status = progress_update_one (tctx, ctx, src_stats.st_size, TRUE);
810 return return_status;
813 /* }}} */
815 /* --------------------------------------------------------------------------------------------- */
816 /* {{{ Erase routines */
817 /** Don't update progress status if progress_count==NULL */
819 static FileProgressStatus
820 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s,
821 gboolean is_toplevel_file)
823 int return_status;
824 struct stat buf;
826 file_progress_show_deleting (ctx, s);
827 if (check_progress_buttons (ctx) == FILE_ABORT)
828 return FILE_ABORT;
829 mc_refresh ();
831 if (tctx->progress_count != 0 && mc_lstat (s, &buf) != 0)
833 /* ignore, most likely the mc_unlink fails, too */
834 buf.st_size = 0;
837 while (mc_unlink (s) != 0)
839 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
840 if (return_status != FILE_RETRY)
841 return return_status;
844 if (tctx->progress_count == 0)
845 return FILE_CONT;
846 return progress_update_one (tctx, ctx, buf.st_size, is_toplevel_file);
849 /* --------------------------------------------------------------------------------------------- */
851 static FileProgressStatus
852 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
854 struct dirent *next;
855 struct stat buf;
856 DIR *reading;
857 char *path;
858 FileProgressStatus return_status = FILE_CONT;
860 if (!strcmp (s, ".."))
861 return FILE_RETRY;
863 reading = mc_opendir (s);
865 if (!reading)
866 return FILE_RETRY;
868 while ((next = mc_readdir (reading)) && return_status == FILE_CONT)
870 if (!strcmp (next->d_name, "."))
871 continue;
872 if (!strcmp (next->d_name, ".."))
873 continue;
874 path = concat_dir_and_file (s, next->d_name);
875 if (mc_lstat (path, &buf))
877 g_free (path);
878 mc_closedir (reading);
879 return FILE_RETRY;
881 if (S_ISDIR (buf.st_mode))
882 return_status =
883 (recursive_erase (tctx, ctx, path) != FILE_CONT) ? FILE_RETRY : FILE_CONT;
884 else
885 return_status = erase_file (tctx, ctx, path, 0);
886 g_free (path);
888 mc_closedir (reading);
889 if (return_status != FILE_CONT)
890 return return_status;
891 file_progress_show_deleting (ctx, s);
892 if (check_progress_buttons (ctx) == FILE_ABORT)
893 return FILE_ABORT;
894 mc_refresh ();
896 while (my_rmdir (s))
898 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
899 if (return_status != FILE_RETRY)
900 return return_status;
903 return FILE_CONT;
906 /* --------------------------------------------------------------------------------------------- */
907 /** Return -1 on error, 1 if there are no entries besides "." and ".."
908 in the directory path points to, 0 else. */
910 static int
911 check_dir_is_empty (const char *path)
913 DIR *dir;
914 struct dirent *d;
915 int i;
917 dir = mc_opendir (path);
918 if (!dir)
919 return -1;
921 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
923 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
924 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
925 continue; /* "." or ".." */
926 i = 0;
927 break;
930 mc_closedir (dir);
931 return i;
934 /* --------------------------------------------------------------------------------------------- */
936 static FileProgressStatus
937 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
939 FileProgressStatus error;
941 if (strcmp (s, "..") == 0)
942 return FILE_SKIP;
944 if (strcmp (s, ".") == 0)
945 return FILE_SKIP;
947 file_progress_show_deleting (ctx, s);
948 if (check_progress_buttons (ctx) == FILE_ABORT)
949 return FILE_ABORT;
950 mc_refresh ();
952 if (1 != check_dir_is_empty (s)) /* not empty or error */
953 return FILE_CONT;
955 while (my_rmdir (s))
957 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
958 if (error != FILE_RETRY)
959 return error;
962 return FILE_CONT;
965 /* }}} */
967 /* --------------------------------------------------------------------------------------------- */
968 /* {{{ Panel operate routines */
971 * Return currently selected entry name or the name of the first marked
972 * entry if there is one.
975 static char *
976 panel_get_file (WPanel * panel, struct stat *stat_buf)
978 int i;
980 if (get_current_type () == view_tree)
982 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
983 char *tree_name = tree_selected_name (tree);
985 mc_stat (tree_name, stat_buf);
986 return tree_name;
989 if (panel->marked)
991 for (i = 0; i < panel->count; i++)
992 if (panel->dir.list[i].f.marked)
994 *stat_buf = panel->dir.list[i].st;
995 return panel->dir.list[i].fname;
998 else
1000 *stat_buf = panel->dir.list[panel->selected].st;
1001 return panel->dir.list[panel->selected].fname;
1003 g_assert_not_reached ();
1004 return NULL;
1007 /* --------------------------------------------------------------------------------------------- */
1009 * panel_compute_totals:
1011 * compute the number of files and the number of bytes
1012 * used up by the whole selection, recursing directories
1013 * as required. In addition, it checks to see if it will
1014 * overwrite any files by doing the copy.
1017 static FileProgressStatus
1018 panel_compute_totals (const WPanel * panel, const void *ui,
1019 compute_dir_size_callback cback,
1020 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
1022 int i;
1024 *ret_marked = 0;
1025 *ret_total = 0;
1027 for (i = 0; i < panel->count; i++)
1029 struct stat *s;
1031 if (!panel->dir.list[i].f.marked)
1032 continue;
1034 s = &panel->dir.list[i].st;
1036 if (S_ISDIR (s->st_mode))
1038 char *dir_name;
1039 size_t subdir_count = 0;
1040 uintmax_t subdir_bytes = 0;
1041 FileProgressStatus status;
1043 dir_name = concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1045 status = compute_dir_size (dir_name, ui, cback,
1046 &subdir_count, &subdir_bytes, compute_symlinks);
1047 g_free (dir_name);
1049 if (status != FILE_CONT)
1050 return FILE_ABORT;
1052 *ret_marked += subdir_count;
1053 *ret_total += subdir_bytes;
1055 else
1057 (*ret_marked)++;
1058 *ret_total += (uintmax_t) s->st_size;
1062 return FILE_CONT;
1065 /* --------------------------------------------------------------------------------------------- */
1066 /** Initialize variables for progress bars */
1067 static FileProgressStatus
1068 panel_operate_init_totals (FileOperation operation,
1069 const WPanel * panel, const char *source, FileOpContext * ctx)
1071 FileProgressStatus status;
1073 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1075 ComputeDirSizeUI *ui;
1077 ui = compute_dir_size_create_ui ();
1079 if (source != NULL)
1080 status = compute_dir_size (source, ui, compute_dir_size_update_ui,
1081 &ctx->progress_count, &ctx->progress_bytes,
1082 ctx->follow_links);
1083 else
1084 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1085 &ctx->progress_count, &ctx->progress_bytes,
1086 ctx->follow_links);
1088 compute_dir_size_destroy_ui (ui);
1090 ctx->progress_totals_computed = (status == FILE_CONT);
1092 else
1094 status = FILE_CONT;
1095 ctx->progress_count = panel->marked;
1096 ctx->progress_bytes = panel->total;
1097 ctx->progress_totals_computed = FALSE;
1100 return status;
1103 /* --------------------------------------------------------------------------------------------- */
1105 * Generate user prompt for panel operation.
1106 * single_source is the name if the source entry or NULL for multiple
1107 * entries.
1108 * src_stat is only used when single_source is not NULL.
1111 static char *
1112 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1113 gboolean single_source, const struct stat *src_stat)
1115 const char *sp, *cp;
1116 char format_string[BUF_MEDIUM];
1117 char *dp = format_string;
1118 gboolean build_question = FALSE;
1120 static gboolean i18n_flag = FALSE;
1121 if (!i18n_flag)
1123 size_t i;
1125 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1126 op_names1[i] = Q_ (op_names1[i]);
1128 #ifdef ENABLE_NLS
1129 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1130 prompt_parts[i] = _(prompt_parts[i]);
1132 one_format = _(one_format);
1133 many_format = _(many_format);
1134 question_format = _(question_format);
1135 #endif /* ENABLE_NLS */
1136 i18n_flag = TRUE;
1139 sp = single_source ? one_format : many_format;
1141 while (*sp != '\0')
1143 switch (*sp)
1145 case '%':
1146 cp = NULL;
1147 switch (sp[1])
1149 case 'o':
1150 cp = op_names1[operation];
1151 break;
1152 case 'm':
1153 if (operation == OP_DELETE)
1155 cp = "";
1156 build_question = TRUE;
1158 else
1159 cp = prompt_parts[5];
1160 break;
1161 case 'e':
1162 if (operation == OP_DELETE)
1164 cp = "";
1165 build_question = TRUE;
1167 else
1168 cp = prompt_parts[6];
1169 break;
1170 case 'f':
1171 if (single_source)
1172 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1173 else
1174 cp = (panel->marked == panel->dirs_marked)
1175 ? prompt_parts[3]
1176 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1177 break;
1178 default:
1179 *dp++ = *sp++;
1182 if (cp != NULL)
1184 sp += 2;
1185 while (*cp != '\0')
1186 *dp++ = *cp++;
1188 break;
1189 default:
1190 *dp++ = *sp++;
1193 *dp = '\0';
1195 if (build_question)
1197 char tmp[BUF_MEDIUM];
1199 memmove (tmp, format_string, sizeof (tmp));
1200 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1203 return g_strdup (format_string);
1206 /* --------------------------------------------------------------------------------------------- */
1208 #ifdef WITH_BACKGROUND
1209 static int
1210 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1212 int pid = ctx->pid;
1214 (void) mode;
1215 ctx->pid = 0;
1217 unregister_task_with_pid (pid);
1218 /* file_op_context_destroy(ctx); */
1219 return 1;
1221 #endif
1223 /* --------------------------------------------------------------------------------------------- */
1224 /*** public functions ****************************************************************************/
1225 /* --------------------------------------------------------------------------------------------- */
1227 FileProgressStatus
1228 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
1229 const char *src_path, const char *dst_path)
1231 uid_t src_uid = (uid_t) - 1;
1232 gid_t src_gid = (gid_t) - 1;
1234 int src_desc, dest_desc = -1;
1235 int n_read, n_written;
1236 mode_t src_mode = 0; /* The mode of the source file */
1237 struct stat sb, sb2;
1238 struct utimbuf utb;
1239 gboolean dst_exists = FALSE, appending = FALSE;
1240 off_t n_read_total = 0, file_size = -1;
1241 FileProgressStatus return_status, temp_status;
1242 struct timeval tv_transfer_start;
1243 dest_status_t dst_status = DEST_NONE;
1244 int open_flags;
1245 gboolean is_first_time = TRUE;
1247 /* FIXME: We should not be using global variables! */
1248 ctx->do_reget = 0;
1249 return_status = FILE_RETRY;
1251 file_progress_show_source (ctx, src_path);
1252 file_progress_show_target (ctx, dst_path);
1253 if (check_progress_buttons (ctx) == FILE_ABORT)
1254 return FILE_ABORT;
1256 mc_refresh ();
1258 while (mc_stat (dst_path, &sb2) == 0)
1260 if (S_ISDIR (sb2.st_mode))
1262 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1263 if (return_status == FILE_RETRY)
1264 continue;
1265 return return_status;
1267 dst_exists = TRUE;
1268 break;
1271 while ((*ctx->stat_func) (src_path, &sb))
1273 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1274 if (return_status != FILE_RETRY)
1275 return return_status;
1278 if (dst_exists)
1280 /* Destination already exists */
1281 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
1282 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), src_path, dst_path);
1283 /* Should we replace destination? */
1284 if (tctx->ask_overwrite)
1286 ctx->do_reget = 0;
1287 return_status = query_replace (ctx, dst_path, &sb, &sb2);
1288 if (return_status != FILE_CONT)
1289 return return_status;
1293 if (!ctx->do_append)
1295 /* Check the hardlinks */
1296 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb) == 1)
1298 /* We have made a hardlink - no more processing is necessary */
1299 return FILE_CONT;
1302 if (S_ISLNK (sb.st_mode))
1303 return make_symlink (ctx, src_path, dst_path);
1305 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
1306 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
1308 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0)
1310 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1311 if (return_status == FILE_RETRY)
1312 continue;
1313 return return_status;
1315 /* Success */
1317 while (ctx->preserve_uidgid && mc_chown (dst_path, sb.st_uid, sb.st_gid))
1319 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1320 if (temp_status == FILE_RETRY)
1321 continue;
1322 return temp_status;
1324 while (ctx->preserve && mc_chmod (dst_path, sb.st_mode & ctx->umask_kill))
1326 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1327 if (temp_status == FILE_RETRY)
1328 continue;
1329 return temp_status;
1331 return FILE_CONT;
1335 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1337 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0)
1339 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1340 if (return_status == FILE_RETRY)
1341 continue;
1342 ctx->do_append = 0;
1343 return return_status;
1346 if (ctx->do_reget != 0)
1348 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1350 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1351 ctx->do_reget = 0;
1352 ctx->do_append = FALSE;
1356 while (mc_fstat (src_desc, &sb))
1358 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1359 if (return_status == FILE_RETRY)
1360 continue;
1361 ctx->do_append = FALSE;
1362 goto ret;
1364 src_mode = sb.st_mode;
1365 src_uid = sb.st_uid;
1366 src_gid = sb.st_gid;
1367 utb.actime = sb.st_atime;
1368 utb.modtime = sb.st_mtime;
1369 file_size = sb.st_size;
1371 open_flags = O_WRONLY;
1372 if (dst_exists)
1374 if (ctx->do_append != 0)
1375 open_flags |= O_APPEND;
1376 else
1377 open_flags |= O_CREAT | O_TRUNC;
1379 else
1381 open_flags |= O_CREAT | O_EXCL;
1384 while ((dest_desc = mc_open (dst_path, open_flags, src_mode)) < 0)
1386 if (errno == EEXIST)
1387 goto ret;
1389 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1390 if (return_status == FILE_RETRY)
1391 continue;
1392 ctx->do_append = FALSE;
1393 goto ret;
1395 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1397 appending = ctx->do_append;
1398 ctx->do_append = FALSE;
1400 /* Find out the optimal buffer size. */
1401 while (mc_fstat (dest_desc, &sb))
1403 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1404 if (return_status == FILE_RETRY)
1405 continue;
1406 goto ret;
1409 ctx->eta_secs = 0.0;
1410 ctx->bps = 0;
1412 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1413 file_progress_show (ctx, 0, file_size, "", TRUE);
1414 else
1415 file_progress_show (ctx, 1, 1, "", TRUE);
1416 return_status = check_progress_buttons (ctx);
1417 mc_refresh ();
1419 if (return_status != FILE_CONT)
1420 goto ret;
1423 struct timeval tv_current, tv_last_update, tv_last_input;
1424 int secs, update_secs;
1425 const char *stalled_msg = "";
1427 tv_last_update = tv_transfer_start;
1429 for (;;)
1431 char buf[BUF_8K];
1433 /* src_read */
1434 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
1435 n_read = -1;
1436 else
1437 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0)
1439 return_status = file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
1440 if (return_status == FILE_RETRY)
1441 continue;
1442 goto ret;
1444 if (n_read == 0)
1445 break;
1447 gettimeofday (&tv_current, NULL);
1449 if (n_read > 0)
1451 char *t = buf;
1452 n_read_total += n_read;
1454 /* Windows NT ftp servers report that files have no
1455 * permissions: -------, so if we happen to have actually
1456 * read something, we should fix the permissions.
1458 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1459 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1460 gettimeofday (&tv_last_input, NULL);
1462 /* dst_write */
1463 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read)
1465 if (n_written > 0)
1467 n_read -= n_written;
1468 t += n_written;
1469 continue;
1471 return_status = file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1472 if (return_status != FILE_RETRY)
1473 goto ret;
1476 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1477 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1479 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1481 copy_file_file_display_progress (tctx, ctx,
1482 tv_current,
1483 tv_transfer_start, file_size, n_read_total);
1484 tv_last_update = tv_current;
1486 is_first_time = FALSE;
1488 if (update_secs > FILEOP_STALLING_INTERVAL)
1490 stalled_msg = _("(stalled)");
1494 gboolean force_update;
1496 force_update =
1497 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1499 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1501 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1502 file_progress_show_total (tctx, ctx,
1503 tctx->progress_bytes + n_read_total + ctx->do_reget,
1504 force_update);
1507 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1508 force_update);
1510 mc_refresh ();
1512 return_status = check_progress_buttons (ctx);
1514 if (return_status != FILE_CONT)
1516 mc_refresh ();
1517 goto ret;
1522 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1524 ret:
1525 while (src_desc != -1 && mc_close (src_desc) < 0)
1527 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1528 if (temp_status == FILE_RETRY)
1529 continue;
1530 if (temp_status == FILE_ABORT)
1531 return_status = temp_status;
1532 break;
1535 while (dest_desc != -1 && mc_close (dest_desc) < 0)
1537 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1538 if (temp_status == FILE_RETRY)
1539 continue;
1540 return_status = temp_status;
1541 break;
1544 if (dst_status == DEST_SHORT)
1546 /* Remove short file */
1547 int result;
1548 result = query_dialog (Q_ ("DialogTitle|Copy"),
1549 _("Incomplete file was retrieved. Keep it?"),
1550 D_ERROR, 2, _("&Delete"), _("&Keep"));
1551 if (result == 0)
1552 mc_unlink (dst_path);
1554 else if (dst_status == DEST_FULL)
1556 /* Copy has succeeded */
1557 if (!appending && ctx->preserve_uidgid)
1559 while (mc_chown (dst_path, src_uid, src_gid))
1561 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1562 if (temp_status == FILE_RETRY)
1563 continue;
1564 return_status = temp_status;
1565 break;
1569 if (!appending)
1571 if (ctx->preserve)
1573 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill)))
1575 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1576 if (temp_status != FILE_RETRY)
1578 return_status = temp_status;
1579 break;
1583 else
1585 src_mode = umask (-1);
1586 umask (src_mode);
1587 src_mode = 0100666 & ~src_mode;
1588 mc_chmod (dst_path, (src_mode & ctx->umask_kill));
1590 mc_utime (dst_path, &utb);
1594 if (return_status == FILE_CONT)
1595 return_status = progress_update_one (tctx, ctx, file_size, tctx->is_toplevel_file);
1597 return return_status;
1600 /* --------------------------------------------------------------------------------------------- */
1602 * I think these copy_*_* functions should have a return type.
1603 * anyway, this function *must* have two directories as arguments.
1605 /* FIXME: This function needs to check the return values of the
1606 function calls */
1608 FileProgressStatus
1609 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
1610 gboolean toplevel, gboolean move_over, gboolean do_delete, struct link * parent_dirs)
1612 struct dirent *next;
1613 struct stat buf, cbuf;
1614 DIR *reading;
1615 char *dest_dir = NULL;
1616 FileProgressStatus return_status = FILE_CONT;
1617 struct utimbuf utb;
1618 struct link *lp;
1619 char *d;
1621 d = g_strdup (_d);
1623 /* First get the mode of the source dir */
1624 retry_src_stat:
1625 if ((*ctx->stat_func) (s, &cbuf))
1627 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
1628 if (return_status == FILE_RETRY)
1629 goto retry_src_stat;
1630 goto ret_fast;
1633 if (is_in_linklist (dest_dirs, s, &cbuf))
1635 /* Don't copy a directory we created before (we don't want to copy
1636 infinitely if a directory is copied into itself) */
1637 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1638 return_status = FILE_CONT;
1639 goto ret_fast;
1642 /* Hmm, hardlink to directory??? - Norbert */
1643 /* FIXME: In this step we should do something
1644 in case the destination already exist */
1645 /* Check the hardlinks */
1646 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf) == 1)
1648 /* We have made a hardlink - no more processing is necessary */
1649 goto ret_fast;
1652 if (!S_ISDIR (cbuf.st_mode))
1654 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
1655 if (return_status == FILE_RETRY)
1656 goto retry_src_stat;
1657 goto ret_fast;
1660 if (is_in_linklist (parent_dirs, s, &cbuf))
1662 /* we found a cyclic symbolic link */
1663 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
1664 return_status = FILE_SKIP;
1665 goto ret_fast;
1668 lp = g_new (struct link, 1);
1669 lp->vfs = vfs_get_class (s);
1670 lp->ino = cbuf.st_ino;
1671 lp->dev = cbuf.st_dev;
1672 lp->next = parent_dirs;
1673 parent_dirs = lp;
1675 retry_dst_stat:
1676 /* Now, check if the dest dir exists, if not, create it. */
1677 if (mc_stat (d, &buf))
1679 /* Here the dir doesn't exist : make it ! */
1680 if (move_over)
1682 if (mc_rename (s, d) == 0)
1684 return_status = FILE_CONT;
1685 goto ret;
1688 dest_dir = d;
1689 d = NULL;
1691 else
1694 * If the destination directory exists, we want to copy the whole
1695 * directory, but we only want this to happen once.
1697 * Escape sequences added to the * to compiler warnings.
1698 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
1699 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
1701 if (!S_ISDIR (buf.st_mode))
1703 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
1704 if (return_status == FILE_RETRY)
1705 goto retry_dst_stat;
1706 goto ret;
1708 /* Dive into subdir if exists */
1709 if (toplevel && ctx->dive_into_subdirs)
1711 dest_dir = concat_dir_and_file (d, x_basename (s));
1713 else
1715 dest_dir = d;
1716 d = NULL;
1717 goto dont_mkdir;
1720 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU))
1722 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
1723 if (return_status != FILE_RETRY)
1724 goto ret;
1727 lp = g_new (struct link, 1);
1728 mc_stat (dest_dir, &buf);
1729 lp->vfs = vfs_get_class (dest_dir);
1730 lp->ino = buf.st_ino;
1731 lp->dev = buf.st_dev;
1732 lp->next = dest_dirs;
1733 dest_dirs = lp;
1735 if (ctx->preserve_uidgid)
1737 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid))
1739 return_status = file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
1740 if (return_status != FILE_RETRY)
1741 goto ret;
1745 dont_mkdir:
1746 /* open the source dir for reading */
1747 reading = mc_opendir (s);
1748 if (reading == NULL)
1749 goto ret;
1751 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1753 char *path;
1755 * Now, we don't want '.' and '..' to be created / copied at any time
1757 if (!strcmp (next->d_name, "."))
1758 continue;
1759 if (!strcmp (next->d_name, ".."))
1760 continue;
1762 /* get the filename and add it to the src directory */
1763 path = concat_dir_and_file (s, next->d_name);
1765 (*ctx->stat_func) (path, &buf);
1766 if (S_ISDIR (buf.st_mode))
1768 char *mdpath;
1770 mdpath = concat_dir_and_file (dest_dir, next->d_name);
1772 * From here, we just intend to recursively copy subdirs, not
1773 * the double functionality of copying different when the target
1774 * dir already exists. So, we give the recursive call the flag 0
1775 * meaning no toplevel.
1777 return_status =
1778 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
1779 g_free (mdpath);
1781 else
1783 char *dest_file;
1785 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
1786 return_status = copy_file_file (tctx, ctx, path, dest_file);
1787 g_free (dest_file);
1789 if (do_delete && return_status == FILE_CONT)
1791 if (ctx->erase_at_end)
1793 static struct link *tail;
1794 size_t len = strlen (path);
1795 lp = g_malloc (sizeof (struct link) + len);
1796 strncpy (lp->name, path, len + 1);
1797 lp->st_mode = buf.st_mode;
1798 lp->next = NULL;
1799 if (erase_list != NULL)
1801 tail->next = lp;
1802 tail = lp;
1804 else
1805 erase_list = tail = lp;
1807 else
1809 if (S_ISDIR (buf.st_mode))
1811 return_status = erase_dir_iff_empty (ctx, path);
1813 else
1814 return_status = erase_file (tctx, ctx, path, FALSE);
1817 g_free (path);
1819 mc_closedir (reading);
1821 if (ctx->preserve)
1823 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1824 utb.actime = cbuf.st_atime;
1825 utb.modtime = cbuf.st_mtime;
1826 mc_utime (dest_dir, &utb);
1828 else
1830 cbuf.st_mode = umask (-1);
1831 umask (cbuf.st_mode);
1832 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
1833 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1836 ret:
1837 g_free (dest_dir);
1838 g_free (parent_dirs);
1839 ret_fast:
1840 g_free (d);
1841 return return_status;
1844 /* }}} */
1846 /* --------------------------------------------------------------------------------------------- */
1847 /* {{{ Move routines */
1849 FileProgressStatus
1850 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
1852 struct stat sbuf, dbuf, destbuf;
1853 struct link *lp;
1854 char *destdir;
1855 FileProgressStatus return_status;
1856 gboolean move_over = FALSE;
1857 gboolean dstat_ok;
1859 file_progress_show_source (ctx, s);
1860 file_progress_show_target (ctx, d);
1861 if (check_progress_buttons (ctx) == FILE_ABORT)
1862 return FILE_ABORT;
1864 mc_refresh ();
1866 mc_stat (s, &sbuf);
1867 dstat_ok = (mc_stat (d, &dbuf) == 0);
1869 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
1870 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
1872 if (!dstat_ok)
1873 destdir = g_strdup (d); /* destination doesn't exist */
1874 else if (!ctx->dive_into_subdirs)
1876 destdir = g_strdup (d);
1877 move_over = TRUE;
1879 else
1880 destdir = concat_dir_and_file (d, x_basename (s));
1882 /* Check if the user inputted an existing dir */
1883 retry_dst_stat:
1884 if (!mc_stat (destdir, &destbuf))
1886 if (move_over)
1888 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
1890 if (return_status != FILE_CONT)
1891 goto ret;
1892 goto oktoret;
1894 else
1896 if (S_ISDIR (destbuf.st_mode))
1897 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
1898 else
1899 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
1900 if (return_status == FILE_RETRY)
1901 goto retry_dst_stat;
1903 g_free (destdir);
1904 return return_status;
1907 retry_rename:
1908 if (mc_rename (s, destdir) == 0)
1910 return_status = FILE_CONT;
1911 goto ret;
1914 if (errno != EXDEV)
1916 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
1917 if (return_status == FILE_RETRY)
1918 goto retry_rename;
1919 goto ret;
1921 /* Failed because of filesystem boundary -> copy dir instead */
1922 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
1924 if (return_status != FILE_CONT)
1925 goto ret;
1926 oktoret:
1927 file_progress_show_source (ctx, NULL);
1928 file_progress_show (ctx, 0, 0, "", FALSE);
1930 return_status = check_progress_buttons (ctx);
1931 if (return_status != FILE_CONT)
1932 goto ret;
1934 mc_refresh ();
1935 if (ctx->erase_at_end)
1937 for (; erase_list && return_status != FILE_ABORT;)
1939 if (S_ISDIR (erase_list->st_mode))
1941 return_status = erase_dir_iff_empty (ctx, erase_list->name);
1943 else
1944 return_status = erase_file (tctx, ctx, erase_list->name, FALSE);
1945 lp = erase_list;
1946 erase_list = erase_list->next;
1947 g_free (lp);
1950 erase_dir_iff_empty (ctx, s);
1952 ret:
1953 g_free (destdir);
1954 while (erase_list)
1956 lp = erase_list;
1957 erase_list = erase_list->next;
1958 g_free (lp);
1960 return return_status;
1963 /* }}} */
1965 /* --------------------------------------------------------------------------------------------- */
1966 /* {{{ Erase routines */
1968 FileProgressStatus
1969 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
1971 FileProgressStatus error;
1973 if (strcmp (s, "..") == 0)
1974 return FILE_SKIP;
1976 if (strcmp (s, ".") == 0)
1977 return FILE_SKIP;
1979 file_progress_show_deleting (ctx, s);
1980 if (check_progress_buttons (ctx) == FILE_ABORT)
1981 return FILE_ABORT;
1982 mc_refresh ();
1984 /* The old way to detect a non empty directory was:
1985 error = my_rmdir (s);
1986 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1987 For the linux user space nfs server (nfs-server-2.2beta29-2)
1988 we would have to check also for EIO. I hope the new way is
1989 fool proof. (Norbert)
1991 error = check_dir_is_empty (s);
1992 if (error == 0)
1993 { /* not empty */
1994 error = query_recursive (ctx, s);
1995 if (error == FILE_CONT)
1996 return recursive_erase (tctx, ctx, s);
1997 else
1998 return error;
2001 while (my_rmdir (s) == -1)
2003 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
2004 if (error != FILE_RETRY)
2005 return error;
2008 return FILE_CONT;
2011 /* }}} */
2013 /* --------------------------------------------------------------------------------------------- */
2014 /* {{{ Panel operate routines */
2016 ComputeDirSizeUI *
2017 compute_dir_size_create_ui (void)
2019 ComputeDirSizeUI *ui;
2021 const char *b_name = N_("&Abort");
2023 #ifdef ENABLE_NLS
2024 b_name = _(b_name);
2025 #endif
2027 ui = g_new (ComputeDirSizeUI, 1);
2029 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
2030 NULL, _("Directory scanning"), DLG_CENTER);
2031 ui->dirname = label_new (3, 3, "");
2032 add_widget (ui->dlg, ui->dirname);
2034 add_widget (ui->dlg,
2035 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
2036 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
2038 /* We will manage the dialog without any help,
2039 that's why we have to call init_dlg */
2040 init_dlg (ui->dlg);
2042 return ui;
2045 /* --------------------------------------------------------------------------------------------- */
2047 void
2048 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
2050 if (ui != NULL)
2052 /* schedule to update passive panel */
2053 other_panel->dirty = 1;
2055 /* close and destroy dialog */
2056 dlg_run_done (ui->dlg);
2057 destroy_dlg (ui->dlg);
2058 g_free (ui);
2062 /* --------------------------------------------------------------------------------------------- */
2064 FileProgressStatus
2065 compute_dir_size_update_ui (const void *ui, const char *dirname)
2067 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
2068 int c;
2069 Gpm_Event event;
2071 if (ui == NULL)
2072 return FILE_CONT;
2074 label_set_text (this->dirname, str_trunc (dirname, this->dlg->cols - 6));
2076 event.x = -1; /* Don't show the GPM cursor */
2077 c = tty_get_event (&event, FALSE, FALSE);
2078 if (c == EV_NONE)
2079 return FILE_CONT;
2081 /* Reinitialize to avoid old values after events other than
2082 selecting a button */
2083 this->dlg->ret_value = FILE_CONT;
2085 dlg_process_event (this->dlg, c, &event);
2087 switch (this->dlg->ret_value)
2089 case B_CANCEL:
2090 case FILE_ABORT:
2091 return FILE_ABORT;
2092 default:
2093 return FILE_CONT;
2097 /* --------------------------------------------------------------------------------------------- */
2099 * compute_dir_size:
2101 * Computes the number of bytes used by the files in a directory
2104 FileProgressStatus
2105 compute_dir_size (const char *dirname, const void *ui,
2106 compute_dir_size_callback cback,
2107 size_t * ret_marked, uintmax_t * ret_total, gboolean compute_symlinks)
2109 int res;
2110 struct stat s;
2111 DIR *dir;
2112 struct dirent *dirent;
2113 FileProgressStatus ret = FILE_CONT;
2115 if (!compute_symlinks)
2117 res = mc_lstat (dirname, &s);
2118 if (res != 0)
2119 return ret;
2121 /* don't scan symlink to directory */
2122 if (S_ISLNK (s.st_mode))
2124 (*ret_marked)++;
2125 *ret_total += (uintmax_t) s.st_size;
2126 return ret;
2130 dir = mc_opendir (dirname);
2132 if (dir == NULL)
2133 return ret;
2135 while ((dirent = mc_readdir (dir)) != NULL)
2137 char *fullname;
2139 ret = (cback != NULL) ? cback (ui, dirname) : FILE_CONT;
2141 if (ret != FILE_CONT)
2142 break;
2144 if (strcmp (dirent->d_name, ".") == 0)
2145 continue;
2146 if (strcmp (dirent->d_name, "..") == 0)
2147 continue;
2149 fullname = concat_dir_and_file (dirname, dirent->d_name);
2150 res = mc_lstat (fullname, &s);
2152 if (res != 0)
2154 g_free (fullname);
2155 continue;
2158 if (S_ISDIR (s.st_mode))
2160 size_t subdir_count = 0;
2161 uintmax_t subdir_bytes = 0;
2163 ret =
2164 compute_dir_size (fullname, ui, cback, &subdir_count, &subdir_bytes,
2165 compute_symlinks);
2167 if (ret != FILE_CONT)
2169 g_free (fullname);
2170 break;
2173 *ret_marked += subdir_count;
2174 *ret_total += subdir_bytes;
2176 else
2178 (*ret_marked)++;
2179 *ret_total += (uintmax_t) s.st_size;
2182 g_free (fullname);
2185 mc_closedir (dir);
2187 return ret;
2190 /* --------------------------------------------------------------------------------------------- */
2192 * panel_operate:
2194 * Performs one of the operations on the selection on the source_panel
2195 * (copy, delete, move).
2197 * Returns TRUE if did change the directory
2198 * structure, Returns FALSE if user aborted
2200 * force_single forces operation on the current entry and affects
2201 * default destination. Current filename is used as default.
2204 gboolean
2205 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2207 WPanel *panel = (WPanel *) source_panel;
2208 const gboolean single_entry = force_single || (panel->marked <= 1)
2209 || (get_current_type () == view_tree);
2211 char *source = NULL;
2212 #ifdef WITH_FULL_PATHS
2213 char *source_with_path = NULL;
2214 #else
2215 #define source_with_path source
2216 #endif /* !WITH_FULL_PATHS */
2217 char *dest = NULL;
2218 char *temp = NULL;
2219 char *save_cwd = NULL, *save_dest = NULL;
2220 struct stat src_stat;
2221 gboolean ret_val = TRUE;
2222 int i;
2223 FileProgressStatus value;
2224 FileOpContext *ctx;
2225 FileOpTotalContext *tctx;
2227 gboolean do_bg = FALSE; /* do background operation? */
2229 static gboolean i18n_flag = FALSE;
2230 if (!i18n_flag)
2232 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2233 op_names[i] = Q_ (op_names[i]);
2234 i18n_flag = TRUE;
2237 free_linklist (&linklist);
2238 free_linklist (&dest_dirs);
2240 /* Update panel contents to avoid actions on deleted files */
2241 if (!panel->is_panelized)
2243 update_panels (UP_RELOAD, UP_KEEPSEL);
2244 repaint_screen ();
2247 if (single_entry)
2249 if (force_single)
2251 source = selection (panel)->fname;
2252 src_stat = selection (panel)->st;
2254 else
2255 source = panel_get_file (panel, &src_stat);
2257 if (!strcmp (source, ".."))
2259 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2260 return FALSE;
2264 ctx = file_op_context_new (operation);
2265 tctx = file_op_total_context_new ();
2266 gettimeofday (&(tctx->transfer_start), (struct timezone *) NULL);
2268 /* Show confirmation dialog */
2269 if (operation != OP_DELETE)
2271 char *dest_dir;
2272 char *dest_dir_;
2273 char *format;
2275 /* Forced single operations default to the original name */
2276 if (force_single)
2277 dest_dir = source;
2278 else if (get_other_type () == view_listing)
2279 dest_dir = other_panel->cwd;
2280 else
2281 dest_dir = panel->cwd;
2283 * Add trailing backslash only when do non-local ops.
2284 * It saves user from occasional file renames (when destination
2285 * dir is deleted)
2287 if (!force_single && dest_dir[0] != '\0' && dest_dir[strlen (dest_dir) - 1] != PATH_SEP)
2289 /* add trailing separator */
2290 dest_dir_ = g_strconcat (dest_dir, PATH_SEP_STR, (char *) NULL);
2292 else
2294 /* just copy */
2295 dest_dir_ = g_strdup (dest_dir);
2297 if (dest_dir_ == NULL)
2299 file_op_total_context_destroy (tctx);
2300 file_op_context_destroy (ctx);
2301 return FALSE;
2304 /* Generate confirmation prompt */
2305 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2307 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2308 source != NULL ? (void *) source
2309 : (void *) &panel->marked, dest_dir_, &do_bg);
2311 g_free (format);
2312 g_free (dest_dir_);
2314 if (dest == NULL || dest[0] == '\0')
2316 file_op_total_context_destroy (tctx);
2317 file_op_context_destroy (ctx);
2318 g_free (dest);
2319 return FALSE;
2322 else if (confirm_delete)
2324 char *format;
2325 char fmd_buf[BUF_MEDIUM];
2327 /* Generate confirmation prompt */
2328 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2330 if (source == NULL)
2331 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2332 else
2334 const int fmd_xlen = 64;
2335 i = fmd_xlen - str_term_width1 (format) - 4;
2336 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2339 g_free (format);
2341 if (safe_delete)
2342 query_set_sel (1);
2344 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2346 if (i != 0)
2348 file_op_total_context_destroy (tctx);
2349 file_op_context_destroy (ctx);
2350 return FALSE;
2355 filegui_dialog_type_t dialog_type;
2357 if (operation == OP_DELETE)
2358 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2359 else
2361 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2362 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2364 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2365 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2368 /* Background also need ctx->ui, but not full */
2369 if (do_bg)
2370 file_op_context_create_ui_without_init (ctx, 1, dialog_type);
2371 else
2372 file_op_context_create_ui (ctx, 1, dialog_type);
2375 #ifdef WITH_BACKGROUND
2376 /* Did the user select to do a background operation? */
2377 if (do_bg)
2379 int v;
2381 v = do_background (ctx, g_strconcat (op_names[operation], ": ", panel->cwd, (char *) NULL));
2382 if (v == -1)
2383 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2385 /* If we are the parent */
2386 if (v == 1)
2388 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
2389 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
2390 /* file_op_context_destroy (ctx); */
2391 return FALSE;
2394 #endif /* WITH_BACKGROUND */
2396 /* Initialize things */
2397 /* We do not want to trash cache every time file is
2398 created/touched. However, this will make our cache contain
2399 invalid data. */
2400 if ((dest != NULL) && (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1)))
2401 save_dest = g_strdup (dest);
2403 if ((panel->cwd[0] != '\0') && (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1)))
2404 save_cwd = g_strdup (panel->cwd);
2406 /* Now, let's do the job */
2408 /* This code is only called by the tree and panel code */
2409 if (single_entry)
2411 /* We now have ETA in all cases */
2413 /* One file: FIXME mc_chdir will take user out of any vfs */
2414 if ((operation != OP_COPY) && (get_current_type () == view_tree) &&
2415 (mc_chdir (PATH_SEP_STR) < 0))
2417 ret_val = FALSE;
2418 goto clean_up;
2421 /* The source and src_stat variables have been initialized before */
2422 #ifdef WITH_FULL_PATHS
2423 if (g_path_is_absolute (source))
2424 source_with_path = g_strdup (source);
2425 else
2426 source_with_path = g_build_filename (panel->cwd, source, (char *) NULL);
2427 #endif /* WITH_FULL_PATHS */
2429 if (panel_operate_init_totals (operation, panel, source_with_path, ctx) == FILE_CONT)
2431 if (operation == OP_DELETE)
2433 if (S_ISDIR (src_stat.st_mode))
2434 value = erase_dir (tctx, ctx, source_with_path);
2435 else
2436 value = erase_file (tctx, ctx, source_with_path, 1);
2438 else
2440 temp = transform_source (ctx, source_with_path);
2441 if (temp == NULL)
2442 value = transform_error;
2443 else
2445 char *repl_dest, *temp2;
2447 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2448 temp2 = concat_dir_and_file (repl_dest, temp);
2449 g_free (temp);
2450 g_free (repl_dest);
2451 g_free (dest);
2452 dest = temp2;
2454 switch (operation)
2456 case OP_COPY:
2457 /* we use file_mask_op_follow_links only with OP_COPY */
2458 ctx->stat_func (source_with_path, &src_stat);
2460 if (S_ISDIR (src_stat.st_mode))
2461 value = copy_dir_dir (tctx, ctx, source_with_path, dest,
2462 TRUE, FALSE, FALSE, NULL);
2463 else
2464 value = copy_file_file (tctx, ctx, source_with_path, dest);
2465 break;
2467 case OP_MOVE:
2468 if (S_ISDIR (src_stat.st_mode))
2469 value = move_dir_dir (tctx, ctx, source_with_path, dest);
2470 else
2471 value = move_file_file (tctx, ctx, source_with_path, dest);
2472 break;
2474 default:
2475 /* Unknown file operation */
2476 abort ();
2479 } /* Copy or move operation */
2481 if ((value == FILE_CONT) && !force_single)
2482 unmark_files (panel);
2485 else
2487 /* Many files */
2489 /* Check destination for copy or move operation */
2490 while (operation != OP_DELETE)
2492 int dst_result;
2493 struct stat dst_stat;
2495 dst_result = mc_stat (dest, &dst_stat);
2497 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2498 break;
2500 if (file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2501 goto clean_up;
2504 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2506 /* Loop for every file, perform the actual copy operation */
2507 for (i = 0; i < panel->count; i++)
2509 if (!panel->dir.list[i].f.marked)
2510 continue; /* Skip the unmarked ones */
2512 source = panel->dir.list[i].fname;
2513 src_stat = panel->dir.list[i].st;
2515 #ifdef WITH_FULL_PATHS
2516 g_free (source_with_path);
2517 if (g_path_is_absolute (source))
2518 source_with_path = g_strdup (source);
2519 else
2520 source_with_path = g_build_filename (panel->cwd, source, (char *) NULL);
2521 #endif /* WITH_FULL_PATHS */
2523 if (operation == OP_DELETE)
2525 if (S_ISDIR (src_stat.st_mode))
2526 value = erase_dir (tctx, ctx, source_with_path);
2527 else
2528 value = erase_file (tctx, ctx, source_with_path, 1);
2530 else
2532 temp = transform_source (ctx, source_with_path);
2534 if (temp == NULL)
2535 value = transform_error;
2536 else
2538 char *temp2, *temp3, *repl_dest;
2540 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2541 temp2 = concat_dir_and_file (repl_dest, temp);
2542 g_free (temp);
2543 g_free (repl_dest);
2544 temp3 = source_with_path;
2545 source_with_path = strutils_shell_unescape (source_with_path);
2546 g_free (temp3);
2547 temp3 = temp2;
2548 temp2 = strutils_shell_unescape (temp2);
2549 g_free (temp3);
2551 switch (operation)
2553 case OP_COPY:
2554 /* we use file_mask_op_follow_links only with OP_COPY */
2555 ctx->stat_func (source_with_path, &src_stat);
2556 if (S_ISDIR (src_stat.st_mode))
2557 value = copy_dir_dir (tctx, ctx, source_with_path, temp2,
2558 TRUE, FALSE, FALSE, NULL);
2559 else
2560 value = copy_file_file (tctx, ctx, source_with_path, temp2);
2561 free_linklist (&dest_dirs);
2562 break;
2564 case OP_MOVE:
2565 if (S_ISDIR (src_stat.st_mode))
2566 value = move_dir_dir (tctx, ctx, source_with_path, temp2);
2567 else
2568 value = move_file_file (tctx, ctx, source_with_path, temp2);
2569 break;
2571 default:
2572 /* Unknown file operation */
2573 abort ();
2576 g_free (temp2);
2578 } /* Copy or move operation */
2580 if (value == FILE_ABORT)
2581 break;
2583 if (value == FILE_CONT)
2584 do_file_mark (panel, i, 0);
2586 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2588 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2589 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
2592 if (operation != OP_DELETE)
2593 file_progress_show (ctx, 0, 0, "", FALSE);
2595 if (check_progress_buttons (ctx) == FILE_ABORT)
2596 break;
2598 mc_refresh ();
2599 } /* Loop for every file */
2601 } /* Many entries */
2603 clean_up:
2604 /* Clean up */
2605 if (save_cwd != NULL)
2607 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2608 g_free (save_cwd);
2611 if (save_dest != NULL)
2613 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2614 g_free (save_dest);
2617 free_linklist (&linklist);
2618 free_linklist (&dest_dirs);
2619 #ifdef WITH_FULL_PATHS
2620 g_free (source_with_path);
2621 #endif /* WITH_FULL_PATHS */
2622 g_free (dest);
2623 g_free (ctx->dest_mask);
2624 ctx->dest_mask = NULL;
2626 #ifdef WITH_BACKGROUND
2627 /* Let our parent know we are saying bye bye */
2628 if (we_are_background)
2630 int cur_pid = getpid ();
2631 /* Send pid to parent with child context, it is fork and
2632 don't modify real parent ctx */
2633 ctx->pid = cur_pid;
2634 parent_call ((void *) end_bg_process, ctx, 0);
2636 vfs_shut ();
2637 _exit (0);
2639 #endif /* WITH_BACKGROUND */
2641 file_op_context_destroy (ctx);
2642 file_op_total_context_destroy (tctx);
2644 return ret_val;
2647 /* }}} */
2649 /* --------------------------------------------------------------------------------------------- */
2650 /* {{{ Query/status report routines */
2651 /** Report error with one file */
2652 FileProgressStatus
2653 file_error (const char *format, const char *file)
2655 char buf[BUF_MEDIUM];
2657 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
2659 return do_file_error (buf);
2662 /* --------------------------------------------------------------------------------------------- */
2665 Cause emacs to enter folding mode for this file:
2666 Local variables:
2667 end: