Ticket #2097: clean up before 4.7.2 release.
[midnight-commander.git] / src / file.c
blob80fb58b1ab4110d4c4580adc5f2beab684043f32
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/vfs/mc-vfs/vfs-impl.h"
63 #include "lib/vfs/mc-vfs/vfs.h"
64 #include "lib/strescape.h"
65 #include "lib/strutil.h"
67 #include "setup.h"
68 #include "dialog.h"
69 #include "widget.h"
70 #include "main.h"
71 #include "layout.h"
72 #include "widget.h"
73 #include "wtools.h"
74 #include "background.h" /* we_are_background */
76 /* Needed for current_panel, other_panel and WTree */
77 #include "dir.h"
78 #include "panel.h"
79 #include "file.h"
80 #include "filegui.h"
81 #include "tree.h"
83 /* }}} */
85 /* Hack: the vfs code should not rely on this */
86 #define WITH_FULL_PATHS 1
88 #define FILEOP_UPDATE_INTERVAL 2
89 #define FILEOP_STALLING_INTERVAL 4
91 int verbose = 1;
94 * Whether the Midnight Commander tries to provide more
95 * information about copy/move sizes and bytes transfered
96 * at the expense of some speed
98 int file_op_compute_totals = 1;
100 /* This is a hard link cache */
101 struct link
103 struct link *next;
104 struct vfs_class *vfs;
105 dev_t dev;
106 ino_t ino;
107 short linkcount;
108 mode_t st_mode;
109 char name[1];
112 /* the hard link cache */
113 static struct link *linklist = NULL;
115 /* the files-to-be-erased list */
116 static struct link *erase_list;
119 * In copy_dir_dir we use two additional single linked lists: The first -
120 * variable name `parent_dirs' - holds information about already copied
121 * directories and is used to detect cyclic symbolic links.
122 * The second (`dest_dirs' below) holds information about just created
123 * target directories and is used to detect when an directory is copied
124 * into itself (we don't want to copy infinitly).
125 * Both lists don't use the linkcount and name structure members of struct
126 * link.
128 static struct link *dest_dirs = NULL;
130 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
131 const char *op_names[3] = {
132 N_("DialogTitle|Copy"),
133 N_("DialogTitle|Move"),
134 N_("DialogTitle|Delete")
137 /* }}} */
139 static FileProgressStatus query_replace (FileOpContext * ctx, const char *destname,
140 struct stat *_s_stat, struct stat *_d_stat);
141 static FileProgressStatus query_recursive (FileOpContext * ctx, const char *s);
142 static FileProgressStatus do_file_error (const char *str);
143 static FileProgressStatus erase_dir_iff_empty (FileOpContext * ctx, const char *s);
144 static FileProgressStatus erase_file (FileOpTotalContext * tctx, FileOpContext * ctx,
145 const char *s, gboolean is_toplevel_file);
146 static FileProgressStatus files_error (const char *format, const char *file1, const char *file2);
148 static FileProgressStatus transform_error = FILE_CONT;
150 static char *
151 transform_source (FileOpContext * ctx, const char *source)
153 char *s, *q;
154 char *fnsource;
156 s = g_strdup (source);
158 /* We remove \n from the filename since regex routines would use \n as an anchor */
159 /* this is just to be allowed to maniupulate file names with \n on it */
160 for (q = s; *q != '\0'; q++)
161 if (*q == '\n')
162 *q = ' ';
164 fnsource = (char *) x_basename (s);
166 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
167 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
168 else
170 q = NULL;
171 transform_error = FILE_SKIP;
174 g_free (s);
175 return q;
178 static void
179 free_linklist (struct link **lc_linklist)
181 struct link *lp, *lp2;
183 for (lp = *lc_linklist; lp != NULL; lp = lp2)
185 lp2 = lp->next;
186 g_free (lp);
188 *lc_linklist = NULL;
191 static int
192 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
194 ino_t ino = sb->st_ino;
195 dev_t dev = sb->st_dev;
196 #ifdef ENABLE_VFS
197 struct vfs_class *vfs = vfs_get_class (path);
198 #endif /* ENABLE_VFS */
200 (void) path;
202 while (lp)
204 #ifdef ENABLE_VFS
205 if (lp->vfs == vfs)
206 #endif /* ENABLE_VFS */
207 if (lp->ino == ino && lp->dev == dev)
208 return 1;
209 lp = lp->next;
211 return 0;
215 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
216 * and a hardlink was succesfully made
218 static int
219 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
221 struct link *lp;
222 struct vfs_class *my_vfs = vfs_get_class (src_name);
223 ino_t ino = pstat->st_ino;
224 dev_t dev = pstat->st_dev;
225 struct stat link_stat;
226 const char *p;
228 if (vfs_file_class_flags (src_name) & VFSF_NOLINKS)
229 return 0;
231 for (lp = linklist; lp != NULL; lp = lp->next)
232 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev)
234 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
235 && link_stat.st_dev == dev && vfs_get_class (lp->name) == my_vfs)
237 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
238 was copied to */
239 if (vfs_get_class (dst_name) == vfs_get_class (p))
241 if (!mc_stat (p, &link_stat))
243 if (!mc_link (p, dst_name))
244 return 1;
248 message (D_ERROR, MSG_ERROR, _(" Cannot make the hardlink "));
249 return 0;
251 lp = (struct link *) g_try_malloc (sizeof (struct link) + strlen (src_name)
252 + strlen (dst_name) + 1);
253 if (lp)
255 char *lpdstname;
256 lp->vfs = my_vfs;
257 lp->ino = ino;
258 lp->dev = dev;
259 strcpy (lp->name, src_name);
260 lpdstname = lp->name + strlen (lp->name) + 1;
261 strcpy (lpdstname, dst_name);
262 lp->next = linklist;
263 linklist = lp;
265 return 0;
269 * Duplicate the contents of the symbolic link src_path in dst_path.
270 * Try to make a stable symlink if the option "stable symlink" was
271 * set in the file mask dialog.
272 * If dst_path is an existing symlink it will be deleted silently
273 * (upper levels take already care of existing files at dst_path).
275 static FileProgressStatus
276 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
278 char link_target[MC_MAXPATHLEN];
279 int len;
280 FileProgressStatus return_status;
281 struct stat sb;
282 gboolean dst_is_symlink;
284 dst_is_symlink = (mc_lstat (dst_path, &sb) == 0) && S_ISLNK (sb.st_mode);
286 retry_src_readlink:
287 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
288 if (len < 0)
290 return_status = file_error (_(" Cannot read source link \"%s\" \n %s "), src_path);
291 if (return_status == FILE_RETRY)
292 goto retry_src_readlink;
293 return return_status;
295 link_target[len] = 0;
297 if (ctx->stable_symlinks)
298 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path))
300 message (D_ERROR, MSG_ERROR,
301 _(" Cannot make stable symlinks across "
302 "non-local filesystems: \n\n" " Option Stable Symlinks will be disabled "));
303 ctx->stable_symlinks = FALSE;
306 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
308 char *p, *q, *s;
310 const char *r = strrchr (src_path, PATH_SEP);
312 if (r)
314 p = g_strndup (src_path, r - src_path + 1);
315 if (g_path_is_absolute (dst_path))
316 q = g_strdup (dst_path);
317 else
318 q = g_strconcat (p, dst_path, (char *) NULL);
319 s = strrchr (q, PATH_SEP);
320 if (s)
322 s[1] = 0;
323 s = g_strconcat (p, link_target, (char *) NULL);
324 g_free (p);
325 g_strlcpy (link_target, s, sizeof (link_target));
326 g_free (s);
327 s = diff_two_paths (q, link_target);
328 if (s)
330 g_strlcpy (link_target, s, sizeof (link_target));
331 g_free (s);
334 else
335 g_free (p);
336 g_free (q);
339 retry_dst_symlink:
340 if (mc_symlink (link_target, dst_path) == 0)
341 /* Success */
342 return FILE_CONT;
344 * if dst_exists, it is obvious that this had failed.
345 * We can delete the old symlink and try again...
347 if (dst_is_symlink)
349 if (!mc_unlink (dst_path))
350 if (mc_symlink (link_target, dst_path) == 0)
351 /* Success */
352 return FILE_CONT;
354 return_status = file_error (_(" Cannot create target symlink \"%s\" \n %s "), dst_path);
355 if (return_status == FILE_RETRY)
356 goto retry_dst_symlink;
357 return return_status;
360 static FileProgressStatus
361 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add,
362 gboolean is_toplevel_file)
364 struct timeval tv_current;
365 static struct timeval tv_start = { };
367 if (is_toplevel_file || ctx->progress_totals_computed)
369 tctx->progress_count++;
370 tctx->progress_bytes += add;
372 if (tv_start.tv_sec == 0)
374 gettimeofday (&tv_start, (struct timezone *) NULL);
376 gettimeofday (&tv_current, (struct timezone *) NULL);
377 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
379 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
380 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
381 tv_start.tv_sec = tv_current.tv_sec;
384 return check_progress_buttons (ctx);
387 /* Status of the destination file */
388 typedef enum
390 DEST_NONE = 0, /* Not created */
391 DEST_SHORT = 1, /* Created, not fully copied */
392 DEST_FULL = 2 /* Created, fully copied */
393 } dest_status_t;
395 static FileProgressStatus
396 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
398 char *msg;
399 int result = 0;
400 const char *head_msg;
402 head_msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
404 msg = g_strdup_printf (fmt, a, b);
405 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
406 g_free (msg);
407 do_refresh ();
409 return (result == 1) ? FILE_ABORT : FILE_SKIP;
412 #ifdef WITH_BACKGROUND
413 static FileProgressStatus
414 warn_same_file (const char *fmt, const char *a, const char *b)
416 union
418 void *p;
419 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
420 const char *a, const char *b);
421 } pntr;
422 pntr.f = real_warn_same_file;
424 if (we_are_background)
425 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
426 else
427 return real_warn_same_file (Foreground, fmt, a, b);
429 #else
430 static FileProgressStatus
431 warn_same_file (const char *fmt, const char *a, const char *b)
433 return real_warn_same_file (Foreground, fmt, a, b);
435 #endif
437 static void
438 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
439 struct timeval tv_current, struct timeval tv_transfer_start,
440 off_t file_size, off_t n_read_total)
442 long dt;
444 /* 1. Update rotating dash after some time */
445 rotate_dash ();
447 /* 3. Compute ETA */
448 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
450 if (n_read_total)
452 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
453 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
455 else
456 ctx->eta_secs = 0.0;
458 /* 4. Compute BPS rate */
459 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
460 if (ctx->bps_time < 1)
461 ctx->bps_time = 1;
462 ctx->bps = n_read_total / ctx->bps_time;
464 /* 5. Compute total ETA and BPS */
465 if (ctx->progress_bytes != 0)
467 double remain_bytes;
468 tctx->copyed_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
469 remain_bytes = ctx->progress_bytes - tctx->copyed_bytes;
470 #if 1
472 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
474 if (total_secs < 1)
475 total_secs = 1;
476 tctx->bps = tctx->copyed_bytes / total_secs;
477 tctx->eta_secs = remain_bytes / tctx->bps;
479 #else
480 /* broken on lot of little files */
481 tctx->bps_count++;
482 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
483 tctx->eta_secs = remain_bytes / tctx->bps;
484 #endif
488 FileProgressStatus
489 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
490 const char *src_path, const char *dst_path)
492 uid_t src_uid = (uid_t) - 1;
493 gid_t src_gid = (gid_t) - 1;
495 int src_desc, dest_desc = -1;
496 int n_read, n_written;
497 mode_t src_mode = 0; /* The mode of the source file */
498 struct stat sb, sb2;
499 struct utimbuf utb;
500 gboolean dst_exists = FALSE, appending = FALSE;
501 off_t n_read_total = 0, file_size = -1;
502 FileProgressStatus return_status, temp_status;
503 struct timeval tv_transfer_start;
504 dest_status_t dst_status = DEST_NONE;
505 int open_flags;
506 gboolean is_first_time = TRUE;
508 /* FIXME: We should not be using global variables! */
509 ctx->do_reget = 0;
510 return_status = FILE_RETRY;
512 file_progress_show_source (ctx, src_path);
513 file_progress_show_target (ctx, dst_path);
514 if (check_progress_buttons (ctx) == FILE_ABORT)
515 return FILE_ABORT;
517 mc_refresh ();
519 while (mc_stat (dst_path, &sb2) == 0)
521 if (S_ISDIR (sb2.st_mode))
523 return_status = file_error (_(" Cannot overwrite directory \"%s\" \n %s "), dst_path);
524 if (return_status == FILE_RETRY)
525 continue;
526 return return_status;
528 dst_exists = TRUE;
529 break;
532 while ((*ctx->stat_func) (src_path, &sb))
534 return_status = file_error (_(" Cannot stat source file \"%s\" \n %s "), src_path);
535 if (return_status != FILE_RETRY)
536 return return_status;
539 if (dst_exists)
541 /* Destination already exists */
542 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
543 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "),
544 src_path, dst_path);
545 /* Should we replace destination? */
546 if (tctx->ask_overwrite)
548 ctx->do_reget = 0;
549 return_status = query_replace (ctx, dst_path, &sb, &sb2);
550 if (return_status != FILE_CONT)
551 return return_status;
555 if (!ctx->do_append)
557 /* Check the hardlinks */
558 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb) == 1)
560 /* We have made a hardlink - no more processing is necessary */
561 return FILE_CONT;
564 if (S_ISLNK (sb.st_mode))
565 return make_symlink (ctx, src_path, dst_path);
567 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
568 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
570 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0)
572 return_status =
573 file_error (_(" Cannot create special file \"%s\" \n %s "), dst_path);
574 if (return_status == FILE_RETRY)
575 continue;
576 return return_status;
578 /* Success */
580 while (ctx->preserve_uidgid && mc_chown (dst_path, sb.st_uid, sb.st_gid))
582 temp_status = file_error (_(" Cannot chown target file \"%s\" \n %s "), dst_path);
583 if (temp_status == FILE_RETRY)
584 continue;
585 return temp_status;
587 while (ctx->preserve && mc_chmod (dst_path, sb.st_mode & ctx->umask_kill))
589 temp_status = file_error (_(" Cannot chmod target file \"%s\" \n %s "), dst_path);
590 if (temp_status == FILE_RETRY)
591 continue;
592 return temp_status;
594 return FILE_CONT;
598 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
600 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0)
602 return_status = file_error (_(" Cannot open source file \"%s\" \n %s "), src_path);
603 if (return_status == FILE_RETRY)
604 continue;
605 ctx->do_append = 0;
606 return return_status;
609 if (ctx->do_reget != 0)
611 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
613 message (D_ERROR, _("Warning"), _(" Reget failed, about to overwrite file "));
614 ctx->do_reget = 0;
615 ctx->do_append = FALSE;
619 while (mc_fstat (src_desc, &sb))
621 return_status = file_error (_(" Cannot fstat source file \"%s\" \n %s "), src_path);
622 if (return_status == FILE_RETRY)
623 continue;
624 ctx->do_append = FALSE;
625 goto ret;
627 src_mode = sb.st_mode;
628 src_uid = sb.st_uid;
629 src_gid = sb.st_gid;
630 utb.actime = sb.st_atime;
631 utb.modtime = sb.st_mtime;
632 file_size = sb.st_size;
634 open_flags = O_WRONLY;
635 if (dst_exists)
637 if (ctx->do_append != 0)
638 open_flags |= O_APPEND;
639 else
640 open_flags |= O_CREAT | O_TRUNC;
642 else
644 open_flags |= O_CREAT | O_EXCL;
647 while ((dest_desc = mc_open (dst_path, open_flags, src_mode)) < 0)
649 if (errno == EEXIST)
650 goto ret;
652 return_status = file_error (_(" Cannot create target file \"%s\" \n %s "), dst_path);
653 if (return_status == FILE_RETRY)
654 continue;
655 ctx->do_append = FALSE;
656 goto ret;
658 dst_status = DEST_SHORT; /* file opened, but not fully copied */
660 appending = ctx->do_append;
661 ctx->do_append = FALSE;
663 /* Find out the optimal buffer size. */
664 while (mc_fstat (dest_desc, &sb))
666 return_status = file_error (_(" Cannot fstat target file \"%s\" \n %s "), dst_path);
667 if (return_status == FILE_RETRY)
668 continue;
669 goto ret;
672 ctx->eta_secs = 0.0;
673 ctx->bps = 0;
675 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
676 file_progress_show (ctx, 0, file_size, "", TRUE);
677 else
678 file_progress_show (ctx, 1, 1, "", TRUE);
679 return_status = check_progress_buttons (ctx);
680 mc_refresh ();
682 if (return_status != FILE_CONT)
683 goto ret;
686 struct timeval tv_current, tv_last_update, tv_last_input;
687 int secs, update_secs;
688 const char *stalled_msg = "";
690 tv_last_update = tv_transfer_start;
692 for (;;)
694 char buf[BUF_8K];
696 /* src_read */
697 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
698 n_read = -1;
699 else
700 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0)
702 return_status =
703 file_error (_(" Cannot read source file \"%s\" \n %s "), src_path);
704 if (return_status == FILE_RETRY)
705 continue;
706 goto ret;
708 if (n_read == 0)
709 break;
711 gettimeofday (&tv_current, NULL);
713 if (n_read > 0)
715 char *t = buf;
716 n_read_total += n_read;
718 /* Windows NT ftp servers report that files have no
719 * permissions: -------, so if we happen to have actually
720 * read something, we should fix the permissions.
722 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
723 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
724 gettimeofday (&tv_last_input, NULL);
726 /* dst_write */
727 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read)
729 if (n_written > 0)
731 n_read -= n_written;
732 t += n_written;
733 continue;
735 return_status =
736 file_error (_(" Cannot write target file \"%s\" \n %s "), dst_path);
737 if (return_status != FILE_RETRY)
738 goto ret;
741 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
742 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
744 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
746 copy_file_file_display_progress (tctx, ctx,
747 tv_current,
748 tv_transfer_start, file_size, n_read_total);
749 tv_last_update = tv_current;
751 is_first_time = FALSE;
753 if (update_secs > FILEOP_STALLING_INTERVAL)
755 stalled_msg = _("(stalled)");
759 gboolean force_update =
760 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
761 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
762 file_progress_show_total (tctx, ctx,
763 tctx->progress_bytes + n_read_total + ctx->do_reget,
764 force_update);
767 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
768 force_update);
770 mc_refresh ();
772 return_status = check_progress_buttons (ctx);
774 if (return_status != FILE_CONT)
776 mc_refresh ();
777 goto ret;
782 dst_status = DEST_FULL; /* copy successful, don't remove target file */
784 ret:
785 while (src_desc != -1 && mc_close (src_desc) < 0)
787 temp_status = file_error (_(" Cannot close source file \"%s\" \n %s "), src_path);
788 if (temp_status == FILE_RETRY)
789 continue;
790 if (temp_status == FILE_ABORT)
791 return_status = temp_status;
792 break;
795 while (dest_desc != -1 && mc_close (dest_desc) < 0)
797 temp_status = file_error (_(" Cannot close target file \"%s\" \n %s "), dst_path);
798 if (temp_status == FILE_RETRY)
799 continue;
800 return_status = temp_status;
801 break;
804 if (dst_status == DEST_SHORT)
806 /* Remove short file */
807 int result;
808 result = query_dialog (Q_ ("DialogTitle|Copy"),
809 _("Incomplete file was retrieved. Keep it?"),
810 D_ERROR, 2, _("&Delete"), _("&Keep"));
811 if (result == 0)
812 mc_unlink (dst_path);
814 else if (dst_status == DEST_FULL)
816 /* Copy has succeeded */
817 if (!appending && ctx->preserve_uidgid)
819 while (mc_chown (dst_path, src_uid, src_gid))
821 temp_status = file_error (_(" Cannot chown target file \"%s\" \n %s "), dst_path);
822 if (temp_status == FILE_RETRY)
823 continue;
824 return_status = temp_status;
825 break;
829 if (!appending)
831 if (ctx->preserve)
833 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill)))
835 temp_status =
836 file_error (_(" Cannot chmod target file \"%s\" \n %s "), dst_path);
837 if (temp_status != FILE_RETRY)
839 return_status = temp_status;
840 break;
844 else
846 src_mode = umask (-1);
847 umask (src_mode);
848 src_mode = 0100666 & ~src_mode;
849 mc_chmod (dst_path, (src_mode & ctx->umask_kill));
851 mc_utime (dst_path, &utb);
855 if (return_status == FILE_CONT)
856 return_status = progress_update_one (tctx, ctx, file_size, tctx->is_toplevel_file);
858 return return_status;
862 * I think these copy_*_* functions should have a return type.
863 * anyway, this function *must* have two directories as arguments.
865 /* FIXME: This function needs to check the return values of the
866 function calls */
867 FileProgressStatus
868 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
869 gboolean toplevel, gboolean move_over, gboolean do_delete, struct link * parent_dirs)
871 struct dirent *next;
872 struct stat buf, cbuf;
873 DIR *reading;
874 char *dest_dir = NULL;
875 FileProgressStatus return_status = FILE_CONT;
876 struct utimbuf utb;
877 struct link *lp;
878 char *d;
880 d = strutils_shell_unescape (_d);
882 /* First get the mode of the source dir */
883 retry_src_stat:
884 if ((*ctx->stat_func) (s, &cbuf))
886 return_status = file_error (_(" Cannot stat source directory \"%s\" \n %s "), s);
887 if (return_status == FILE_RETRY)
888 goto retry_src_stat;
889 goto ret_fast;
892 if (is_in_linklist (dest_dirs, s, &cbuf))
894 /* Don't copy a directory we created before (we don't want to copy
895 infinitely if a directory is copied into itself) */
896 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
897 return_status = FILE_CONT;
898 goto ret_fast;
901 /* Hmm, hardlink to directory??? - Norbert */
902 /* FIXME: In this step we should do something
903 in case the destination already exist */
904 /* Check the hardlinks */
905 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf) == 1)
907 /* We have made a hardlink - no more processing is necessary */
908 goto ret_fast;
911 if (!S_ISDIR (cbuf.st_mode))
913 return_status = file_error (_(" Source \"%s\" is not a directory \n %s "), s);
914 if (return_status == FILE_RETRY)
915 goto retry_src_stat;
916 goto ret_fast;
919 if (is_in_linklist (parent_dirs, s, &cbuf))
921 /* we found a cyclic symbolic link */
922 message (D_ERROR, MSG_ERROR, _(" Cannot copy cyclic symbolic link \n `%s' "), s);
923 return_status = FILE_SKIP;
924 goto ret_fast;
927 lp = g_new (struct link, 1);
928 lp->vfs = vfs_get_class (s);
929 lp->ino = cbuf.st_ino;
930 lp->dev = cbuf.st_dev;
931 lp->next = parent_dirs;
932 parent_dirs = lp;
934 retry_dst_stat:
935 /* Now, check if the dest dir exists, if not, create it. */
936 if (mc_stat (d, &buf))
938 /* Here the dir doesn't exist : make it ! */
939 if (move_over)
941 if (mc_rename (s, d) == 0)
943 return_status = FILE_CONT;
944 goto ret;
947 dest_dir = d;
948 d = NULL;
950 else
953 * If the destination directory exists, we want to copy the whole
954 * directory, but we only want this to happen once.
956 * Escape sequences added to the * to compiler warnings.
957 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
958 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
960 if (!S_ISDIR (buf.st_mode))
962 return_status = file_error (_(" Destination \"%s\" must be a directory \n %s "), d);
963 if (return_status == FILE_RETRY)
964 goto retry_dst_stat;
965 goto ret;
967 /* Dive into subdir if exists */
968 if (toplevel && ctx->dive_into_subdirs)
970 dest_dir = concat_dir_and_file (d, x_basename (s));
972 else
974 dest_dir = d;
975 d = NULL;
976 goto dont_mkdir;
979 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU))
981 return_status = file_error (_(" Cannot create target directory \"%s\" \n %s "), dest_dir);
982 if (return_status != FILE_RETRY)
983 goto ret;
986 lp = g_new (struct link, 1);
987 mc_stat (dest_dir, &buf);
988 lp->vfs = vfs_get_class (dest_dir);
989 lp->ino = buf.st_ino;
990 lp->dev = buf.st_dev;
991 lp->next = dest_dirs;
992 dest_dirs = lp;
994 if (ctx->preserve_uidgid)
996 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid))
998 return_status =
999 file_error (_(" Cannot chown target directory \"%s\" \n %s "), dest_dir);
1000 if (return_status != FILE_RETRY)
1001 goto ret;
1005 dont_mkdir:
1006 /* open the source dir for reading */
1007 reading = mc_opendir (s);
1008 if (reading == NULL)
1009 goto ret;
1011 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1013 char *path;
1015 * Now, we don't want '.' and '..' to be created / copied at any time
1017 if (!strcmp (next->d_name, "."))
1018 continue;
1019 if (!strcmp (next->d_name, ".."))
1020 continue;
1022 /* get the filename and add it to the src directory */
1023 path = concat_dir_and_file (s, next->d_name);
1025 (*ctx->stat_func) (path, &buf);
1026 if (S_ISDIR (buf.st_mode))
1028 char *mdpath;
1030 mdpath = concat_dir_and_file (dest_dir, next->d_name);
1032 * From here, we just intend to recursively copy subdirs, not
1033 * the double functionality of copying different when the target
1034 * dir already exists. So, we give the recursive call the flag 0
1035 * meaning no toplevel.
1037 return_status =
1038 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
1039 g_free (mdpath);
1041 else
1043 char *dest_file;
1045 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
1046 return_status = copy_file_file (tctx, ctx, path, dest_file);
1047 g_free (dest_file);
1049 if (do_delete && return_status == FILE_CONT)
1051 if (ctx->erase_at_end)
1053 static struct link *tail;
1054 size_t len = strlen (path);
1055 lp = g_malloc (sizeof (struct link) + len);
1056 strncpy (lp->name, path, len + 1);
1057 lp->st_mode = buf.st_mode;
1058 lp->next = NULL;
1059 if (erase_list != NULL)
1061 tail->next = lp;
1062 tail = lp;
1064 else
1065 erase_list = tail = lp;
1067 else
1069 if (S_ISDIR (buf.st_mode))
1071 return_status = erase_dir_iff_empty (ctx, path);
1073 else
1074 return_status = erase_file (tctx, ctx, path, FALSE);
1077 g_free (path);
1079 mc_closedir (reading);
1081 if (ctx->preserve)
1083 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1084 utb.actime = cbuf.st_atime;
1085 utb.modtime = cbuf.st_mtime;
1086 mc_utime (dest_dir, &utb);
1088 else
1090 cbuf.st_mode = umask (-1);
1091 umask (cbuf.st_mode);
1092 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
1093 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1096 ret:
1097 g_free (dest_dir);
1098 g_free (parent_dirs);
1099 ret_fast:
1100 g_free (d);
1101 return return_status;
1104 /* }}} */
1106 /* {{{ Move routines */
1108 static FileProgressStatus
1109 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
1111 struct stat src_stats, dst_stats;
1112 FileProgressStatus return_status = FILE_CONT;
1113 gboolean copy_done = FALSE;
1114 gboolean old_ask_overwrite;
1116 file_progress_show_source (ctx, s);
1117 file_progress_show_target (ctx, d);
1118 if (check_progress_buttons (ctx) == FILE_ABORT)
1119 return FILE_ABORT;
1121 mc_refresh ();
1123 while (mc_lstat (s, &src_stats) != 0)
1125 /* Source doesn't exist */
1126 return_status = file_error (_(" Cannot stat file \"%s\" \n %s "), s);
1127 if (return_status != FILE_RETRY)
1128 return return_status;
1131 if (mc_lstat (d, &dst_stats) == 0)
1133 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
1134 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "), s, d);
1136 if (S_ISDIR (dst_stats.st_mode))
1138 message (D_ERROR, MSG_ERROR, _(" Cannot overwrite directory `%s' "), d);
1139 do_refresh ();
1140 return FILE_SKIP;
1143 if (confirm_overwrite)
1145 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1146 if (return_status != FILE_CONT)
1147 return return_status;
1149 /* Ok to overwrite */
1152 if (!ctx->do_append)
1154 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
1156 return_status = make_symlink (ctx, s, d);
1157 if (return_status == FILE_CONT)
1158 goto retry_src_remove;
1159 else
1160 return return_status;
1163 if (mc_rename (s, d) == 0)
1164 return progress_update_one (tctx, ctx, src_stats.st_size, TRUE);
1166 #if 0
1167 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1168 one nfs to the same, but on the server it is on two different
1169 filesystems. Then nfs returns EIO instead of EXDEV.
1170 Hope it will not hurt if we always in case of error try to copy/delete. */
1171 else
1172 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1174 if (errno != EXDEV)
1176 return_status = files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s, d);
1177 if (return_status == FILE_RETRY)
1178 goto retry_rename;
1179 return return_status;
1181 #endif
1183 /* Failed because filesystem boundary -> copy the file instead */
1184 old_ask_overwrite = tctx->ask_overwrite;
1185 tctx->ask_overwrite = FALSE;
1186 return_status = copy_file_file (tctx, ctx, s, d);
1187 tctx->ask_overwrite = old_ask_overwrite;
1188 if (return_status != FILE_CONT)
1189 return return_status;
1191 copy_done = TRUE;
1193 file_progress_show_source (ctx, NULL);
1194 file_progress_show (ctx, 0, 0, "", FALSE);
1196 return_status = check_progress_buttons (ctx);
1197 if (return_status != FILE_CONT)
1198 return return_status;
1200 mc_refresh ();
1202 retry_src_remove:
1203 if (mc_unlink (s))
1205 return_status = file_error (_(" Cannot remove file \"%s\" \n %s "), s);
1206 if (return_status == FILE_RETRY)
1207 goto retry_src_remove;
1208 return return_status;
1211 if (!copy_done)
1212 return_status = progress_update_one (tctx, ctx, src_stats.st_size, TRUE);
1214 return return_status;
1217 FileProgressStatus
1218 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
1220 struct stat sbuf, dbuf, destbuf;
1221 struct link *lp;
1222 char *destdir;
1223 FileProgressStatus return_status;
1224 gboolean move_over = FALSE;
1225 gboolean dstat_ok;
1227 file_progress_show_source (ctx, s);
1228 file_progress_show_target (ctx, d);
1229 if (check_progress_buttons (ctx) == FILE_ABORT)
1230 return FILE_ABORT;
1232 mc_refresh ();
1234 mc_stat (s, &sbuf);
1235 dstat_ok = (mc_stat (d, &dbuf) == 0);
1237 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
1238 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same directory "), s, d);
1240 if (!dstat_ok)
1241 destdir = g_strdup (d); /* destination doesn't exist */
1242 else if (!ctx->dive_into_subdirs)
1244 destdir = g_strdup (d);
1245 move_over = TRUE;
1247 else
1248 destdir = concat_dir_and_file (d, x_basename (s));
1250 /* Check if the user inputted an existing dir */
1251 retry_dst_stat:
1252 if (!mc_stat (destdir, &destbuf))
1254 if (move_over)
1256 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
1258 if (return_status != FILE_CONT)
1259 goto ret;
1260 goto oktoret;
1262 else
1264 if (S_ISDIR (destbuf.st_mode))
1265 return_status = file_error (_(" Cannot overwrite directory \"%s\" %s "), destdir);
1266 else
1267 return_status = file_error (_(" Cannot overwrite file \"%s\" %s "), destdir);
1268 if (return_status == FILE_RETRY)
1269 goto retry_dst_stat;
1271 g_free (destdir);
1272 return return_status;
1275 retry_rename:
1276 if (mc_rename (s, destdir) == 0)
1278 return_status = FILE_CONT;
1279 goto ret;
1282 if (errno != EXDEV)
1284 return_status = files_error (_(" Cannot move directory \"%s\" to \"%s\" \n %s "), s, d);
1285 if (return_status == FILE_RETRY)
1286 goto retry_rename;
1287 goto ret;
1289 /* Failed because of filesystem boundary -> copy dir instead */
1290 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
1292 if (return_status != FILE_CONT)
1293 goto ret;
1294 oktoret:
1295 file_progress_show_source (ctx, NULL);
1296 file_progress_show (ctx, 0, 0, "", FALSE);
1298 return_status = check_progress_buttons (ctx);
1299 if (return_status != FILE_CONT)
1300 goto ret;
1302 mc_refresh ();
1303 if (ctx->erase_at_end)
1305 for (; erase_list && return_status != FILE_ABORT;)
1307 if (S_ISDIR (erase_list->st_mode))
1309 return_status = erase_dir_iff_empty (ctx, erase_list->name);
1311 else
1312 return_status = erase_file (tctx, ctx, erase_list->name, FALSE);
1313 lp = erase_list;
1314 erase_list = erase_list->next;
1315 g_free (lp);
1318 erase_dir_iff_empty (ctx, s);
1320 ret:
1321 g_free (destdir);
1322 while (erase_list)
1324 lp = erase_list;
1325 erase_list = erase_list->next;
1326 g_free (lp);
1328 return return_status;
1331 /* }}} */
1333 /* {{{ Erase routines */
1334 /* Don't update progress status if progress_count==NULL */
1335 static FileProgressStatus
1336 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s,
1337 gboolean is_toplevel_file)
1339 int return_status;
1340 struct stat buf;
1342 file_progress_show_deleting (ctx, s);
1343 if (check_progress_buttons (ctx) == FILE_ABORT)
1344 return FILE_ABORT;
1345 mc_refresh ();
1347 if (tctx->progress_count && mc_lstat (s, &buf))
1349 /* ignore, most likely the mc_unlink fails, too */
1350 buf.st_size = 0;
1353 while (mc_unlink (s))
1355 return_status = file_error (_(" Cannot delete file \"%s\" \n %s "), s);
1356 if (return_status != FILE_RETRY)
1357 return return_status;
1360 if (tctx->progress_count)
1361 return progress_update_one (tctx, ctx, buf.st_size, is_toplevel_file);
1362 else
1363 return FILE_CONT;
1366 static FileProgressStatus
1367 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
1369 struct dirent *next;
1370 struct stat buf;
1371 DIR *reading;
1372 char *path;
1373 FileProgressStatus return_status = FILE_CONT;
1375 if (!strcmp (s, ".."))
1376 return FILE_RETRY;
1378 reading = mc_opendir (s);
1380 if (!reading)
1381 return FILE_RETRY;
1383 while ((next = mc_readdir (reading)) && return_status == FILE_CONT)
1385 if (!strcmp (next->d_name, "."))
1386 continue;
1387 if (!strcmp (next->d_name, ".."))
1388 continue;
1389 path = concat_dir_and_file (s, next->d_name);
1390 if (mc_lstat (path, &buf))
1392 g_free (path);
1393 mc_closedir (reading);
1394 return FILE_RETRY;
1396 if (S_ISDIR (buf.st_mode))
1397 return_status =
1398 (recursive_erase (tctx, ctx, path) != FILE_CONT) ? FILE_RETRY : FILE_CONT;
1399 else
1400 return_status = erase_file (tctx, ctx, path, 0);
1401 g_free (path);
1403 mc_closedir (reading);
1404 if (return_status != FILE_CONT)
1405 return return_status;
1406 file_progress_show_deleting (ctx, s);
1407 if (check_progress_buttons (ctx) == FILE_ABORT)
1408 return FILE_ABORT;
1409 mc_refresh ();
1411 while (my_rmdir (s))
1413 return_status = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1414 if (return_status != FILE_RETRY)
1415 return return_status;
1418 return FILE_CONT;
1421 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1422 in the directory path points to, 0 else. */
1423 static int
1424 check_dir_is_empty (const char *path)
1426 DIR *dir;
1427 struct dirent *d;
1428 int i;
1430 dir = mc_opendir (path);
1431 if (!dir)
1432 return -1;
1434 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1436 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1437 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1438 continue; /* "." or ".." */
1439 i = 0;
1440 break;
1443 mc_closedir (dir);
1444 return i;
1447 FileProgressStatus
1448 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
1450 FileProgressStatus error;
1452 if (strcmp (s, "..") == 0)
1453 return FILE_SKIP;
1455 if (strcmp (s, ".") == 0)
1456 return FILE_SKIP;
1458 file_progress_show_deleting (ctx, s);
1459 if (check_progress_buttons (ctx) == FILE_ABORT)
1460 return FILE_ABORT;
1461 mc_refresh ();
1463 /* The old way to detect a non empty directory was:
1464 error = my_rmdir (s);
1465 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1466 For the linux user space nfs server (nfs-server-2.2beta29-2)
1467 we would have to check also for EIO. I hope the new way is
1468 fool proof. (Norbert)
1470 error = check_dir_is_empty (s);
1471 if (error == 0)
1472 { /* not empty */
1473 error = query_recursive (ctx, s);
1474 if (error == FILE_CONT)
1475 return recursive_erase (tctx, ctx, s);
1476 else
1477 return error;
1480 while (my_rmdir (s) == -1)
1482 error = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1483 if (error != FILE_RETRY)
1484 return error;
1487 return FILE_CONT;
1490 static FileProgressStatus
1491 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1493 FileProgressStatus error;
1495 if (strcmp (s, "..") == 0)
1496 return FILE_SKIP;
1498 if (strcmp (s, ".") == 0)
1499 return FILE_SKIP;
1501 file_progress_show_deleting (ctx, s);
1502 if (check_progress_buttons (ctx) == FILE_ABORT)
1503 return FILE_ABORT;
1504 mc_refresh ();
1506 if (1 != check_dir_is_empty (s)) /* not empty or error */
1507 return FILE_CONT;
1509 while (my_rmdir (s))
1511 error = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1512 if (error != FILE_RETRY)
1513 return error;
1516 return FILE_CONT;
1519 /* }}} */
1521 /* {{{ Panel operate routines */
1524 * Return currently selected entry name or the name of the first marked
1525 * entry if there is one.
1527 static char *
1528 panel_get_file (WPanel * panel, struct stat *stat_buf)
1530 int i;
1532 if (get_current_type () == view_tree)
1534 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
1535 char *tree_name = tree_selected_name (tree);
1537 mc_stat (tree_name, stat_buf);
1538 return tree_name;
1541 if (panel->marked)
1543 for (i = 0; i < panel->count; i++)
1544 if (panel->dir.list[i].f.marked)
1546 *stat_buf = panel->dir.list[i].st;
1547 return panel->dir.list[i].fname;
1550 else
1552 *stat_buf = panel->dir.list[panel->selected].st;
1553 return panel->dir.list[panel->selected].fname;
1555 g_assert_not_reached ();
1556 return NULL;
1560 ComputeDirSizeUI *
1561 compute_dir_size_create_ui (void)
1563 ComputeDirSizeUI *ui;
1565 const char *b_name = N_("&Abort");
1567 #ifdef ENABLE_NLS
1568 b_name = _(b_name);
1569 #endif
1571 ui = g_new (ComputeDirSizeUI, 1);
1573 ui->dlg = create_dlg (0, 0, 8, COLS / 2, dialog_colors, NULL,
1574 NULL, _("Directory scanning"), DLG_CENTER);
1575 ui->dirname = label_new (3, 3, "");
1576 add_widget (ui->dlg, ui->dirname);
1578 add_widget (ui->dlg,
1579 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
1580 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
1582 /* We will manage the dialog without any help,
1583 that's why we have to call init_dlg */
1584 init_dlg (ui->dlg);
1586 return ui;
1589 void
1590 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
1592 if (ui != NULL)
1594 /* schedule to update passive panel */
1595 other_panel->dirty = 1;
1597 /* close and destroy dialog */
1598 dlg_run_done (ui->dlg);
1599 destroy_dlg (ui->dlg);
1600 g_free (ui);
1604 FileProgressStatus
1605 compute_dir_size_update_ui (const void *ui, const char *dirname)
1607 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
1608 int c;
1609 Gpm_Event event;
1611 if (ui == NULL)
1612 return FILE_CONT;
1614 label_set_text (this->dirname, name_trunc (dirname, this->dlg->cols - 6));
1616 event.x = -1; /* Don't show the GPM cursor */
1617 c = tty_get_event (&event, FALSE, FALSE);
1618 if (c == EV_NONE)
1619 return FILE_CONT;
1621 /* Reinitialize to avoid old values after events other than
1622 selecting a button */
1623 this->dlg->ret_value = FILE_CONT;
1625 dlg_process_event (this->dlg, c, &event);
1627 switch (this->dlg->ret_value)
1629 case B_CANCEL:
1630 case FILE_ABORT:
1631 return FILE_ABORT;
1632 default:
1633 return FILE_CONT;
1638 * compute_dir_size:
1640 * Computes the number of bytes used by the files in a directory
1642 FileProgressStatus
1643 compute_dir_size (const char *dirname, const void *ui,
1644 compute_dir_size_callback cback,
1645 off_t * ret_marked, double *ret_total, gboolean compute_symlinks)
1647 int res;
1648 struct stat s;
1649 DIR *dir;
1650 struct dirent *dirent;
1651 FileProgressStatus ret = FILE_CONT;
1653 if (!compute_symlinks)
1655 res = mc_lstat (dirname, &s);
1656 if (res != 0)
1657 return ret;
1659 /* don't scan symlink to directory */
1660 if (S_ISLNK (s.st_mode))
1662 (*ret_marked)++;
1663 *ret_total += s.st_size;
1664 return ret;
1668 dir = mc_opendir (dirname);
1670 if (dir == NULL)
1671 return ret;
1673 while ((dirent = mc_readdir (dir)) != NULL)
1675 char *fullname;
1677 ret = (cback != NULL) ? cback (ui, dirname) : FILE_CONT;
1679 if (ret != FILE_CONT)
1680 break;
1682 if (strcmp (dirent->d_name, ".") == 0)
1683 continue;
1684 if (strcmp (dirent->d_name, "..") == 0)
1685 continue;
1687 fullname = concat_dir_and_file (dirname, dirent->d_name);
1688 res = mc_lstat (fullname, &s);
1690 if (res != 0)
1692 g_free (fullname);
1693 continue;
1696 if (S_ISDIR (s.st_mode))
1698 off_t subdir_count = 0;
1699 double subdir_bytes = 0;
1701 ret =
1702 compute_dir_size (fullname, ui, cback, &subdir_count, &subdir_bytes,
1703 compute_symlinks);
1705 if (ret != FILE_CONT)
1707 g_free (fullname);
1708 break;
1711 *ret_marked += subdir_count;
1712 *ret_total += subdir_bytes;
1714 else
1716 (*ret_marked)++;
1717 *ret_total += s.st_size;
1720 g_free (fullname);
1723 mc_closedir (dir);
1725 return ret;
1729 * panel_compute_totals:
1731 * compute the number of files and the number of bytes
1732 * used up by the whole selection, recursing directories
1733 * as required. In addition, it checks to see if it will
1734 * overwrite any files by doing the copy.
1736 static FileProgressStatus
1737 panel_compute_totals (const WPanel * panel, const void *ui,
1738 compute_dir_size_callback cback,
1739 off_t * ret_marked, double *ret_total, gboolean compute_symlinks)
1741 int i;
1743 *ret_marked = 0;
1744 *ret_total = 0.0;
1746 for (i = 0; i < panel->count; i++)
1748 struct stat *s;
1750 if (!panel->dir.list[i].f.marked)
1751 continue;
1753 s = &panel->dir.list[i].st;
1755 if (S_ISDIR (s->st_mode))
1757 char *dir_name;
1758 off_t subdir_count = 0;
1759 double subdir_bytes = 0;
1760 FileProgressStatus status;
1762 dir_name = concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1764 status = compute_dir_size (dir_name, ui, cback,
1765 &subdir_count, &subdir_bytes, compute_symlinks);
1766 g_free (dir_name);
1768 if (status != FILE_CONT)
1769 return FILE_ABORT;
1771 *ret_marked += subdir_count;
1772 *ret_total += subdir_bytes;
1774 else
1776 (*ret_marked)++;
1777 *ret_total += s->st_size;
1781 return FILE_CONT;
1784 /* Initialize variables for progress bars */
1785 static FileProgressStatus
1786 panel_operate_init_totals (FileOperation operation,
1787 const WPanel * panel, const char *source, FileOpContext * ctx)
1789 FileProgressStatus status;
1791 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1793 ComputeDirSizeUI *ui;
1795 ui = compute_dir_size_create_ui ();
1797 if (source != NULL)
1798 status = compute_dir_size (source, ui, compute_dir_size_update_ui,
1799 &ctx->progress_count, &ctx->progress_bytes,
1800 ctx->follow_links);
1801 else
1802 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1803 &ctx->progress_count, &ctx->progress_bytes,
1804 ctx->follow_links);
1806 compute_dir_size_destroy_ui (ui);
1808 ctx->progress_totals_computed = (status == FILE_CONT);
1810 else
1812 status = FILE_CONT;
1813 ctx->progress_count = panel->marked;
1814 ctx->progress_bytes = panel->total;
1815 ctx->progress_totals_computed = FALSE;
1818 return status;
1822 * This array introduced to avoid translation problems. The former (op_names)
1823 * is assumed to be nouns, suitable in dialog box titles; this one should
1824 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1825 * (I don't use spaces around the words, because someday they could be
1826 * dropped, when widgets get smarter)
1829 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
1830 static const char *op_names1[] = {
1831 N_("FileOperation|Copy"),
1832 N_("FileOperation|Move"),
1833 N_("FileOperation|Delete")
1837 * These are formats for building a prompt. Parts encoded as follows:
1838 * %o - operation from op_names1
1839 * %f - file/files or files/directories, as appropriate
1840 * %m - "with source mask" or question mark for delete
1841 * %s - source name (truncated)
1842 * %d - number of marked files
1843 * %e - "to:" or question mark for delete
1845 * xgettext:no-c-format */
1846 static const char *one_format = N_("%o %f \"%s\"%m");
1847 /* xgettext:no-c-format */
1848 static const char *many_format = N_("%o %d %f%m");
1850 static const char *prompt_parts[] = {
1851 N_("file"),
1852 N_("files"),
1853 N_("directory"),
1854 N_("directories"),
1855 N_("files/directories"),
1856 N_(" with source mask:"),
1857 N_(" to:")
1860 static const char *question_format = N_("%s?");
1863 * Generate user prompt for panel operation.
1864 * single_source is the name if the source entry or NULL for multiple
1865 * entries.
1866 * src_stat is only used when single_source is not NULL.
1868 static char *
1869 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1870 gboolean single_source, const struct stat *src_stat)
1872 const char *sp, *cp;
1873 char format_string[BUF_MEDIUM];
1874 char *dp = format_string;
1875 gboolean build_question = FALSE;
1877 #ifdef ENABLE_NLS
1878 static gboolean i18n_flag = FALSE;
1879 if (!i18n_flag)
1881 size_t i;
1883 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1884 op_names1[i] = Q_ (op_names1[i]);
1886 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1887 prompt_parts[i] = _(prompt_parts[i]);
1889 one_format = _(one_format);
1890 many_format = _(many_format);
1891 question_format = _(question_format);
1892 i18n_flag = TRUE;
1894 #endif /* ENABLE_NLS */
1896 sp = single_source ? one_format : many_format;
1898 while (*sp != '\0')
1900 switch (*sp)
1902 case '%':
1903 cp = NULL;
1904 switch (sp[1])
1906 case 'o':
1907 cp = op_names1[operation];
1908 break;
1909 case 'm':
1910 if (operation == OP_DELETE)
1912 cp = "";
1913 build_question = TRUE;
1915 else
1916 cp = prompt_parts[5];
1917 break;
1918 case 'e':
1919 if (operation == OP_DELETE)
1921 cp = "";
1922 build_question = TRUE;
1924 else
1925 cp = prompt_parts[6];
1926 break;
1927 case 'f':
1928 if (single_source)
1929 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1930 else
1931 cp = (panel->marked == panel->dirs_marked)
1932 ? prompt_parts[3]
1933 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1934 break;
1935 default:
1936 *dp++ = *sp++;
1939 if (cp != NULL)
1941 sp += 2;
1942 while (*cp != '\0')
1943 *dp++ = *cp++;
1945 break;
1946 default:
1947 *dp++ = *sp++;
1950 *dp = '\0';
1952 if (build_question)
1954 char tmp[BUF_MEDIUM];
1956 memmove (tmp, format_string, sizeof (tmp));
1957 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1960 return g_strdup (format_string);
1963 #ifdef WITH_BACKGROUND
1964 static int
1965 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1967 int pid = ctx->pid;
1969 (void) mode;
1970 ctx->pid = 0;
1972 unregister_task_with_pid (pid);
1973 /* file_op_context_destroy(ctx); */
1974 return 1;
1976 #endif
1979 * panel_operate:
1981 * Performs one of the operations on the selection on the source_panel
1982 * (copy, delete, move).
1984 * Returns TRUE if did change the directory
1985 * structure, Returns FALSE if user aborted
1987 * force_single forces operation on the current entry and affects
1988 * default destination. Current filename is used as default.
1990 gboolean
1991 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
1993 WPanel *panel = (WPanel *) source_panel;
1994 const gboolean single_entry = force_single || (panel->marked <= 1)
1995 || (get_current_type () == view_tree);
1997 char *source = NULL;
1998 #ifdef WITH_FULL_PATHS
1999 char *source_with_path = NULL;
2000 #else
2001 # define source_with_path source
2002 #endif /* !WITH_FULL_PATHS */
2003 char *dest = NULL;
2004 char *temp = NULL;
2005 char *save_cwd = NULL, *save_dest = NULL;
2006 struct stat src_stat;
2007 gboolean ret_val = TRUE;
2008 int i;
2009 FileProgressStatus value;
2010 FileOpContext *ctx;
2011 FileOpTotalContext *tctx;
2013 gboolean do_bg = FALSE; /* do background operation? */
2015 #ifdef ENABLE_NLS
2016 static gboolean i18n_flag = FALSE;
2017 if (!i18n_flag)
2019 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2020 op_names[i] = Q_ (op_names[i]);
2021 i18n_flag = TRUE;
2023 #endif /* ENABLE_NLS */
2025 free_linklist (&linklist);
2026 free_linklist (&dest_dirs);
2028 /* Update panel contents to avoid actions on deleted files */
2029 if (!panel->is_panelized)
2031 update_panels (UP_RELOAD, UP_KEEPSEL);
2032 repaint_screen ();
2035 if (single_entry)
2037 if (force_single)
2039 source = selection (panel)->fname;
2040 src_stat = selection (panel)->st;
2042 else
2043 source = panel_get_file (panel, &src_stat);
2045 if (!strcmp (source, ".."))
2047 message (D_ERROR, MSG_ERROR, _(" Cannot operate on \"..\"! "));
2048 return FALSE;
2052 ctx = file_op_context_new (operation);
2053 tctx = file_op_total_context_new ();
2054 gettimeofday (&(tctx->transfer_start), (struct timezone *) NULL);
2056 /* Show confirmation dialog */
2057 if (operation != OP_DELETE)
2059 char *dest_dir;
2060 char *dest_dir_;
2061 char *format;
2063 /* Forced single operations default to the original name */
2064 if (force_single)
2065 dest_dir = source;
2066 else if (get_other_type () == view_listing)
2067 dest_dir = other_panel->cwd;
2068 else
2069 dest_dir = panel->cwd;
2071 * Add trailing backslash only when do non-local ops.
2072 * It saves user from occasional file renames (when destination
2073 * dir is deleted)
2075 if (!force_single && dest_dir[0] != '\0' && dest_dir[strlen (dest_dir) - 1] != PATH_SEP)
2077 /* add trailing separator */
2078 dest_dir_ = g_strconcat (dest_dir, PATH_SEP_STR, (char *) NULL);
2080 else
2082 /* just copy */
2083 dest_dir_ = g_strdup (dest_dir);
2085 if (dest_dir_ == NULL)
2087 file_op_total_context_destroy (tctx);
2088 file_op_context_destroy (ctx);
2089 return FALSE;
2092 /* Generate confirmation prompt */
2093 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2095 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2096 source != NULL ? (void *) source
2097 : (void *) &panel->marked, dest_dir_, &do_bg);
2099 g_free (format);
2100 g_free (dest_dir_);
2102 if (dest == NULL || dest[0] == '\0')
2104 file_op_total_context_destroy (tctx);
2105 file_op_context_destroy (ctx);
2106 g_free (dest);
2107 return FALSE;
2110 else if (confirm_delete)
2112 char *format;
2113 char fmd_buf[BUF_MEDIUM];
2115 /* Generate confirmation prompt */
2116 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2118 if (source == NULL)
2119 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2120 else
2122 const int fmd_xlen = 64;
2123 i = fmd_xlen - str_term_width1 (format) - 4;
2124 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2127 g_free (format);
2129 if (safe_delete)
2130 query_set_sel (1);
2132 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2134 if (i != 0)
2136 file_op_total_context_destroy (tctx);
2137 file_op_context_destroy (ctx);
2138 return FALSE;
2143 filegui_dialog_type_t dialog_type;
2145 if (operation == OP_DELETE)
2146 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2147 else
2149 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2150 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2152 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2153 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2156 /* Background also need ctx->ui, but not full */
2157 if (do_bg)
2158 file_op_context_create_ui_without_init (ctx, 1, dialog_type);
2159 else
2160 file_op_context_create_ui (ctx, 1, dialog_type);
2163 #ifdef WITH_BACKGROUND
2164 /* Did the user select to do a background operation? */
2165 if (do_bg)
2167 int v;
2169 v = do_background (ctx, g_strconcat (op_names[operation], ": ", panel->cwd, (char *) NULL));
2170 if (v == -1)
2171 message (D_ERROR, MSG_ERROR, _(" Sorry, I could not put the job in background "));
2173 /* If we are the parent */
2174 if (v == 1)
2176 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
2177 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
2178 /* file_op_context_destroy (ctx); */
2179 return FALSE;
2182 #endif /* WITH_BACKGROUND */
2184 /* Initialize things */
2185 /* We do not want to trash cache every time file is
2186 created/touched. However, this will make our cache contain
2187 invalid data. */
2188 if ((dest != NULL) && (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1)))
2189 save_dest = g_strdup (dest);
2191 if ((panel->cwd[0] != '\0') && (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1)))
2192 save_cwd = g_strdup (panel->cwd);
2194 /* Now, let's do the job */
2196 /* This code is only called by the tree and panel code */
2197 if (single_entry)
2199 /* We now have ETA in all cases */
2201 /* One file: FIXME mc_chdir will take user out of any vfs */
2202 if ((operation != OP_COPY) && (get_current_type () == view_tree) &&
2203 (mc_chdir (PATH_SEP_STR) < 0))
2205 ret_val = FALSE;
2206 goto clean_up;
2209 /* The source and src_stat variables have been initialized before */
2210 #ifdef WITH_FULL_PATHS
2211 source_with_path = concat_dir_and_file (panel->cwd, source);
2212 #endif /* WITH_FULL_PATHS */
2214 if (panel_operate_init_totals (operation, panel, source_with_path, ctx) == FILE_CONT)
2216 if (operation == OP_DELETE)
2218 if (S_ISDIR (src_stat.st_mode))
2219 value = erase_dir (tctx, ctx, source_with_path);
2220 else
2221 value = erase_file (tctx, ctx, source_with_path, 1);
2223 else
2225 temp = transform_source (ctx, source_with_path);
2226 if (temp == NULL)
2227 value = transform_error;
2228 else
2230 char *repl_dest, *temp2;
2232 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2233 temp2 = concat_dir_and_file (repl_dest, temp);
2234 g_free (temp);
2235 g_free (repl_dest);
2236 g_free (dest);
2237 dest = temp2;
2239 switch (operation)
2241 case OP_COPY:
2242 /* we use file_mask_op_follow_links only with OP_COPY */
2243 ctx->stat_func (source_with_path, &src_stat);
2245 if (S_ISDIR (src_stat.st_mode))
2246 value = copy_dir_dir (tctx, ctx, source_with_path, dest,
2247 TRUE, FALSE, FALSE, NULL);
2248 else
2249 value = copy_file_file (tctx, ctx, source_with_path, dest);
2250 break;
2252 case OP_MOVE:
2253 if (S_ISDIR (src_stat.st_mode))
2254 value = move_dir_dir (tctx, ctx, source_with_path, dest);
2255 else
2256 value = move_file_file (tctx, ctx, source_with_path, dest);
2257 break;
2259 default:
2260 /* Unknown file operation */
2261 abort ();
2264 } /* Copy or move operation */
2266 if ((value == FILE_CONT) && !force_single)
2267 unmark_files (panel);
2270 else
2272 /* Many files */
2274 /* Check destination for copy or move operation */
2275 while (operation != OP_DELETE)
2277 int dst_result;
2278 struct stat dst_stat;
2280 dst_result = mc_stat (dest, &dst_stat);
2282 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2283 break;
2285 if (file_error (_(" Destination \"%s\" must be a directory \n %s "),
2286 dest) != FILE_RETRY)
2287 goto clean_up;
2290 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2292 /* Loop for every file, perform the actual copy operation */
2293 for (i = 0; i < panel->count; i++)
2295 if (!panel->dir.list[i].f.marked)
2296 continue; /* Skip the unmarked ones */
2298 source = panel->dir.list[i].fname;
2299 src_stat = panel->dir.list[i].st;
2301 #ifdef WITH_FULL_PATHS
2302 g_free (source_with_path);
2303 source_with_path = concat_dir_and_file (panel->cwd, source);
2304 #endif /* WITH_FULL_PATHS */
2306 if (operation == OP_DELETE)
2308 if (S_ISDIR (src_stat.st_mode))
2309 value = erase_dir (tctx, ctx, source_with_path);
2310 else
2311 value = erase_file (tctx, ctx, source_with_path, 1);
2313 else
2315 temp = transform_source (ctx, source_with_path);
2317 if (temp == NULL)
2318 value = transform_error;
2319 else
2321 char *temp2, *temp3, *repl_dest;
2323 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2324 temp2 = concat_dir_and_file (repl_dest, temp);
2325 g_free (temp);
2326 g_free (repl_dest);
2327 temp3 = source_with_path;
2328 source_with_path = strutils_shell_unescape (source_with_path);
2329 g_free (temp3);
2330 temp3 = temp2;
2331 temp2 = strutils_shell_unescape (temp2);
2332 g_free (temp3);
2334 switch (operation)
2336 case OP_COPY:
2337 /* we use file_mask_op_follow_links only with OP_COPY */
2338 ctx->stat_func (source_with_path, &src_stat);
2339 if (S_ISDIR (src_stat.st_mode))
2340 value = copy_dir_dir (tctx, ctx, source_with_path, temp2,
2341 TRUE, FALSE, FALSE, NULL);
2342 else
2343 value = copy_file_file (tctx, ctx, source_with_path, temp2);
2344 free_linklist (&dest_dirs);
2345 break;
2347 case OP_MOVE:
2348 if (S_ISDIR (src_stat.st_mode))
2349 value = move_dir_dir (tctx, ctx, source_with_path, temp2);
2350 else
2351 value = move_file_file (tctx, ctx, source_with_path, temp2);
2352 break;
2354 default:
2355 /* Unknown file operation */
2356 abort ();
2359 g_free (temp2);
2361 } /* Copy or move operation */
2363 if (value == FILE_ABORT)
2364 break;
2366 if (value == FILE_CONT)
2367 do_file_mark (panel, i, 0);
2369 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2371 if (verbose)
2373 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
2375 if (operation != OP_DELETE)
2376 file_progress_show (ctx, 0, 0, "", FALSE);
2379 if (check_progress_buttons (ctx) == FILE_ABORT)
2380 break;
2382 mc_refresh ();
2383 } /* Loop for every file */
2385 } /* Many entries */
2387 clean_up:
2388 /* Clean up */
2389 if (save_cwd != NULL)
2391 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2392 g_free (save_cwd);
2395 if (save_dest != NULL)
2397 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2398 g_free (save_dest);
2401 free_linklist (&linklist);
2402 free_linklist (&dest_dirs);
2403 #ifdef WITH_FULL_PATHS
2404 g_free (source_with_path);
2405 #endif /* WITH_FULL_PATHS */
2406 g_free (dest);
2407 g_free (ctx->dest_mask);
2408 ctx->dest_mask = NULL;
2410 #ifdef WITH_BACKGROUND
2411 /* Let our parent know we are saying bye bye */
2412 if (we_are_background)
2414 int cur_pid = getpid ();
2415 /* Send pid to parent with child context, it is fork and
2416 don't modify real parent ctx */
2417 ctx->pid = cur_pid;
2418 parent_call ((void *) end_bg_process, ctx, 0);
2420 vfs_shut ();
2421 _exit (0);
2423 #endif /* WITH_BACKGROUND */
2425 file_op_context_destroy (ctx);
2426 file_op_total_context_destroy (tctx);
2428 return ret_val;
2431 /* }}} */
2433 /* {{{ Query/status report routines */
2435 static FileProgressStatus
2436 real_do_file_error (enum OperationMode mode, const char *error)
2438 int result;
2439 const char *msg;
2441 msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
2442 result = query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"), _("&Abort"));
2444 switch (result)
2446 case 0:
2447 do_refresh ();
2448 return FILE_SKIP;
2450 case 1:
2451 do_refresh ();
2452 return FILE_RETRY;
2454 case 2:
2455 default:
2456 return FILE_ABORT;
2460 /* Report error with one file */
2461 FileProgressStatus
2462 file_error (const char *format, const char *file)
2464 char buf[BUF_MEDIUM];
2466 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
2468 return do_file_error (buf);
2471 /* Report error with two files */
2472 static FileProgressStatus
2473 files_error (const char *format, const char *file1, const char *file2)
2475 char buf[BUF_MEDIUM];
2476 char *nfile1 = g_strdup (path_trunc (file1, 15));
2477 char *nfile2 = g_strdup (path_trunc (file2, 15));
2479 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
2481 g_free (nfile1);
2482 g_free (nfile2);
2484 return do_file_error (buf);
2487 static FileProgressStatus
2488 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
2490 gchar *text;
2492 if (ctx->recursive_result < RECURSIVE_ALWAYS)
2494 const char *msg = mode == Foreground
2495 ? _("\n Directory not empty. \n"
2496 " Delete it recursively? ")
2497 : _("\n Background process: Directory not empty \n" " Delete it recursively? ");
2498 text = g_strconcat (_(" Delete: "), path_trunc (s, 30), " ", (char *) NULL);
2500 if (safe_delete)
2501 query_set_sel (1);
2503 ctx->recursive_result =
2504 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
2505 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
2507 if (ctx->recursive_result != RECURSIVE_ABORT)
2508 do_refresh ();
2509 g_free (text);
2512 switch (ctx->recursive_result)
2514 case RECURSIVE_YES:
2515 case RECURSIVE_ALWAYS:
2516 return FILE_CONT;
2518 case RECURSIVE_NO:
2519 case RECURSIVE_NEVER:
2520 return FILE_SKIP;
2522 case RECURSIVE_ABORT:
2523 default:
2524 return FILE_ABORT;
2528 #ifdef WITH_BACKGROUND
2529 static FileProgressStatus
2530 do_file_error (const char *str)
2532 union
2534 void *p;
2535 FileProgressStatus (*f) (enum OperationMode, const char *);
2536 } pntr;
2537 pntr.f = real_do_file_error;
2539 if (we_are_background)
2540 return parent_call (pntr.p, NULL, 1, strlen (str), str);
2541 else
2542 return real_do_file_error (Foreground, str);
2545 static FileProgressStatus
2546 query_recursive (FileOpContext * ctx, const char *s)
2548 union
2550 void *p;
2551 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
2552 } pntr;
2553 pntr.f = real_query_recursive;
2555 if (we_are_background)
2556 return parent_call (pntr.p, ctx, 1, strlen (s), s);
2557 else
2558 return real_query_recursive (ctx, Foreground, s);
2561 static FileProgressStatus
2562 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
2563 struct stat *_d_stat)
2565 union
2567 void *p;
2568 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
2569 struct stat *, struct stat *);
2570 } pntr;
2571 pntr.f = file_progress_real_query_replace;
2573 if (we_are_background)
2574 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
2575 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
2576 else
2577 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
2580 #else
2581 static FileProgressStatus
2582 do_file_error (const char *str)
2584 return real_do_file_error (Foreground, str);
2587 static FileProgressStatus
2588 query_recursive (FileOpContext * ctx, const char *s)
2590 return real_query_recursive (ctx, Foreground, s);
2593 static FileProgressStatus
2594 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
2595 struct stat *_d_stat)
2597 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
2600 #endif /* !WITH_BACKGROUND */
2603 Cause emacs to enter folding mode for this file:
2604 Local variables:
2605 end: