Some optimization of loops in translation functions.
[midnight-commander.git] / src / file.c
blob5fd182cda244b4ab3b6ef3787e8755041f2e7e56
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/vfs/mc-vfs/vfs.h"
66 #include "setup.h"
67 #include "dialog.h"
68 #include "widget.h"
69 #include "main.h"
70 #include "layout.h" /* rotate_dash() */
71 #include "widget.h"
72 #include "wtools.h"
73 #include "background.h" /* we_are_background */
75 /* Needed for current_panel, other_panel and WTree */
76 #include "dir.h"
77 #include "panel.h"
78 #include "file.h"
79 #include "filegui.h"
80 #include "tree.h"
82 /* }}} */
84 /* Hack: the vfs code should not rely on this */
85 #define WITH_FULL_PATHS 1
87 #define FILEOP_UPDATE_INTERVAL 2
88 #define FILEOP_STALLING_INTERVAL 4
90 int verbose = 1;
93 * Whether the Midnight Commander tries to provide more
94 * information about copy/move sizes and bytes transfered
95 * at the expense of some speed
97 int file_op_compute_totals = 1;
99 /* This is a hard link cache */
100 struct link
102 struct link *next;
103 struct vfs_class *vfs;
104 dev_t dev;
105 ino_t ino;
106 short linkcount;
107 mode_t st_mode;
108 char name[1];
111 /* the hard link cache */
112 static struct link *linklist = NULL;
114 /* the files-to-be-erased list */
115 static struct link *erase_list;
118 * In copy_dir_dir we use two additional single linked lists: The first -
119 * variable name `parent_dirs' - holds information about already copied
120 * directories and is used to detect cyclic symbolic links.
121 * The second (`dest_dirs' below) holds information about just created
122 * target directories and is used to detect when an directory is copied
123 * into itself (we don't want to copy infinitly).
124 * Both lists don't use the linkcount and name structure members of struct
125 * link.
127 static struct link *dest_dirs = NULL;
129 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
130 const char *op_names[3] = {
131 N_("DialogTitle|Copy"),
132 N_("DialogTitle|Move"),
133 N_("DialogTitle|Delete")
136 /* }}} */
138 static FileProgressStatus query_replace (FileOpContext * ctx, const char *destname,
139 struct stat *_s_stat, struct stat *_d_stat);
140 static FileProgressStatus query_recursive (FileOpContext * ctx, const char *s);
141 static FileProgressStatus do_file_error (const char *str);
142 static FileProgressStatus erase_dir_iff_empty (FileOpContext * ctx, const char *s);
143 static FileProgressStatus erase_file (FileOpTotalContext * tctx, FileOpContext * ctx,
144 const char *s, gboolean is_toplevel_file);
145 static FileProgressStatus files_error (const char *format, const char *file1, const char *file2);
147 static FileProgressStatus transform_error = FILE_CONT;
149 static char *
150 transform_source (FileOpContext * ctx, const char *source)
152 char *s, *q;
153 char *fnsource;
155 s = g_strdup (source);
157 /* We remove \n from the filename since regex routines would use \n as an anchor */
158 /* this is just to be allowed to maniupulate file names with \n on it */
159 for (q = s; *q != '\0'; q++)
160 if (*q == '\n')
161 *q = ' ';
163 fnsource = (char *) x_basename (s);
165 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
166 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
167 else
169 q = NULL;
170 transform_error = FILE_SKIP;
173 g_free (s);
174 return q;
177 static void
178 free_linklist (struct link **lc_linklist)
180 struct link *lp, *lp2;
182 for (lp = *lc_linklist; lp != NULL; lp = lp2)
184 lp2 = lp->next;
185 g_free (lp);
187 *lc_linklist = NULL;
190 static int
191 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
193 ino_t ino = sb->st_ino;
194 dev_t dev = sb->st_dev;
195 struct vfs_class *vfs = vfs_get_class (path);
197 while (lp != NULL)
199 if (lp->vfs == vfs)
200 if (lp->ino == ino && lp->dev == dev)
201 return 1;
202 lp = lp->next;
204 return 0;
208 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
209 * and a hardlink was succesfully made
211 static int
212 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
214 struct link *lp;
215 struct vfs_class *my_vfs = vfs_get_class (src_name);
216 ino_t ino = pstat->st_ino;
217 dev_t dev = pstat->st_dev;
218 struct stat link_stat;
219 const char *p;
221 if ((vfs_file_class_flags (src_name) & VFSF_NOLINKS) != 0)
222 return 0;
224 for (lp = linklist; lp != NULL; lp = lp->next)
225 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev)
227 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
228 && link_stat.st_dev == dev && vfs_get_class (lp->name) == my_vfs)
230 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
231 was copied to */
232 if (vfs_get_class (dst_name) == vfs_get_class (p))
234 if (!mc_stat (p, &link_stat))
236 if (!mc_link (p, dst_name))
237 return 1;
241 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
242 return 0;
244 lp = (struct link *) g_try_malloc (sizeof (struct link) + strlen (src_name)
245 + strlen (dst_name) + 1);
246 if (lp)
248 char *lpdstname;
249 lp->vfs = my_vfs;
250 lp->ino = ino;
251 lp->dev = dev;
252 strcpy (lp->name, src_name);
253 lpdstname = lp->name + strlen (lp->name) + 1;
254 strcpy (lpdstname, dst_name);
255 lp->next = linklist;
256 linklist = lp;
258 return 0;
262 * Duplicate the contents of the symbolic link src_path in dst_path.
263 * Try to make a stable symlink if the option "stable symlink" was
264 * set in the file mask dialog.
265 * If dst_path is an existing symlink it will be deleted silently
266 * (upper levels take already care of existing files at dst_path).
268 static FileProgressStatus
269 make_symlink (FileOpContext * ctx, const char *src_path, const char *dst_path)
271 char link_target[MC_MAXPATHLEN];
272 int len;
273 FileProgressStatus return_status;
274 struct stat sb;
275 gboolean dst_is_symlink;
277 dst_is_symlink = (mc_lstat (dst_path, &sb) == 0) && S_ISLNK (sb.st_mode);
279 retry_src_readlink:
280 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
281 if (len < 0)
283 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
284 if (return_status == FILE_RETRY)
285 goto retry_src_readlink;
286 return return_status;
288 link_target[len] = 0;
290 if (ctx->stable_symlinks)
291 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path))
293 message (D_ERROR, MSG_ERROR,
294 _("Cannot make stable symlinks across"
295 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
296 ctx->stable_symlinks = FALSE;
299 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
301 char *p, *q, *s;
303 const char *r = strrchr (src_path, PATH_SEP);
305 if (r)
307 p = g_strndup (src_path, r - src_path + 1);
308 if (g_path_is_absolute (dst_path))
309 q = g_strdup (dst_path);
310 else
311 q = g_strconcat (p, dst_path, (char *) NULL);
312 s = strrchr (q, PATH_SEP);
313 if (s)
315 s[1] = 0;
316 s = g_strconcat (p, link_target, (char *) NULL);
317 g_free (p);
318 g_strlcpy (link_target, s, sizeof (link_target));
319 g_free (s);
320 s = diff_two_paths (q, link_target);
321 if (s)
323 g_strlcpy (link_target, s, sizeof (link_target));
324 g_free (s);
327 else
328 g_free (p);
329 g_free (q);
332 retry_dst_symlink:
333 if (mc_symlink (link_target, dst_path) == 0)
334 /* Success */
335 return FILE_CONT;
337 * if dst_exists, it is obvious that this had failed.
338 * We can delete the old symlink and try again...
340 if (dst_is_symlink)
342 if (!mc_unlink (dst_path))
343 if (mc_symlink (link_target, dst_path) == 0)
344 /* Success */
345 return FILE_CONT;
347 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
348 if (return_status == FILE_RETRY)
349 goto retry_dst_symlink;
350 return return_status;
353 static FileProgressStatus
354 progress_update_one (FileOpTotalContext * tctx, FileOpContext * ctx, off_t add,
355 gboolean is_toplevel_file)
357 struct timeval tv_current;
358 static struct timeval tv_start = { };
360 if (is_toplevel_file || ctx->progress_totals_computed)
362 tctx->progress_count++;
363 tctx->progress_bytes += add;
365 if (tv_start.tv_sec == 0)
367 gettimeofday (&tv_start, (struct timezone *) NULL);
369 gettimeofday (&tv_current, (struct timezone *) NULL);
370 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
372 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
373 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
374 tv_start.tv_sec = tv_current.tv_sec;
377 return check_progress_buttons (ctx);
380 /* Status of the destination file */
381 typedef enum
383 DEST_NONE = 0, /* Not created */
384 DEST_SHORT = 1, /* Created, not fully copied */
385 DEST_FULL = 2 /* Created, fully copied */
386 } dest_status_t;
388 static FileProgressStatus
389 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
391 char *msg;
392 int result = 0;
393 const char *head_msg;
395 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
397 msg = g_strdup_printf (fmt, a, b);
398 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
399 g_free (msg);
400 do_refresh ();
402 return (result == 1) ? FILE_ABORT : FILE_SKIP;
405 #ifdef WITH_BACKGROUND
406 static FileProgressStatus
407 warn_same_file (const char *fmt, const char *a, const char *b)
409 union
411 void *p;
412 FileProgressStatus (*f) (enum OperationMode, const char *fmt,
413 const char *a, const char *b);
414 } pntr;
415 pntr.f = real_warn_same_file;
417 if (we_are_background)
418 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
419 else
420 return real_warn_same_file (Foreground, fmt, a, b);
422 #else
423 static FileProgressStatus
424 warn_same_file (const char *fmt, const char *a, const char *b)
426 return real_warn_same_file (Foreground, fmt, a, b);
428 #endif
430 static void
431 copy_file_file_display_progress (FileOpTotalContext * tctx, FileOpContext * ctx,
432 struct timeval tv_current, struct timeval tv_transfer_start,
433 off_t file_size, off_t n_read_total)
435 long dt;
437 /* 1. Update rotating dash after some time */
438 rotate_dash ();
440 /* 3. Compute ETA */
441 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
443 if (n_read_total)
445 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
446 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
448 else
449 ctx->eta_secs = 0.0;
451 /* 4. Compute BPS rate */
452 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
453 if (ctx->bps_time < 1)
454 ctx->bps_time = 1;
455 ctx->bps = n_read_total / ctx->bps_time;
457 /* 5. Compute total ETA and BPS */
458 if (ctx->progress_bytes != 0)
460 double remain_bytes;
461 tctx->copyed_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
462 remain_bytes = ctx->progress_bytes - tctx->copyed_bytes;
463 #if 1
465 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
467 if (total_secs < 1)
468 total_secs = 1;
469 tctx->bps = tctx->copyed_bytes / total_secs;
470 tctx->eta_secs = remain_bytes / tctx->bps;
472 #else
473 /* broken on lot of little files */
474 tctx->bps_count++;
475 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
476 tctx->eta_secs = remain_bytes / tctx->bps;
477 #endif
481 FileProgressStatus
482 copy_file_file (FileOpTotalContext * tctx, FileOpContext * ctx,
483 const char *src_path, const char *dst_path)
485 uid_t src_uid = (uid_t) - 1;
486 gid_t src_gid = (gid_t) - 1;
488 int src_desc, dest_desc = -1;
489 int n_read, n_written;
490 mode_t src_mode = 0; /* The mode of the source file */
491 struct stat sb, sb2;
492 struct utimbuf utb;
493 gboolean dst_exists = FALSE, appending = FALSE;
494 off_t n_read_total = 0, file_size = -1;
495 FileProgressStatus return_status, temp_status;
496 struct timeval tv_transfer_start;
497 dest_status_t dst_status = DEST_NONE;
498 int open_flags;
499 gboolean is_first_time = TRUE;
501 /* FIXME: We should not be using global variables! */
502 ctx->do_reget = 0;
503 return_status = FILE_RETRY;
505 file_progress_show_source (ctx, src_path);
506 file_progress_show_target (ctx, dst_path);
507 if (check_progress_buttons (ctx) == FILE_ABORT)
508 return FILE_ABORT;
510 mc_refresh ();
512 while (mc_stat (dst_path, &sb2) == 0)
514 if (S_ISDIR (sb2.st_mode))
516 return_status = file_error (_("Cannot overwrite directory\"%s\"\n%s"), dst_path);
517 if (return_status == FILE_RETRY)
518 continue;
519 return return_status;
521 dst_exists = TRUE;
522 break;
525 while ((*ctx->stat_func) (src_path, &sb))
527 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
528 if (return_status != FILE_RETRY)
529 return return_status;
532 if (dst_exists)
534 /* Destination already exists */
535 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino)
536 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
537 src_path, dst_path);
538 /* Should we replace destination? */
539 if (tctx->ask_overwrite)
541 ctx->do_reget = 0;
542 return_status = query_replace (ctx, dst_path, &sb, &sb2);
543 if (return_status != FILE_CONT)
544 return return_status;
548 if (!ctx->do_append)
550 /* Check the hardlinks */
551 if (!ctx->follow_links && sb.st_nlink > 1 && check_hardlinks (src_path, dst_path, &sb) == 1)
553 /* We have made a hardlink - no more processing is necessary */
554 return FILE_CONT;
557 if (S_ISLNK (sb.st_mode))
558 return make_symlink (ctx, src_path, dst_path);
560 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
561 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) || S_ISSOCK (sb.st_mode))
563 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0)
565 return_status =
566 file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
567 if (return_status == FILE_RETRY)
568 continue;
569 return return_status;
571 /* Success */
573 while (ctx->preserve_uidgid && mc_chown (dst_path, sb.st_uid, sb.st_gid))
575 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
576 if (temp_status == FILE_RETRY)
577 continue;
578 return temp_status;
580 while (ctx->preserve && mc_chmod (dst_path, sb.st_mode & ctx->umask_kill))
582 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
583 if (temp_status == FILE_RETRY)
584 continue;
585 return temp_status;
587 return FILE_CONT;
591 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
593 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0)
595 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
596 if (return_status == FILE_RETRY)
597 continue;
598 ctx->do_append = 0;
599 return return_status;
602 if (ctx->do_reget != 0)
604 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
606 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
607 ctx->do_reget = 0;
608 ctx->do_append = FALSE;
612 while (mc_fstat (src_desc, &sb))
614 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
615 if (return_status == FILE_RETRY)
616 continue;
617 ctx->do_append = FALSE;
618 goto ret;
620 src_mode = sb.st_mode;
621 src_uid = sb.st_uid;
622 src_gid = sb.st_gid;
623 utb.actime = sb.st_atime;
624 utb.modtime = sb.st_mtime;
625 file_size = sb.st_size;
627 open_flags = O_WRONLY;
628 if (dst_exists)
630 if (ctx->do_append != 0)
631 open_flags |= O_APPEND;
632 else
633 open_flags |= O_CREAT | O_TRUNC;
635 else
637 open_flags |= O_CREAT | O_EXCL;
640 while ((dest_desc = mc_open (dst_path, open_flags, src_mode)) < 0)
642 if (errno == EEXIST)
643 goto ret;
645 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
646 if (return_status == FILE_RETRY)
647 continue;
648 ctx->do_append = FALSE;
649 goto ret;
651 dst_status = DEST_SHORT; /* file opened, but not fully copied */
653 appending = ctx->do_append;
654 ctx->do_append = FALSE;
656 /* Find out the optimal buffer size. */
657 while (mc_fstat (dest_desc, &sb))
659 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
660 if (return_status == FILE_RETRY)
661 continue;
662 goto ret;
665 ctx->eta_secs = 0.0;
666 ctx->bps = 0;
668 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
669 file_progress_show (ctx, 0, file_size, "", TRUE);
670 else
671 file_progress_show (ctx, 1, 1, "", TRUE);
672 return_status = check_progress_buttons (ctx);
673 mc_refresh ();
675 if (return_status != FILE_CONT)
676 goto ret;
679 struct timeval tv_current, tv_last_update, tv_last_input;
680 int secs, update_secs;
681 const char *stalled_msg = "";
683 tv_last_update = tv_transfer_start;
685 for (;;)
687 char buf[BUF_8K];
689 /* src_read */
690 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
691 n_read = -1;
692 else
693 while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0)
695 return_status =
696 file_error (_("Cannot read source file\"%s\"\n%s"), src_path);
697 if (return_status == FILE_RETRY)
698 continue;
699 goto ret;
701 if (n_read == 0)
702 break;
704 gettimeofday (&tv_current, NULL);
706 if (n_read > 0)
708 char *t = buf;
709 n_read_total += n_read;
711 /* Windows NT ftp servers report that files have no
712 * permissions: -------, so if we happen to have actually
713 * read something, we should fix the permissions.
715 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
716 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
717 gettimeofday (&tv_last_input, NULL);
719 /* dst_write */
720 while ((n_written = mc_write (dest_desc, t, n_read)) < n_read)
722 if (n_written > 0)
724 n_read -= n_written;
725 t += n_written;
726 continue;
728 return_status =
729 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
730 if (return_status != FILE_RETRY)
731 goto ret;
734 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
735 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
737 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
739 copy_file_file_display_progress (tctx, ctx,
740 tv_current,
741 tv_transfer_start, file_size, n_read_total);
742 tv_last_update = tv_current;
744 is_first_time = FALSE;
746 if (update_secs > FILEOP_STALLING_INTERVAL)
748 stalled_msg = _("(stalled)");
752 gboolean force_update =
753 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
754 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
755 file_progress_show_total (tctx, ctx,
756 tctx->progress_bytes + n_read_total + ctx->do_reget,
757 force_update);
760 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
761 force_update);
763 mc_refresh ();
765 return_status = check_progress_buttons (ctx);
767 if (return_status != FILE_CONT)
769 mc_refresh ();
770 goto ret;
775 dst_status = DEST_FULL; /* copy successful, don't remove target file */
777 ret:
778 while (src_desc != -1 && mc_close (src_desc) < 0)
780 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
781 if (temp_status == FILE_RETRY)
782 continue;
783 if (temp_status == FILE_ABORT)
784 return_status = temp_status;
785 break;
788 while (dest_desc != -1 && mc_close (dest_desc) < 0)
790 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
791 if (temp_status == FILE_RETRY)
792 continue;
793 return_status = temp_status;
794 break;
797 if (dst_status == DEST_SHORT)
799 /* Remove short file */
800 int result;
801 result = query_dialog (Q_ ("DialogTitle|Copy"),
802 _("Incomplete file was retrieved. Keep it?"),
803 D_ERROR, 2, _("&Delete"), _("&Keep"));
804 if (result == 0)
805 mc_unlink (dst_path);
807 else if (dst_status == DEST_FULL)
809 /* Copy has succeeded */
810 if (!appending && ctx->preserve_uidgid)
812 while (mc_chown (dst_path, src_uid, src_gid))
814 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
815 if (temp_status == FILE_RETRY)
816 continue;
817 return_status = temp_status;
818 break;
822 if (!appending)
824 if (ctx->preserve)
826 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill)))
828 temp_status =
829 file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
830 if (temp_status != FILE_RETRY)
832 return_status = temp_status;
833 break;
837 else
839 src_mode = umask (-1);
840 umask (src_mode);
841 src_mode = 0100666 & ~src_mode;
842 mc_chmod (dst_path, (src_mode & ctx->umask_kill));
844 mc_utime (dst_path, &utb);
848 if (return_status == FILE_CONT)
849 return_status = progress_update_one (tctx, ctx, file_size, tctx->is_toplevel_file);
851 return return_status;
855 * I think these copy_*_* functions should have a return type.
856 * anyway, this function *must* have two directories as arguments.
858 /* FIXME: This function needs to check the return values of the
859 function calls */
860 FileProgressStatus
861 copy_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *_d,
862 gboolean toplevel, gboolean move_over, gboolean do_delete, struct link * parent_dirs)
864 struct dirent *next;
865 struct stat buf, cbuf;
866 DIR *reading;
867 char *dest_dir = NULL;
868 FileProgressStatus return_status = FILE_CONT;
869 struct utimbuf utb;
870 struct link *lp;
871 char *d;
873 d = g_strdup (_d);
875 /* First get the mode of the source dir */
876 retry_src_stat:
877 if ((*ctx->stat_func) (s, &cbuf))
879 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
880 if (return_status == FILE_RETRY)
881 goto retry_src_stat;
882 goto ret_fast;
885 if (is_in_linklist (dest_dirs, s, &cbuf))
887 /* Don't copy a directory we created before (we don't want to copy
888 infinitely if a directory is copied into itself) */
889 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
890 return_status = FILE_CONT;
891 goto ret_fast;
894 /* Hmm, hardlink to directory??? - Norbert */
895 /* FIXME: In this step we should do something
896 in case the destination already exist */
897 /* Check the hardlinks */
898 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf) == 1)
900 /* We have made a hardlink - no more processing is necessary */
901 goto ret_fast;
904 if (!S_ISDIR (cbuf.st_mode))
906 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
907 if (return_status == FILE_RETRY)
908 goto retry_src_stat;
909 goto ret_fast;
912 if (is_in_linklist (parent_dirs, s, &cbuf))
914 /* we found a cyclic symbolic link */
915 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
916 return_status = FILE_SKIP;
917 goto ret_fast;
920 lp = g_new (struct link, 1);
921 lp->vfs = vfs_get_class (s);
922 lp->ino = cbuf.st_ino;
923 lp->dev = cbuf.st_dev;
924 lp->next = parent_dirs;
925 parent_dirs = lp;
927 retry_dst_stat:
928 /* Now, check if the dest dir exists, if not, create it. */
929 if (mc_stat (d, &buf))
931 /* Here the dir doesn't exist : make it ! */
932 if (move_over)
934 if (mc_rename (s, d) == 0)
936 return_status = FILE_CONT;
937 goto ret;
940 dest_dir = d;
941 d = NULL;
943 else
946 * If the destination directory exists, we want to copy the whole
947 * directory, but we only want this to happen once.
949 * Escape sequences added to the * to compiler warnings.
950 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
951 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
953 if (!S_ISDIR (buf.st_mode))
955 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
956 if (return_status == FILE_RETRY)
957 goto retry_dst_stat;
958 goto ret;
960 /* Dive into subdir if exists */
961 if (toplevel && ctx->dive_into_subdirs)
963 dest_dir = concat_dir_and_file (d, x_basename (s));
965 else
967 dest_dir = d;
968 d = NULL;
969 goto dont_mkdir;
972 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU))
974 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), dest_dir);
975 if (return_status != FILE_RETRY)
976 goto ret;
979 lp = g_new (struct link, 1);
980 mc_stat (dest_dir, &buf);
981 lp->vfs = vfs_get_class (dest_dir);
982 lp->ino = buf.st_ino;
983 lp->dev = buf.st_dev;
984 lp->next = dest_dirs;
985 dest_dirs = lp;
987 if (ctx->preserve_uidgid)
989 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid))
991 return_status =
992 file_error (_("Cannot chown target directory \"%s\"\n%s"), dest_dir);
993 if (return_status != FILE_RETRY)
994 goto ret;
998 dont_mkdir:
999 /* open the source dir for reading */
1000 reading = mc_opendir (s);
1001 if (reading == NULL)
1002 goto ret;
1004 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1006 char *path;
1008 * Now, we don't want '.' and '..' to be created / copied at any time
1010 if (!strcmp (next->d_name, "."))
1011 continue;
1012 if (!strcmp (next->d_name, ".."))
1013 continue;
1015 /* get the filename and add it to the src directory */
1016 path = concat_dir_and_file (s, next->d_name);
1018 (*ctx->stat_func) (path, &buf);
1019 if (S_ISDIR (buf.st_mode))
1021 char *mdpath;
1023 mdpath = concat_dir_and_file (dest_dir, next->d_name);
1025 * From here, we just intend to recursively copy subdirs, not
1026 * the double functionality of copying different when the target
1027 * dir already exists. So, we give the recursive call the flag 0
1028 * meaning no toplevel.
1030 return_status =
1031 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
1032 g_free (mdpath);
1034 else
1036 char *dest_file;
1038 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
1039 return_status = copy_file_file (tctx, ctx, path, dest_file);
1040 g_free (dest_file);
1042 if (do_delete && return_status == FILE_CONT)
1044 if (ctx->erase_at_end)
1046 static struct link *tail;
1047 size_t len = strlen (path);
1048 lp = g_malloc (sizeof (struct link) + len);
1049 strncpy (lp->name, path, len + 1);
1050 lp->st_mode = buf.st_mode;
1051 lp->next = NULL;
1052 if (erase_list != NULL)
1054 tail->next = lp;
1055 tail = lp;
1057 else
1058 erase_list = tail = lp;
1060 else
1062 if (S_ISDIR (buf.st_mode))
1064 return_status = erase_dir_iff_empty (ctx, path);
1066 else
1067 return_status = erase_file (tctx, ctx, path, FALSE);
1070 g_free (path);
1072 mc_closedir (reading);
1074 if (ctx->preserve)
1076 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1077 utb.actime = cbuf.st_atime;
1078 utb.modtime = cbuf.st_mtime;
1079 mc_utime (dest_dir, &utb);
1081 else
1083 cbuf.st_mode = umask (-1);
1084 umask (cbuf.st_mode);
1085 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
1086 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1089 ret:
1090 g_free (dest_dir);
1091 g_free (parent_dirs);
1092 ret_fast:
1093 g_free (d);
1094 return return_status;
1097 /* }}} */
1099 /* {{{ Move routines */
1101 static FileProgressStatus
1102 move_file_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
1104 struct stat src_stats, dst_stats;
1105 FileProgressStatus return_status = FILE_CONT;
1106 gboolean copy_done = FALSE;
1107 gboolean old_ask_overwrite;
1109 file_progress_show_source (ctx, s);
1110 file_progress_show_target (ctx, d);
1111 if (check_progress_buttons (ctx) == FILE_ABORT)
1112 return FILE_ABORT;
1114 mc_refresh ();
1116 while (mc_lstat (s, &src_stats) != 0)
1118 /* Source doesn't exist */
1119 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
1120 if (return_status != FILE_RETRY)
1121 return return_status;
1124 if (mc_lstat (d, &dst_stats) == 0)
1126 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
1127 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
1129 if (S_ISDIR (dst_stats.st_mode))
1131 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
1132 do_refresh ();
1133 return FILE_SKIP;
1136 if (confirm_overwrite)
1138 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1139 if (return_status != FILE_CONT)
1140 return return_status;
1142 /* Ok to overwrite */
1145 if (!ctx->do_append)
1147 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
1149 return_status = make_symlink (ctx, s, d);
1150 if (return_status == FILE_CONT)
1151 goto retry_src_remove;
1152 else
1153 return return_status;
1156 if (mc_rename (s, d) == 0)
1157 return progress_update_one (tctx, ctx, src_stats.st_size, TRUE);
1159 #if 0
1160 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1161 one nfs to the same, but on the server it is on two different
1162 filesystems. Then nfs returns EIO instead of EXDEV.
1163 Hope it will not hurt if we always in case of error try to copy/delete. */
1164 else
1165 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1167 if (errno != EXDEV)
1169 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
1170 if (return_status == FILE_RETRY)
1171 goto retry_rename;
1172 return return_status;
1174 #endif
1176 /* Failed because filesystem boundary -> copy the file instead */
1177 old_ask_overwrite = tctx->ask_overwrite;
1178 tctx->ask_overwrite = FALSE;
1179 return_status = copy_file_file (tctx, ctx, s, d);
1180 tctx->ask_overwrite = old_ask_overwrite;
1181 if (return_status != FILE_CONT)
1182 return return_status;
1184 copy_done = TRUE;
1186 file_progress_show_source (ctx, NULL);
1187 file_progress_show (ctx, 0, 0, "", FALSE);
1189 return_status = check_progress_buttons (ctx);
1190 if (return_status != FILE_CONT)
1191 return return_status;
1193 mc_refresh ();
1195 retry_src_remove:
1196 if (mc_unlink (s))
1198 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
1199 if (return_status == FILE_RETRY)
1200 goto retry_src_remove;
1201 return return_status;
1204 if (!copy_done)
1205 return_status = progress_update_one (tctx, ctx, src_stats.st_size, TRUE);
1207 return return_status;
1210 FileProgressStatus
1211 move_dir_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s, const char *d)
1213 struct stat sbuf, dbuf, destbuf;
1214 struct link *lp;
1215 char *destdir;
1216 FileProgressStatus return_status;
1217 gboolean move_over = FALSE;
1218 gboolean dstat_ok;
1220 file_progress_show_source (ctx, s);
1221 file_progress_show_target (ctx, d);
1222 if (check_progress_buttons (ctx) == FILE_ABORT)
1223 return FILE_ABORT;
1225 mc_refresh ();
1227 mc_stat (s, &sbuf);
1228 dstat_ok = (mc_stat (d, &dbuf) == 0);
1230 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
1231 return warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
1233 if (!dstat_ok)
1234 destdir = g_strdup (d); /* destination doesn't exist */
1235 else if (!ctx->dive_into_subdirs)
1237 destdir = g_strdup (d);
1238 move_over = TRUE;
1240 else
1241 destdir = concat_dir_and_file (d, x_basename (s));
1243 /* Check if the user inputted an existing dir */
1244 retry_dst_stat:
1245 if (!mc_stat (destdir, &destbuf))
1247 if (move_over)
1249 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL);
1251 if (return_status != FILE_CONT)
1252 goto ret;
1253 goto oktoret;
1255 else
1257 if (S_ISDIR (destbuf.st_mode))
1258 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), destdir);
1259 else
1260 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), destdir);
1261 if (return_status == FILE_RETRY)
1262 goto retry_dst_stat;
1264 g_free (destdir);
1265 return return_status;
1268 retry_rename:
1269 if (mc_rename (s, destdir) == 0)
1271 return_status = FILE_CONT;
1272 goto ret;
1275 if (errno != EXDEV)
1277 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
1278 if (return_status == FILE_RETRY)
1279 goto retry_rename;
1280 goto ret;
1282 /* Failed because of filesystem boundary -> copy dir instead */
1283 return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL);
1285 if (return_status != FILE_CONT)
1286 goto ret;
1287 oktoret:
1288 file_progress_show_source (ctx, NULL);
1289 file_progress_show (ctx, 0, 0, "", FALSE);
1291 return_status = check_progress_buttons (ctx);
1292 if (return_status != FILE_CONT)
1293 goto ret;
1295 mc_refresh ();
1296 if (ctx->erase_at_end)
1298 for (; erase_list && return_status != FILE_ABORT;)
1300 if (S_ISDIR (erase_list->st_mode))
1302 return_status = erase_dir_iff_empty (ctx, erase_list->name);
1304 else
1305 return_status = erase_file (tctx, ctx, erase_list->name, FALSE);
1306 lp = erase_list;
1307 erase_list = erase_list->next;
1308 g_free (lp);
1311 erase_dir_iff_empty (ctx, s);
1313 ret:
1314 g_free (destdir);
1315 while (erase_list)
1317 lp = erase_list;
1318 erase_list = erase_list->next;
1319 g_free (lp);
1321 return return_status;
1324 /* }}} */
1326 /* {{{ Erase routines */
1327 /* Don't update progress status if progress_count==NULL */
1328 static FileProgressStatus
1329 erase_file (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s,
1330 gboolean is_toplevel_file)
1332 int return_status;
1333 struct stat buf;
1335 file_progress_show_deleting (ctx, s);
1336 if (check_progress_buttons (ctx) == FILE_ABORT)
1337 return FILE_ABORT;
1338 mc_refresh ();
1340 if (tctx->progress_count && mc_lstat (s, &buf))
1342 /* ignore, most likely the mc_unlink fails, too */
1343 buf.st_size = 0;
1346 while (mc_unlink (s))
1348 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), s);
1349 if (return_status != FILE_RETRY)
1350 return return_status;
1353 if (tctx->progress_count)
1354 return progress_update_one (tctx, ctx, buf.st_size, is_toplevel_file);
1355 else
1356 return FILE_CONT;
1359 static FileProgressStatus
1360 recursive_erase (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
1362 struct dirent *next;
1363 struct stat buf;
1364 DIR *reading;
1365 char *path;
1366 FileProgressStatus return_status = FILE_CONT;
1368 if (!strcmp (s, ".."))
1369 return FILE_RETRY;
1371 reading = mc_opendir (s);
1373 if (!reading)
1374 return FILE_RETRY;
1376 while ((next = mc_readdir (reading)) && return_status == FILE_CONT)
1378 if (!strcmp (next->d_name, "."))
1379 continue;
1380 if (!strcmp (next->d_name, ".."))
1381 continue;
1382 path = concat_dir_and_file (s, next->d_name);
1383 if (mc_lstat (path, &buf))
1385 g_free (path);
1386 mc_closedir (reading);
1387 return FILE_RETRY;
1389 if (S_ISDIR (buf.st_mode))
1390 return_status =
1391 (recursive_erase (tctx, ctx, path) != FILE_CONT) ? FILE_RETRY : FILE_CONT;
1392 else
1393 return_status = erase_file (tctx, ctx, path, 0);
1394 g_free (path);
1396 mc_closedir (reading);
1397 if (return_status != FILE_CONT)
1398 return return_status;
1399 file_progress_show_deleting (ctx, s);
1400 if (check_progress_buttons (ctx) == FILE_ABORT)
1401 return FILE_ABORT;
1402 mc_refresh ();
1404 while (my_rmdir (s))
1406 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1407 if (return_status != FILE_RETRY)
1408 return return_status;
1411 return FILE_CONT;
1414 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1415 in the directory path points to, 0 else. */
1416 static int
1417 check_dir_is_empty (const char *path)
1419 DIR *dir;
1420 struct dirent *d;
1421 int i;
1423 dir = mc_opendir (path);
1424 if (!dir)
1425 return -1;
1427 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir))
1429 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1430 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1431 continue; /* "." or ".." */
1432 i = 0;
1433 break;
1436 mc_closedir (dir);
1437 return i;
1440 FileProgressStatus
1441 erase_dir (FileOpTotalContext * tctx, FileOpContext * ctx, const char *s)
1443 FileProgressStatus error;
1445 if (strcmp (s, "..") == 0)
1446 return FILE_SKIP;
1448 if (strcmp (s, ".") == 0)
1449 return FILE_SKIP;
1451 file_progress_show_deleting (ctx, s);
1452 if (check_progress_buttons (ctx) == FILE_ABORT)
1453 return FILE_ABORT;
1454 mc_refresh ();
1456 /* The old way to detect a non empty directory was:
1457 error = my_rmdir (s);
1458 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1459 For the linux user space nfs server (nfs-server-2.2beta29-2)
1460 we would have to check also for EIO. I hope the new way is
1461 fool proof. (Norbert)
1463 error = check_dir_is_empty (s);
1464 if (error == 0)
1465 { /* not empty */
1466 error = query_recursive (ctx, s);
1467 if (error == FILE_CONT)
1468 return recursive_erase (tctx, ctx, s);
1469 else
1470 return error;
1473 while (my_rmdir (s) == -1)
1475 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1476 if (error != FILE_RETRY)
1477 return error;
1480 return FILE_CONT;
1483 static FileProgressStatus
1484 erase_dir_iff_empty (FileOpContext * ctx, const char *s)
1486 FileProgressStatus error;
1488 if (strcmp (s, "..") == 0)
1489 return FILE_SKIP;
1491 if (strcmp (s, ".") == 0)
1492 return FILE_SKIP;
1494 file_progress_show_deleting (ctx, s);
1495 if (check_progress_buttons (ctx) == FILE_ABORT)
1496 return FILE_ABORT;
1497 mc_refresh ();
1499 if (1 != check_dir_is_empty (s)) /* not empty or error */
1500 return FILE_CONT;
1502 while (my_rmdir (s))
1504 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1505 if (error != FILE_RETRY)
1506 return error;
1509 return FILE_CONT;
1512 /* }}} */
1514 /* {{{ Panel operate routines */
1517 * Return currently selected entry name or the name of the first marked
1518 * entry if there is one.
1520 static char *
1521 panel_get_file (WPanel * panel, struct stat *stat_buf)
1523 int i;
1525 if (get_current_type () == view_tree)
1527 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
1528 char *tree_name = tree_selected_name (tree);
1530 mc_stat (tree_name, stat_buf);
1531 return tree_name;
1534 if (panel->marked)
1536 for (i = 0; i < panel->count; i++)
1537 if (panel->dir.list[i].f.marked)
1539 *stat_buf = panel->dir.list[i].st;
1540 return panel->dir.list[i].fname;
1543 else
1545 *stat_buf = panel->dir.list[panel->selected].st;
1546 return panel->dir.list[panel->selected].fname;
1548 g_assert_not_reached ();
1549 return NULL;
1553 ComputeDirSizeUI *
1554 compute_dir_size_create_ui (void)
1556 ComputeDirSizeUI *ui;
1558 const char *b_name = N_("&Abort");
1560 #ifdef ENABLE_NLS
1561 b_name = _(b_name);
1562 #endif
1564 ui = g_new (ComputeDirSizeUI, 1);
1566 ui->dlg = create_dlg (TRUE, 0, 0, 8, COLS / 2, dialog_colors, NULL,
1567 NULL, _("Directory scanning"), DLG_CENTER);
1568 ui->dirname = label_new (3, 3, "");
1569 add_widget (ui->dlg, ui->dirname);
1571 add_widget (ui->dlg,
1572 button_new (5, (ui->dlg->cols - strlen (b_name)) / 2,
1573 FILE_ABORT, NORMAL_BUTTON, b_name, NULL));
1575 /* We will manage the dialog without any help,
1576 that's why we have to call init_dlg */
1577 init_dlg (ui->dlg);
1579 return ui;
1582 void
1583 compute_dir_size_destroy_ui (ComputeDirSizeUI * ui)
1585 if (ui != NULL)
1587 /* schedule to update passive panel */
1588 other_panel->dirty = 1;
1590 /* close and destroy dialog */
1591 dlg_run_done (ui->dlg);
1592 destroy_dlg (ui->dlg);
1593 g_free (ui);
1597 FileProgressStatus
1598 compute_dir_size_update_ui (const void *ui, const char *dirname)
1600 const ComputeDirSizeUI *this = (const ComputeDirSizeUI *) ui;
1601 int c;
1602 Gpm_Event event;
1604 if (ui == NULL)
1605 return FILE_CONT;
1607 label_set_text (this->dirname, name_trunc (dirname, this->dlg->cols - 6));
1609 event.x = -1; /* Don't show the GPM cursor */
1610 c = tty_get_event (&event, FALSE, FALSE);
1611 if (c == EV_NONE)
1612 return FILE_CONT;
1614 /* Reinitialize to avoid old values after events other than
1615 selecting a button */
1616 this->dlg->ret_value = FILE_CONT;
1618 dlg_process_event (this->dlg, c, &event);
1620 switch (this->dlg->ret_value)
1622 case B_CANCEL:
1623 case FILE_ABORT:
1624 return FILE_ABORT;
1625 default:
1626 return FILE_CONT;
1631 * compute_dir_size:
1633 * Computes the number of bytes used by the files in a directory
1635 FileProgressStatus
1636 compute_dir_size (const char *dirname, const void *ui,
1637 compute_dir_size_callback cback,
1638 off_t * ret_marked, double *ret_total, gboolean compute_symlinks)
1640 int res;
1641 struct stat s;
1642 DIR *dir;
1643 struct dirent *dirent;
1644 FileProgressStatus ret = FILE_CONT;
1646 if (!compute_symlinks)
1648 res = mc_lstat (dirname, &s);
1649 if (res != 0)
1650 return ret;
1652 /* don't scan symlink to directory */
1653 if (S_ISLNK (s.st_mode))
1655 (*ret_marked)++;
1656 *ret_total += s.st_size;
1657 return ret;
1661 dir = mc_opendir (dirname);
1663 if (dir == NULL)
1664 return ret;
1666 while ((dirent = mc_readdir (dir)) != NULL)
1668 char *fullname;
1670 ret = (cback != NULL) ? cback (ui, dirname) : FILE_CONT;
1672 if (ret != FILE_CONT)
1673 break;
1675 if (strcmp (dirent->d_name, ".") == 0)
1676 continue;
1677 if (strcmp (dirent->d_name, "..") == 0)
1678 continue;
1680 fullname = concat_dir_and_file (dirname, dirent->d_name);
1681 res = mc_lstat (fullname, &s);
1683 if (res != 0)
1685 g_free (fullname);
1686 continue;
1689 if (S_ISDIR (s.st_mode))
1691 off_t subdir_count = 0;
1692 double subdir_bytes = 0;
1694 ret =
1695 compute_dir_size (fullname, ui, cback, &subdir_count, &subdir_bytes,
1696 compute_symlinks);
1698 if (ret != FILE_CONT)
1700 g_free (fullname);
1701 break;
1704 *ret_marked += subdir_count;
1705 *ret_total += subdir_bytes;
1707 else
1709 (*ret_marked)++;
1710 *ret_total += s.st_size;
1713 g_free (fullname);
1716 mc_closedir (dir);
1718 return ret;
1722 * panel_compute_totals:
1724 * compute the number of files and the number of bytes
1725 * used up by the whole selection, recursing directories
1726 * as required. In addition, it checks to see if it will
1727 * overwrite any files by doing the copy.
1729 static FileProgressStatus
1730 panel_compute_totals (const WPanel * panel, const void *ui,
1731 compute_dir_size_callback cback,
1732 off_t * ret_marked, double *ret_total, gboolean compute_symlinks)
1734 int i;
1736 *ret_marked = 0;
1737 *ret_total = 0.0;
1739 for (i = 0; i < panel->count; i++)
1741 struct stat *s;
1743 if (!panel->dir.list[i].f.marked)
1744 continue;
1746 s = &panel->dir.list[i].st;
1748 if (S_ISDIR (s->st_mode))
1750 char *dir_name;
1751 off_t subdir_count = 0;
1752 double subdir_bytes = 0;
1753 FileProgressStatus status;
1755 dir_name = concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1757 status = compute_dir_size (dir_name, ui, cback,
1758 &subdir_count, &subdir_bytes, compute_symlinks);
1759 g_free (dir_name);
1761 if (status != FILE_CONT)
1762 return FILE_ABORT;
1764 *ret_marked += subdir_count;
1765 *ret_total += subdir_bytes;
1767 else
1769 (*ret_marked)++;
1770 *ret_total += s->st_size;
1774 return FILE_CONT;
1777 /* Initialize variables for progress bars */
1778 static FileProgressStatus
1779 panel_operate_init_totals (FileOperation operation,
1780 const WPanel * panel, const char *source, FileOpContext * ctx)
1782 FileProgressStatus status;
1784 if (operation != OP_MOVE && verbose && file_op_compute_totals)
1786 ComputeDirSizeUI *ui;
1788 ui = compute_dir_size_create_ui ();
1790 if (source != NULL)
1791 status = compute_dir_size (source, ui, compute_dir_size_update_ui,
1792 &ctx->progress_count, &ctx->progress_bytes,
1793 ctx->follow_links);
1794 else
1795 status = panel_compute_totals (panel, ui, compute_dir_size_update_ui,
1796 &ctx->progress_count, &ctx->progress_bytes,
1797 ctx->follow_links);
1799 compute_dir_size_destroy_ui (ui);
1801 ctx->progress_totals_computed = (status == FILE_CONT);
1803 else
1805 status = FILE_CONT;
1806 ctx->progress_count = panel->marked;
1807 ctx->progress_bytes = panel->total;
1808 ctx->progress_totals_computed = FALSE;
1811 return status;
1815 * This array introduced to avoid translation problems. The former (op_names)
1816 * is assumed to be nouns, suitable in dialog box titles; this one should
1817 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1818 * (I don't use spaces around the words, because someday they could be
1819 * dropped, when widgets get smarter)
1822 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
1823 static const char *op_names1[] = {
1824 N_("FileOperation|Copy"),
1825 N_("FileOperation|Move"),
1826 N_("FileOperation|Delete")
1830 * These are formats for building a prompt. Parts encoded as follows:
1831 * %o - operation from op_names1
1832 * %f - file/files or files/directories, as appropriate
1833 * %m - "with source mask" or question mark for delete
1834 * %s - source name (truncated)
1835 * %d - number of marked files
1836 * %e - "to:" or question mark for delete
1838 * xgettext:no-c-format */
1839 static const char *one_format = N_("%o %f \"%s\"%m");
1840 /* xgettext:no-c-format */
1841 static const char *many_format = N_("%o %d %f%m");
1843 static const char *prompt_parts[] = {
1844 N_("file"),
1845 N_("files"),
1846 N_("directory"),
1847 N_("directories"),
1848 N_("files/directories"),
1849 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
1850 N_(" with source mask:"),
1851 N_("to:")
1854 static const char *question_format = N_("%s?");
1857 * Generate user prompt for panel operation.
1858 * single_source is the name if the source entry or NULL for multiple
1859 * entries.
1860 * src_stat is only used when single_source is not NULL.
1862 static char *
1863 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1864 gboolean single_source, const struct stat *src_stat)
1866 const char *sp, *cp;
1867 char format_string[BUF_MEDIUM];
1868 char *dp = format_string;
1869 gboolean build_question = FALSE;
1871 static gboolean i18n_flag = FALSE;
1872 if (!i18n_flag)
1874 size_t i;
1876 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1877 op_names1[i] = Q_ (op_names1[i]);
1879 #ifdef ENABLE_NLS
1880 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1881 prompt_parts[i] = _(prompt_parts[i]);
1883 one_format = _(one_format);
1884 many_format = _(many_format);
1885 question_format = _(question_format);
1886 #endif /* ENABLE_NLS */
1887 i18n_flag = TRUE;
1890 sp = single_source ? one_format : many_format;
1892 while (*sp != '\0')
1894 switch (*sp)
1896 case '%':
1897 cp = NULL;
1898 switch (sp[1])
1900 case 'o':
1901 cp = op_names1[operation];
1902 break;
1903 case 'm':
1904 if (operation == OP_DELETE)
1906 cp = "";
1907 build_question = TRUE;
1909 else
1910 cp = prompt_parts[5];
1911 break;
1912 case 'e':
1913 if (operation == OP_DELETE)
1915 cp = "";
1916 build_question = TRUE;
1918 else
1919 cp = prompt_parts[6];
1920 break;
1921 case 'f':
1922 if (single_source)
1923 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1924 else
1925 cp = (panel->marked == panel->dirs_marked)
1926 ? prompt_parts[3]
1927 : (panel->dirs_marked ? prompt_parts[4] : prompt_parts[1]);
1928 break;
1929 default:
1930 *dp++ = *sp++;
1933 if (cp != NULL)
1935 sp += 2;
1936 while (*cp != '\0')
1937 *dp++ = *cp++;
1939 break;
1940 default:
1941 *dp++ = *sp++;
1944 *dp = '\0';
1946 if (build_question)
1948 char tmp[BUF_MEDIUM];
1950 memmove (tmp, format_string, sizeof (tmp));
1951 g_snprintf (format_string, sizeof (format_string), question_format, tmp);
1954 return g_strdup (format_string);
1957 #ifdef WITH_BACKGROUND
1958 static int
1959 end_bg_process (FileOpContext * ctx, enum OperationMode mode)
1961 int pid = ctx->pid;
1963 (void) mode;
1964 ctx->pid = 0;
1966 unregister_task_with_pid (pid);
1967 /* file_op_context_destroy(ctx); */
1968 return 1;
1970 #endif
1973 * panel_operate:
1975 * Performs one of the operations on the selection on the source_panel
1976 * (copy, delete, move).
1978 * Returns TRUE if did change the directory
1979 * structure, Returns FALSE if user aborted
1981 * force_single forces operation on the current entry and affects
1982 * default destination. Current filename is used as default.
1984 gboolean
1985 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
1987 WPanel *panel = (WPanel *) source_panel;
1988 const gboolean single_entry = force_single || (panel->marked <= 1)
1989 || (get_current_type () == view_tree);
1991 char *source = NULL;
1992 #ifdef WITH_FULL_PATHS
1993 char *source_with_path = NULL;
1994 #else
1995 # define source_with_path source
1996 #endif /* !WITH_FULL_PATHS */
1997 char *dest = NULL;
1998 char *temp = NULL;
1999 char *save_cwd = NULL, *save_dest = NULL;
2000 struct stat src_stat;
2001 gboolean ret_val = TRUE;
2002 int i;
2003 FileProgressStatus value;
2004 FileOpContext *ctx;
2005 FileOpTotalContext *tctx;
2007 gboolean do_bg = FALSE; /* do background operation? */
2009 static gboolean i18n_flag = FALSE;
2010 if (!i18n_flag)
2012 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
2013 op_names[i] = Q_ (op_names[i]);
2014 i18n_flag = TRUE;
2017 free_linklist (&linklist);
2018 free_linklist (&dest_dirs);
2020 /* Update panel contents to avoid actions on deleted files */
2021 if (!panel->is_panelized)
2023 update_panels (UP_RELOAD, UP_KEEPSEL);
2024 repaint_screen ();
2027 if (single_entry)
2029 if (force_single)
2031 source = selection (panel)->fname;
2032 src_stat = selection (panel)->st;
2034 else
2035 source = panel_get_file (panel, &src_stat);
2037 if (!strcmp (source, ".."))
2039 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2040 return FALSE;
2044 ctx = file_op_context_new (operation);
2045 tctx = file_op_total_context_new ();
2046 gettimeofday (&(tctx->transfer_start), (struct timezone *) NULL);
2048 /* Show confirmation dialog */
2049 if (operation != OP_DELETE)
2051 char *dest_dir;
2052 char *dest_dir_;
2053 char *format;
2055 /* Forced single operations default to the original name */
2056 if (force_single)
2057 dest_dir = source;
2058 else if (get_other_type () == view_listing)
2059 dest_dir = other_panel->cwd;
2060 else
2061 dest_dir = panel->cwd;
2063 * Add trailing backslash only when do non-local ops.
2064 * It saves user from occasional file renames (when destination
2065 * dir is deleted)
2067 if (!force_single && dest_dir[0] != '\0' && dest_dir[strlen (dest_dir) - 1] != PATH_SEP)
2069 /* add trailing separator */
2070 dest_dir_ = g_strconcat (dest_dir, PATH_SEP_STR, (char *) NULL);
2072 else
2074 /* just copy */
2075 dest_dir_ = g_strdup (dest_dir);
2077 if (dest_dir_ == NULL)
2079 file_op_total_context_destroy (tctx);
2080 file_op_context_destroy (ctx);
2081 return FALSE;
2084 /* Generate confirmation prompt */
2085 format = panel_operate_generate_prompt (panel, operation, source != NULL, &src_stat);
2087 dest = file_mask_dialog (ctx, operation, source != NULL, format,
2088 source != NULL ? (void *) source
2089 : (void *) &panel->marked, dest_dir_, &do_bg);
2091 g_free (format);
2092 g_free (dest_dir_);
2094 if (dest == NULL || dest[0] == '\0')
2096 file_op_total_context_destroy (tctx);
2097 file_op_context_destroy (ctx);
2098 g_free (dest);
2099 return FALSE;
2102 else if (confirm_delete)
2104 char *format;
2105 char fmd_buf[BUF_MEDIUM];
2107 /* Generate confirmation prompt */
2108 format = panel_operate_generate_prompt (panel, OP_DELETE, source != NULL, &src_stat);
2110 if (source == NULL)
2111 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2112 else
2114 const int fmd_xlen = 64;
2115 i = fmd_xlen - str_term_width1 (format) - 4;
2116 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2119 g_free (format);
2121 if (safe_delete)
2122 query_set_sel (1);
2124 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2126 if (i != 0)
2128 file_op_total_context_destroy (tctx);
2129 file_op_context_destroy (ctx);
2130 return FALSE;
2135 filegui_dialog_type_t dialog_type;
2137 if (operation == OP_DELETE)
2138 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2139 else
2141 dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single))
2142 ? FILEGUI_DIALOG_MULTI_ITEM : FILEGUI_DIALOG_ONE_ITEM;
2144 if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode))
2145 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2148 /* Background also need ctx->ui, but not full */
2149 if (do_bg)
2150 file_op_context_create_ui_without_init (ctx, 1, dialog_type);
2151 else
2152 file_op_context_create_ui (ctx, 1, dialog_type);
2155 #ifdef WITH_BACKGROUND
2156 /* Did the user select to do a background operation? */
2157 if (do_bg)
2159 int v;
2161 v = do_background (ctx, g_strconcat (op_names[operation], ": ", panel->cwd, (char *) NULL));
2162 if (v == -1)
2163 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2165 /* If we are the parent */
2166 if (v == 1)
2168 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
2169 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
2170 /* file_op_context_destroy (ctx); */
2171 return FALSE;
2174 #endif /* WITH_BACKGROUND */
2176 /* Initialize things */
2177 /* We do not want to trash cache every time file is
2178 created/touched. However, this will make our cache contain
2179 invalid data. */
2180 if ((dest != NULL) && (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1)))
2181 save_dest = g_strdup (dest);
2183 if ((panel->cwd[0] != '\0') && (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1)))
2184 save_cwd = g_strdup (panel->cwd);
2186 /* Now, let's do the job */
2188 /* This code is only called by the tree and panel code */
2189 if (single_entry)
2191 /* We now have ETA in all cases */
2193 /* One file: FIXME mc_chdir will take user out of any vfs */
2194 if ((operation != OP_COPY) && (get_current_type () == view_tree) &&
2195 (mc_chdir (PATH_SEP_STR) < 0))
2197 ret_val = FALSE;
2198 goto clean_up;
2201 /* The source and src_stat variables have been initialized before */
2202 #ifdef WITH_FULL_PATHS
2203 source_with_path = concat_dir_and_file (panel->cwd, source);
2204 #endif /* WITH_FULL_PATHS */
2206 if (panel_operate_init_totals (operation, panel, source_with_path, ctx) == FILE_CONT)
2208 if (operation == OP_DELETE)
2210 if (S_ISDIR (src_stat.st_mode))
2211 value = erase_dir (tctx, ctx, source_with_path);
2212 else
2213 value = erase_file (tctx, ctx, source_with_path, 1);
2215 else
2217 temp = transform_source (ctx, source_with_path);
2218 if (temp == NULL)
2219 value = transform_error;
2220 else
2222 char *repl_dest, *temp2;
2224 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2225 temp2 = concat_dir_and_file (repl_dest, temp);
2226 g_free (temp);
2227 g_free (repl_dest);
2228 g_free (dest);
2229 dest = temp2;
2231 switch (operation)
2233 case OP_COPY:
2234 /* we use file_mask_op_follow_links only with OP_COPY */
2235 ctx->stat_func (source_with_path, &src_stat);
2237 if (S_ISDIR (src_stat.st_mode))
2238 value = copy_dir_dir (tctx, ctx, source_with_path, dest,
2239 TRUE, FALSE, FALSE, NULL);
2240 else
2241 value = copy_file_file (tctx, ctx, source_with_path, dest);
2242 break;
2244 case OP_MOVE:
2245 if (S_ISDIR (src_stat.st_mode))
2246 value = move_dir_dir (tctx, ctx, source_with_path, dest);
2247 else
2248 value = move_file_file (tctx, ctx, source_with_path, dest);
2249 break;
2251 default:
2252 /* Unknown file operation */
2253 abort ();
2256 } /* Copy or move operation */
2258 if ((value == FILE_CONT) && !force_single)
2259 unmark_files (panel);
2262 else
2264 /* Many files */
2266 /* Check destination for copy or move operation */
2267 while (operation != OP_DELETE)
2269 int dst_result;
2270 struct stat dst_stat;
2272 dst_result = mc_stat (dest, &dst_stat);
2274 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2275 break;
2277 if (file_error (_("Destination \"%s\" must be a directory\n%s"),
2278 dest) != FILE_RETRY)
2279 goto clean_up;
2282 if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT)
2284 /* Loop for every file, perform the actual copy operation */
2285 for (i = 0; i < panel->count; i++)
2287 if (!panel->dir.list[i].f.marked)
2288 continue; /* Skip the unmarked ones */
2290 source = panel->dir.list[i].fname;
2291 src_stat = panel->dir.list[i].st;
2293 #ifdef WITH_FULL_PATHS
2294 g_free (source_with_path);
2295 source_with_path = concat_dir_and_file (panel->cwd, source);
2296 #endif /* WITH_FULL_PATHS */
2298 if (operation == OP_DELETE)
2300 if (S_ISDIR (src_stat.st_mode))
2301 value = erase_dir (tctx, ctx, source_with_path);
2302 else
2303 value = erase_file (tctx, ctx, source_with_path, 1);
2305 else
2307 temp = transform_source (ctx, source_with_path);
2309 if (temp == NULL)
2310 value = transform_error;
2311 else
2313 char *temp2, *temp3, *repl_dest;
2315 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2316 temp2 = concat_dir_and_file (repl_dest, temp);
2317 g_free (temp);
2318 g_free (repl_dest);
2319 temp3 = source_with_path;
2320 source_with_path = strutils_shell_unescape (source_with_path);
2321 g_free (temp3);
2322 temp3 = temp2;
2323 temp2 = strutils_shell_unescape (temp2);
2324 g_free (temp3);
2326 switch (operation)
2328 case OP_COPY:
2329 /* we use file_mask_op_follow_links only with OP_COPY */
2330 ctx->stat_func (source_with_path, &src_stat);
2331 if (S_ISDIR (src_stat.st_mode))
2332 value = copy_dir_dir (tctx, ctx, source_with_path, temp2,
2333 TRUE, FALSE, FALSE, NULL);
2334 else
2335 value = copy_file_file (tctx, ctx, source_with_path, temp2);
2336 free_linklist (&dest_dirs);
2337 break;
2339 case OP_MOVE:
2340 if (S_ISDIR (src_stat.st_mode))
2341 value = move_dir_dir (tctx, ctx, source_with_path, temp2);
2342 else
2343 value = move_file_file (tctx, ctx, source_with_path, temp2);
2344 break;
2346 default:
2347 /* Unknown file operation */
2348 abort ();
2351 g_free (temp2);
2353 } /* Copy or move operation */
2355 if (value == FILE_ABORT)
2356 break;
2358 if (value == FILE_CONT)
2359 do_file_mark (panel, i, 0);
2361 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2363 if (verbose)
2365 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
2367 if (operation != OP_DELETE)
2368 file_progress_show (ctx, 0, 0, "", FALSE);
2371 if (check_progress_buttons (ctx) == FILE_ABORT)
2372 break;
2374 mc_refresh ();
2375 } /* Loop for every file */
2377 } /* Many entries */
2379 clean_up:
2380 /* Clean up */
2381 if (save_cwd != NULL)
2383 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2384 g_free (save_cwd);
2387 if (save_dest != NULL)
2389 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2390 g_free (save_dest);
2393 free_linklist (&linklist);
2394 free_linklist (&dest_dirs);
2395 #ifdef WITH_FULL_PATHS
2396 g_free (source_with_path);
2397 #endif /* WITH_FULL_PATHS */
2398 g_free (dest);
2399 g_free (ctx->dest_mask);
2400 ctx->dest_mask = NULL;
2402 #ifdef WITH_BACKGROUND
2403 /* Let our parent know we are saying bye bye */
2404 if (we_are_background)
2406 int cur_pid = getpid ();
2407 /* Send pid to parent with child context, it is fork and
2408 don't modify real parent ctx */
2409 ctx->pid = cur_pid;
2410 parent_call ((void *) end_bg_process, ctx, 0);
2412 vfs_shut ();
2413 _exit (0);
2415 #endif /* WITH_BACKGROUND */
2417 file_op_context_destroy (ctx);
2418 file_op_total_context_destroy (tctx);
2420 return ret_val;
2423 /* }}} */
2425 /* {{{ Query/status report routines */
2427 static FileProgressStatus
2428 real_do_file_error (enum OperationMode mode, const char *error)
2430 int result;
2431 const char *msg;
2433 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
2434 result = query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"), _("&Abort"));
2436 switch (result)
2438 case 0:
2439 do_refresh ();
2440 return FILE_SKIP;
2442 case 1:
2443 do_refresh ();
2444 return FILE_RETRY;
2446 case 2:
2447 default:
2448 return FILE_ABORT;
2452 /* Report error with one file */
2453 FileProgressStatus
2454 file_error (const char *format, const char *file)
2456 char buf[BUF_MEDIUM];
2458 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
2460 return do_file_error (buf);
2463 /* Report error with two files */
2464 static FileProgressStatus
2465 files_error (const char *format, const char *file1, const char *file2)
2467 char buf[BUF_MEDIUM];
2468 char *nfile1 = g_strdup (path_trunc (file1, 15));
2469 char *nfile2 = g_strdup (path_trunc (file2, 15));
2471 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
2473 g_free (nfile1);
2474 g_free (nfile2);
2476 return do_file_error (buf);
2479 static FileProgressStatus
2480 real_query_recursive (FileOpContext * ctx, enum OperationMode mode, const char *s)
2482 gchar *text;
2484 if (ctx->recursive_result < RECURSIVE_ALWAYS)
2486 const char *msg = mode == Foreground
2487 ? _("\nDirectory not empty.\nDelete it recursively?")
2488 : _("\nBackground process: Directory not empty.\nDelete it recursively?");
2489 text = g_strconcat (_("Delete:"), " ", path_trunc (s, 30), (char *) NULL);
2491 if (safe_delete)
2492 query_set_sel (1);
2494 ctx->recursive_result =
2495 (FileCopyMode) query_dialog (text, msg, D_ERROR, 5,
2496 _("&Yes"), _("&No"), _("A&ll"), _("Non&e"), _("&Abort"));
2498 if (ctx->recursive_result != RECURSIVE_ABORT)
2499 do_refresh ();
2500 g_free (text);
2503 switch (ctx->recursive_result)
2505 case RECURSIVE_YES:
2506 case RECURSIVE_ALWAYS:
2507 return FILE_CONT;
2509 case RECURSIVE_NO:
2510 case RECURSIVE_NEVER:
2511 return FILE_SKIP;
2513 case RECURSIVE_ABORT:
2514 default:
2515 return FILE_ABORT;
2519 #ifdef WITH_BACKGROUND
2520 static FileProgressStatus
2521 do_file_error (const char *str)
2523 union
2525 void *p;
2526 FileProgressStatus (*f) (enum OperationMode, const char *);
2527 } pntr;
2528 pntr.f = real_do_file_error;
2530 if (we_are_background)
2531 return parent_call (pntr.p, NULL, 1, strlen (str), str);
2532 else
2533 return real_do_file_error (Foreground, str);
2536 static FileProgressStatus
2537 query_recursive (FileOpContext * ctx, const char *s)
2539 union
2541 void *p;
2542 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *);
2543 } pntr;
2544 pntr.f = real_query_recursive;
2546 if (we_are_background)
2547 return parent_call (pntr.p, ctx, 1, strlen (s), s);
2548 else
2549 return real_query_recursive (ctx, Foreground, s);
2552 static FileProgressStatus
2553 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
2554 struct stat *_d_stat)
2556 union
2558 void *p;
2559 FileProgressStatus (*f) (FileOpContext *, enum OperationMode, const char *,
2560 struct stat *, struct stat *);
2561 } pntr;
2562 pntr.f = file_progress_real_query_replace;
2564 if (we_are_background)
2565 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
2566 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
2567 else
2568 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
2571 #else
2572 static FileProgressStatus
2573 do_file_error (const char *str)
2575 return real_do_file_error (Foreground, str);
2578 static FileProgressStatus
2579 query_recursive (FileOpContext * ctx, const char *s)
2581 return real_query_recursive (ctx, Foreground, s);
2584 static FileProgressStatus
2585 query_replace (FileOpContext * ctx, const char *destname, struct stat *_s_stat,
2586 struct stat *_d_stat)
2588 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
2591 #endif /* !WITH_BACKGROUND */
2594 Cause emacs to enter folding mode for this file:
2595 Local variables:
2596 end: