added a new parameter for completion flags to input_new
[midnight-commander.git] / src / file.c
blob3748fe9d5da6d2377de4ac0facf6df901d193289
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 /* {{{ Include files */
42 #include <config.h>
44 #include <ctype.h>
45 #include <errno.h>
46 #include <stdio.h>
47 #include <string.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
53 #include "mhl/memory.h"
54 #include "mhl/escape.h"
56 #include "global.h"
57 #include "tty.h"
58 #include "eregex.h"
59 #include "setup.h"
60 #include "color.h"
61 #include "win.h"
62 #include "dialog.h"
63 #include "widget.h"
64 #include "main.h" /* cmd_buf */
65 #include "layout.h"
66 #include "widget.h"
67 #include "wtools.h"
68 #include "background.h" /* we_are_background */
69 #include "util.h"
71 /* Needed for current_panel, other_panel and WTree */
72 #include "dir.h"
73 #include "panel.h"
74 #include "file.h"
75 #include "filegui.h"
76 #include "tree.h"
77 #include "key.h"
78 #include "../vfs/vfs-impl.h"
80 /* }}} */
82 /* Hack: the vfs code should not rely on this */
83 #define WITH_FULL_PATHS 1
85 int verbose = 1;
88 * Whether the Midnight Commander tries to provide more
89 * information about copy/move sizes and bytes transfered
90 * at the expense of some speed
92 int file_op_compute_totals = 1;
94 /* This is a hard link cache */
95 struct link {
96 struct link *next;
97 struct vfs_class *vfs;
98 dev_t dev;
99 ino_t ino;
100 short linkcount;
101 mode_t st_mode;
102 char name[1];
105 /* the hard link cache */
106 static struct link *linklist = NULL;
108 /* the files-to-be-erased list */
109 static struct link *erase_list;
112 * In copy_dir_dir we use two additional single linked lists: The first -
113 * variable name `parent_dirs' - holds information about already copied
114 * directories and is used to detect cyclic symbolic links.
115 * The second (`dest_dirs' below) holds information about just created
116 * target directories and is used to detect when an directory is copied
117 * into itself (we don't want to copy infinitly).
118 * Both lists don't use the linkcount and name structure members of struct
119 * link.
121 static struct link *dest_dirs = 0;
123 const char *op_names[3] = {
124 N_(" Copy "),
125 N_(" Move "),
126 N_(" Delete ")
129 /* }}} */
131 static int query_replace (FileOpContext * ctx, const char *destname,
132 struct stat *_s_stat, struct stat *_d_stat);
133 static int query_recursive (FileOpContext * ctx, const char *s);
134 static int do_file_error (const char *str);
135 static int erase_dir_iff_empty (FileOpContext *ctx, const char *s);
136 static int erase_file (FileOpContext *ctx, const char *s,
137 off_t *progress_count, double *progress_bytes,
138 int is_toplevel_file);
139 static int files_error (const char *format, const char *file1,
140 const char *file2);
143 enum CaseConvs { NO_CONV = 0, UP_CHAR = 1, LOW_CHAR = 2, UP_SECT =
144 4, LOW_SECT = 8 };
146 static char
147 convert_case (char c, enum CaseConvs *conversion)
149 if (*conversion & UP_CHAR) {
150 *conversion &= ~UP_CHAR;
151 return toupper ((unsigned char) c);
152 } else if (*conversion & LOW_CHAR) {
153 *conversion &= ~LOW_CHAR;
154 return tolower ((unsigned char) c);
155 } else if (*conversion & UP_SECT) {
156 return toupper ((unsigned char) c);
157 } else if (*conversion & LOW_SECT) {
158 return tolower ((unsigned char) c);
159 } else
160 return c;
163 static int transform_error = 0;
165 static const char *
166 do_transform_source (FileOpContext *ctx, const char *source)
168 size_t j, k, l, len;
169 const char *fnsource = x_basename (source);
170 int next_reg;
171 enum CaseConvs case_conv = NO_CONV;
172 static char fntarget[MC_MAXPATHLEN];
174 len = strlen (fnsource);
175 j = re_match (&ctx->rx, fnsource, len, 0, &ctx->regs);
176 if (j != len) {
177 transform_error = FILE_SKIP;
178 return NULL;
180 for (next_reg = 1, j = 0, k = 0; j < strlen (ctx->dest_mask); j++) {
181 switch (ctx->dest_mask[j]) {
182 case '\\':
183 if (mhl_shell_is_char_escaped (&ctx->dest_mask[j])){
184 fntarget[k++] = ctx->dest_mask[j++];
185 fntarget[k++] = ctx->dest_mask[j];
186 break;
187 } else {
188 j++;
189 if (!isdigit ((unsigned char) ctx->dest_mask[j])) {
190 /* Backslash followed by non-digit */
191 switch (ctx->dest_mask[j]) {
192 case 'U':
193 case_conv |= UP_SECT;
194 case_conv &= ~LOW_SECT;
195 break;
196 case 'u':
197 case_conv |= UP_CHAR;
198 break;
199 case 'L':
200 case_conv |= LOW_SECT;
201 case_conv &= ~UP_SECT;
202 break;
203 case 'l':
204 case_conv |= LOW_CHAR;
205 break;
206 case 'E':
207 case_conv = NO_CONV;
208 break;
209 default:
210 /* Backslash as quote mark */
211 fntarget[k++] =
212 convert_case (ctx->dest_mask[j], &case_conv);
214 break;
215 } else {
216 /* Backslash followed by digit */
217 next_reg = ctx->dest_mask[j] - '0';
218 /* Fall through */
222 case '*':
223 if (next_reg < 0 || next_reg >= RE_NREGS
224 || ctx->regs.start[next_reg] < 0) {
225 message (1, MSG_ERROR, _(" Invalid target mask "));
226 transform_error = FILE_ABORT;
227 return NULL;
229 for (l = (size_t) ctx->regs.start[next_reg];
230 l < (size_t) ctx->regs.end[next_reg]; l++)
231 fntarget[k++] = convert_case (fnsource[l], &case_conv);
232 next_reg++;
233 break;
235 default:
236 fntarget[k++] = convert_case (ctx->dest_mask[j], &case_conv);
237 break;
240 fntarget[k] = 0;
241 return fntarget;
244 static const char *
245 transform_source (FileOpContext *ctx, const char *source)
247 char *s = g_strdup (source);
248 char *q;
249 const char *p;
251 /* We remove \n from the filename since regex routines would use \n as an anchor */
252 /* this is just to be allowed to maniupulate file names with \n on it */
253 for (q = s; *q; q++) {
254 if (*q == '\n')
255 *q = ' ';
257 p = do_transform_source (ctx, s);
258 g_free (s);
259 return p;
262 static void
263 free_linklist (struct link **linklist)
265 struct link *lp, *lp2;
267 for (lp = *linklist; lp != NULL; lp = lp2) {
268 lp2 = lp->next;
269 g_free (lp);
271 *linklist = NULL;
274 static int
275 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
277 ino_t ino = sb->st_ino;
278 dev_t dev = sb->st_dev;
279 #ifdef USE_VFS
280 struct vfs_class *vfs = vfs_get_class (path);
281 #endif /* USE_VFS */
283 while (lp) {
284 #ifdef USE_VFS
285 if (lp->vfs == vfs)
286 #endif /* USE_VFS */
287 if (lp->ino == ino && lp->dev == dev)
288 return 1;
289 lp = lp->next;
291 return 0;
295 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
296 * and a hardlink was succesfully made
298 static int
299 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
301 struct link *lp;
302 struct vfs_class *my_vfs = vfs_get_class (src_name);
303 ino_t ino = pstat->st_ino;
304 dev_t dev = pstat->st_dev;
305 struct stat link_stat;
306 const char *p;
308 if (vfs_file_class_flags (src_name) & VFSF_NOLINKS)
309 return 0;
311 for (lp = linklist; lp != NULL; lp = lp->next)
312 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev) {
313 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
314 && link_stat.st_dev == dev
315 && vfs_get_class (lp->name) == my_vfs) {
316 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
317 was copied to */
318 if (vfs_get_class (dst_name) == vfs_get_class (p)) {
319 if (!mc_stat (p, &link_stat)) {
320 if (!mc_link (p, dst_name))
321 return 1;
325 message (1, MSG_ERROR, _(" Cannot make the hardlink "));
326 return 0;
328 lp = (struct link *) g_malloc (sizeof (struct link) + strlen (src_name)
329 + strlen (dst_name) + 1);
330 if (lp) {
331 char *lpdstname;
332 lp->vfs = my_vfs;
333 lp->ino = ino;
334 lp->dev = dev;
335 strcpy (lp->name, src_name);
336 lpdstname = lp->name + strlen(lp->name) + 1;
337 strcpy (lpdstname, dst_name);
338 lp->next = linklist;
339 linklist = lp;
341 return 0;
345 * Duplicate the contents of the symbolic link src_path in dst_path.
346 * Try to make a stable symlink if the option "stable symlink" was
347 * set in the file mask dialog.
348 * If dst_path is an existing symlink it will be deleted silently
349 * (upper levels take already care of existing files at dst_path).
351 static int
352 make_symlink (FileOpContext *ctx, const char *src_path, const char *dst_path)
354 char link_target[MC_MAXPATHLEN];
355 int len;
356 int return_status;
357 struct stat sb;
358 int dst_is_symlink;
360 if (mc_lstat (dst_path, &sb) == 0 && S_ISLNK (sb.st_mode))
361 dst_is_symlink = 1;
362 else
363 dst_is_symlink = 0;
365 retry_src_readlink:
366 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
367 if (len < 0) {
368 return_status =
369 file_error (_(" Cannot read source link \"%s\" \n %s "),
370 src_path);
371 if (return_status == FILE_RETRY)
372 goto retry_src_readlink;
373 return return_status;
375 link_target[len] = 0;
377 if (ctx->stable_symlinks)
378 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path)) {
379 message (1, MSG_ERROR,
380 _(" Cannot make stable symlinks across "
381 "non-local filesystems: \n\n"
382 " Option Stable Symlinks will be disabled "));
383 ctx->stable_symlinks = 0;
386 if (ctx->stable_symlinks && *link_target != PATH_SEP) {
387 char *p, *q, *s;
389 const char *r = strrchr (src_path, PATH_SEP);
391 if (r) {
392 p = g_strndup (src_path, r - src_path + 1);
393 if (*dst_path == PATH_SEP)
394 q = g_strdup (dst_path);
395 else
396 q = g_strconcat (p, dst_path, (char *) NULL);
397 s = strrchr (q, PATH_SEP);
398 if (s) {
399 s[1] = 0;
400 s = g_strconcat (p, link_target, (char *) NULL);
401 g_free (p);
402 g_strlcpy (link_target, s, sizeof (link_target));
403 g_free (s);
404 s = diff_two_paths (q, link_target);
405 if (s) {
406 g_strlcpy (link_target, s, sizeof (link_target));
407 g_free (s);
409 } else
410 g_free (p);
411 g_free (q);
414 retry_dst_symlink:
415 if (mc_symlink (link_target, dst_path) == 0)
416 /* Success */
417 return FILE_CONT;
419 * if dst_exists, it is obvious that this had failed.
420 * We can delete the old symlink and try again...
422 if (dst_is_symlink) {
423 if (!mc_unlink (dst_path))
424 if (mc_symlink (link_target, dst_path) == 0)
425 /* Success */
426 return FILE_CONT;
428 return_status =
429 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
430 dst_path);
431 if (return_status == FILE_RETRY)
432 goto retry_dst_symlink;
433 return return_status;
436 static int
437 progress_update_one (FileOpContext *ctx,
438 off_t *progress_count,
439 double *progress_bytes, off_t add, int is_toplevel_file)
441 int ret;
443 if (is_toplevel_file || ctx->progress_totals_computed) {
444 (*progress_count)++;
445 (*progress_bytes) += add;
448 /* Apply some heuristic here to not call the update stuff very often */
449 ret =
450 file_progress_show_count (ctx, *progress_count,
451 ctx->progress_count);
452 if (ret != FILE_CONT)
453 return ret;
454 ret =
455 file_progress_show_bytes (ctx, *progress_bytes,
456 ctx->progress_bytes);
458 return ret;
461 /* Status of the destination file */
462 enum {
463 DEST_NONE, /* Not created */
464 DEST_SHORT, /* Created, not fully copied */
465 DEST_FULL /* Created, fully copied */
469 copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path,
470 int ask_overwrite, off_t *progress_count,
471 double *progress_bytes, int is_toplevel_file)
473 uid_t src_uid = (uid_t) - 1;
474 gid_t src_gid = (gid_t) - 1;
476 char *buf = NULL;
477 int buf_size = BUF_8K;
478 int src_desc, dest_desc = -1;
479 int n_read, n_written;
480 mode_t src_mode = 0; /* The mode of the source file */
481 struct stat sb, sb2;
482 struct utimbuf utb;
483 int dst_exists = 0, appending = 0;
484 off_t n_read_total = 0, file_size = -1;
485 int return_status, temp_status;
486 struct timeval tv_transfer_start;
487 int dst_status = DEST_NONE; /* 1 if the file is not fully copied */
489 /* FIXME: We should not be using global variables! */
490 ctx->do_reget = 0;
491 return_status = FILE_RETRY;
493 if (file_progress_show_source (ctx, src_path) == FILE_ABORT ||
494 file_progress_show_target (ctx, dst_path) == FILE_ABORT)
495 return FILE_ABORT;
497 mc_refresh ();
499 while (mc_stat (dst_path, &sb2) == 0) {
500 if (S_ISDIR (sb2.st_mode)) {
501 return_status =
502 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
503 dst_path);
504 if (return_status == FILE_RETRY)
505 continue;
506 return return_status;
508 dst_exists = 1;
509 break;
512 while ((*ctx->stat_func) (src_path, &sb)) {
513 return_status =
514 file_error (_(" Cannot stat source file \"%s\" \n %s "),
515 src_path);
516 if (return_status != FILE_RETRY)
517 return return_status;
520 if (dst_exists) {
521 /* Destination already exists */
522 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino) {
523 message (1, MSG_ERROR,
524 _(" `%s' and `%s' are the same file "), src_path, dst_path);
525 do_refresh ();
526 return FILE_SKIP;
529 /* Should we replace destination? */
530 if (ask_overwrite) {
531 ctx->do_reget = 0;
532 return_status = query_replace (ctx, dst_path, &sb, &sb2);
533 if (return_status != FILE_CONT)
534 return return_status;
538 if (!ctx->do_append) {
539 /* Check the hardlinks */
540 if (!ctx->follow_links && sb.st_nlink > 1 &&
541 check_hardlinks (src_path, dst_path, &sb) == 1) {
542 /* We have made a hardlink - no more processing is necessary */
543 return FILE_CONT;
546 if (S_ISLNK (sb.st_mode)) {
547 int retval;
549 retval = make_symlink (ctx, src_path, dst_path);
550 return retval;
553 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
554 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) ||
555 S_ISSOCK (sb.st_mode)) {
556 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill,
557 sb.st_rdev) < 0) {
558 return_status = file_error (
559 _(" Cannot create special file \"%s\" \n %s "), dst_path);
560 if (return_status == FILE_RETRY)
561 continue;
562 return return_status;
564 /* Success */
566 while (ctx->preserve_uidgid
567 && mc_chown (dst_path, sb.st_uid, sb.st_gid)) {
568 temp_status = file_error (
569 _(" Cannot chown target file \"%s\" \n %s "), dst_path);
570 if (temp_status == FILE_RETRY)
571 continue;
572 return temp_status;
574 while (ctx->preserve &&
575 mc_chmod (dst_path, sb.st_mode & ctx->umask_kill)) {
576 temp_status = file_error (
577 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
578 if (temp_status == FILE_RETRY)
579 continue;
580 return temp_status;
582 return FILE_CONT;
586 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
588 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0) {
589 return_status = file_error (
590 _(" Cannot open source file \"%s\" \n %s "), src_path);
591 if (return_status == FILE_RETRY)
592 continue;
593 ctx->do_append = 0;
594 return return_status;
597 if (ctx->do_reget) {
598 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget) {
599 message (1, _("Warning"),
600 _(" Reget failed, about to overwrite file "));
601 ctx->do_reget = ctx->do_append = 0;
605 while (mc_fstat (src_desc, &sb)) {
606 return_status = file_error (
607 _(" Cannot fstat source file \"%s\" \n %s "), src_path);
608 if (return_status == FILE_RETRY)
609 continue;
610 ctx->do_append = 0;
611 goto ret;
613 src_mode = sb.st_mode;
614 src_uid = sb.st_uid;
615 src_gid = sb.st_gid;
616 utb.actime = sb.st_atime;
617 utb.modtime = sb.st_mtime;
618 file_size = sb.st_size;
620 /* Create the new regular file with small permissions initially,
621 do not create a security hole. FIXME: You have security hole
622 here, btw. Imagine copying to /tmp and symlink attack :-( */
624 while ((dest_desc = mc_open (dst_path, O_WRONLY | (ctx->do_append ?
625 O_APPEND : (O_CREAT | O_TRUNC)), 0600)) < 0) {
626 return_status = file_error (
627 _(" Cannot create target file \"%s\" \n %s "), dst_path);
628 if (return_status == FILE_RETRY)
629 continue;
630 ctx->do_append = 0;
631 goto ret;
633 dst_status = DEST_SHORT; /* file opened, but not fully copied */
635 appending = ctx->do_append;
636 ctx->do_append = 0;
638 /* Find out the optimal buffer size. */
639 while (mc_fstat (dest_desc, &sb)) {
640 return_status = file_error (
641 _(" Cannot fstat target file \"%s\" \n %s "), dst_path);
642 if (return_status == FILE_RETRY)
643 continue;
644 goto ret;
646 buf = g_malloc (buf_size);
648 ctx->eta_secs = 0.0;
649 ctx->bps = 0;
651 return_status = file_progress_show (ctx, 0, file_size);
653 mc_refresh ();
655 if (return_status != FILE_CONT)
656 goto ret;
659 struct timeval tv_current, tv_last_update, tv_last_input;
660 int secs, update_secs;
661 long dt;
662 const char *stalled_msg;
664 tv_last_update = tv_transfer_start;
666 for (;;) {
667 /* src_read */
668 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
669 n_read = -1;
670 else
671 while ((n_read = mc_read (src_desc, buf, buf_size)) < 0) {
672 return_status = file_error (
673 _(" Cannot read source file \"%s\" \n %s "), src_path);
674 if (return_status == FILE_RETRY)
675 continue;
676 goto ret;
678 if (n_read == 0)
679 break;
681 gettimeofday (&tv_current, NULL);
683 if (n_read > 0) {
684 char *t = buf;
685 n_read_total += n_read;
687 /* Windows NT ftp servers report that files have no
688 * permissions: -------, so if we happen to have actually
689 * read something, we should fix the permissions.
691 if (!(src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)))
692 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
693 gettimeofday (&tv_last_input, NULL);
695 /* dst_write */
696 while ((n_written =
697 mc_write (dest_desc, t, n_read)) < n_read) {
698 if (n_written > 0) {
699 n_read -= n_written;
700 t += n_written;
701 continue;
703 return_status =
704 file_error (_(" Cannot write target file \"%s\" \n %s "),
705 dst_path);
706 if (return_status != FILE_RETRY)
707 goto ret;
711 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
712 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
713 if (secs > 2) {
714 rotate_dash ();
715 tv_last_update = tv_current;
718 /* 2. Check for a stalled condition */
719 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
720 stalled_msg = "";
721 if (update_secs > 4) {
722 stalled_msg = _("(stalled)");
725 /* 3. Compute ETA */
726 if (secs > 2) {
727 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
729 if (n_read_total) {
730 ctx->eta_secs =
731 ((dt / (double) n_read_total) * file_size) - dt;
732 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
733 } else
734 ctx->eta_secs = 0.0;
737 /* 4. Compute BPS rate */
738 if (secs > 2) {
739 ctx->bps_time =
740 (tv_current.tv_sec - tv_transfer_start.tv_sec);
741 if (ctx->bps_time < 1)
742 ctx->bps_time = 1;
743 ctx->bps = n_read_total / ctx->bps_time;
746 file_progress_set_stalled_label (ctx, stalled_msg);
747 return_status = file_progress_show_bytes (ctx, *progress_bytes +
748 n_read_total + ctx->do_reget, ctx->progress_bytes);
749 if (return_status == FILE_CONT) {
750 return_status =
751 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size);
753 mc_refresh ();
754 if (return_status != FILE_CONT)
755 goto ret;
759 dst_status = DEST_FULL; /* copy successful, don't remove target file */
761 ret:
762 g_free (buf);
764 while (src_desc != -1 && mc_close (src_desc) < 0) {
765 temp_status = file_error (
766 _(" Cannot close source file \"%s\" \n %s "), src_path);
767 if (temp_status == FILE_RETRY)
768 continue;
769 if (temp_status == FILE_ABORT)
770 return_status = temp_status;
771 break;
774 while (dest_desc != -1 && mc_close (dest_desc) < 0) {
775 temp_status = file_error (
776 _(" Cannot close target file \"%s\" \n %s "), dst_path);
777 if (temp_status == FILE_RETRY)
778 continue;
779 return_status = temp_status;
780 break;
783 if (dst_status == DEST_SHORT) {
784 /* Remove short file */
785 int result;
786 result = query_dialog (_("Copy"),
787 _("Incomplete file was retrieved. Keep it?"),
788 D_ERROR, 2, _("&Delete"), _("&Keep"));
789 if (!result)
790 mc_unlink (dst_path);
791 } else if (dst_status == DEST_FULL) {
792 /* Copy has succeeded */
793 if (!appending && ctx->preserve_uidgid) {
794 while (mc_chown (dst_path, src_uid, src_gid)) {
795 temp_status = file_error (
796 _(" Cannot chown target file \"%s\" \n %s "), dst_path);
797 if (temp_status == FILE_RETRY)
798 continue;
799 return_status = temp_status;
800 break;
804 if (!appending && ctx->preserve) {
805 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill))) {
806 temp_status = file_error (
807 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
808 if (temp_status != FILE_RETRY) {
809 return_status = temp_status;
810 break;
813 mc_utime (dst_path, &utb);
817 if (return_status == FILE_CONT)
818 return_status =
819 progress_update_one (ctx, progress_count, progress_bytes,
820 file_size, is_toplevel_file);
822 return return_status;
826 * I think these copy_*_* functions should have a return type.
827 * anyway, this function *must* have two directories as arguments.
829 /* FIXME: This function needs to check the return values of the
830 function calls */
832 copy_dir_dir (FileOpContext *ctx, const char *s, const char *d, int toplevel,
833 int move_over, int delete, struct link *parent_dirs,
834 off_t *progress_count, double *progress_bytes)
836 struct dirent *next;
837 struct stat buf, cbuf;
838 DIR *reading;
839 char *path, *mdpath, *dest_file, *dest_dir;
840 int return_status = FILE_CONT;
841 struct utimbuf utb;
842 struct link *lp;
844 /* First get the mode of the source dir */
845 retry_src_stat:
846 if ((*ctx->stat_func) (s, &cbuf)) {
847 return_status =
848 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s);
849 if (return_status == FILE_RETRY)
850 goto retry_src_stat;
851 return return_status;
854 if (is_in_linklist (dest_dirs, s, &cbuf)) {
855 /* Don't copy a directory we created before (we don't want to copy
856 infinitely if a directory is copied into itself) */
857 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
858 return FILE_CONT;
861 /* Hmm, hardlink to directory??? - Norbert */
862 /* FIXME: In this step we should do something
863 in case the destination already exist */
864 /* Check the hardlinks */
865 if (ctx->preserve && cbuf.st_nlink > 1
866 && check_hardlinks (s, d, &cbuf) == 1) {
867 /* We have made a hardlink - no more processing is necessary */
868 return return_status;
871 if (!S_ISDIR (cbuf.st_mode)) {
872 return_status =
873 file_error (_(" Source \"%s\" is not a directory \n %s "), s);
874 if (return_status == FILE_RETRY)
875 goto retry_src_stat;
876 return return_status;
879 if (is_in_linklist (parent_dirs, s, &cbuf)) {
880 /* we found a cyclic symbolic link */
881 message (1, MSG_ERROR,
882 _(" Cannot copy cyclic symbolic link \n `%s' "), s);
883 return FILE_SKIP;
886 lp = g_new (struct link, 1);
887 lp->vfs = vfs_get_class (s);
888 lp->ino = cbuf.st_ino;
889 lp->dev = cbuf.st_dev;
890 lp->next = parent_dirs;
891 parent_dirs = lp;
893 retry_dst_stat:
894 /* Now, check if the dest dir exists, if not, create it. */
895 if (mc_stat (d, &buf)) {
896 /* Here the dir doesn't exist : make it ! */
898 if (move_over) {
899 if (mc_rename (s, d) == 0) {
900 g_free (parent_dirs);
901 return FILE_CONT;
904 dest_dir = g_strdup (d);
905 } else {
907 * If the destination directory exists, we want to copy the whole
908 * directory, but we only want this to happen once.
910 * Escape sequences added to the * to compiler warnings.
911 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
912 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
914 if (!S_ISDIR (buf.st_mode)) {
915 return_status = file_error(
916 _(" Destination \"%s\" must be a directory \n %s "), d);
917 if (return_status == FILE_RETRY)
918 goto retry_dst_stat;
919 g_free (parent_dirs);
920 return return_status;
922 /* Dive into subdir if exists */
923 if (toplevel && ctx->dive_into_subdirs) {
924 dest_dir = concat_dir_and_file (d, x_basename (s));
925 } else {
926 dest_dir = g_strdup (d);
927 goto dont_mkdir;
930 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU)) {
931 return_status = file_error (
932 _(" Cannot create target directory \"%s\" \n %s "), dest_dir);
933 if (return_status != FILE_RETRY)
934 goto ret;
937 lp = g_new (struct link, 1);
938 mc_stat (dest_dir, &buf);
939 lp->vfs = vfs_get_class (dest_dir);
940 lp->ino = buf.st_ino;
941 lp->dev = buf.st_dev;
942 lp->next = dest_dirs;
943 dest_dirs = lp;
945 if (ctx->preserve_uidgid) {
946 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid)) {
947 return_status = file_error (
948 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir);
949 if (return_status != FILE_RETRY)
950 goto ret;
954 dont_mkdir:
955 /* open the source dir for reading */
956 if ((reading = mc_opendir (s)) == 0) {
957 goto ret;
960 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT) {
962 * Now, we don't want '.' and '..' to be created / copied at any time
964 if (!strcmp (next->d_name, "."))
965 continue;
966 if (!strcmp (next->d_name, ".."))
967 continue;
969 /* get the filename and add it to the src directory */
970 path = concat_dir_and_file (s, next->d_name);
972 (*ctx->stat_func) (path, &buf);
973 if (S_ISDIR (buf.st_mode)) {
974 mdpath = concat_dir_and_file (dest_dir, next->d_name);
976 * From here, we just intend to recursively copy subdirs, not
977 * the double functionality of copying different when the target
978 * dir already exists. So, we give the recursive call the flag 0
979 * meaning no toplevel.
981 return_status = copy_dir_dir (ctx, path, mdpath, 0, 0, delete,
982 parent_dirs, progress_count, progress_bytes);
983 g_free (mdpath);
984 } else {
985 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
986 return_status = copy_file_file (ctx, path, dest_file, 1,
987 progress_count, progress_bytes, 0);
988 g_free (dest_file);
990 if (delete && return_status == FILE_CONT) {
991 if (ctx->erase_at_end) {
992 static struct link *tail;
993 lp = g_malloc (sizeof (struct link) + strlen (path));
994 strcpy (lp->name, path);
995 lp->st_mode = buf.st_mode;
996 lp->next = 0;
997 if (erase_list) {
998 tail->next = lp;
999 tail = lp;
1000 } else
1001 erase_list = tail = lp;
1002 } else {
1003 if (S_ISDIR (buf.st_mode)) {
1004 return_status = erase_dir_iff_empty (ctx, path);
1005 } else
1006 return_status = erase_file (ctx, path, 0, 0, 0);
1009 g_free (path);
1011 mc_closedir (reading);
1013 if (ctx->preserve) {
1014 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1015 utb.actime = cbuf.st_atime;
1016 utb.modtime = cbuf.st_mtime;
1017 mc_utime (dest_dir, &utb);
1020 ret:
1021 g_free (dest_dir);
1022 g_free (parent_dirs);
1023 return return_status;
1026 /* }}} */
1028 /* {{{ Move routines */
1030 static int
1031 move_file_file (FileOpContext *ctx, const char *s, const char *d,
1032 off_t *progress_count, double *progress_bytes)
1034 struct stat src_stats, dst_stats;
1035 int return_status = FILE_CONT;
1036 gboolean copy_done = FALSE;
1038 if (file_progress_show_source (ctx, s) == FILE_ABORT
1039 || file_progress_show_target (ctx, d) == FILE_ABORT)
1040 return FILE_ABORT;
1042 mc_refresh ();
1044 while (mc_lstat (s, &src_stats) != 0) {
1045 /* Source doesn't exist */
1046 return_status =
1047 file_error (_(" Cannot stat file \"%s\" \n %s "), s);
1048 if (return_status != FILE_RETRY)
1049 return return_status;
1052 if (mc_lstat (d, &dst_stats) == 0) {
1053 if (src_stats.st_dev == dst_stats.st_dev
1054 && src_stats.st_ino == dst_stats.st_ino) {
1055 int msize = COLS - 36;
1056 char st[MC_MAXPATHLEN];
1057 char dt[MC_MAXPATHLEN];
1059 if (msize < 0)
1060 msize = 40;
1061 msize /= 2;
1063 strcpy (st, path_trunc (s, msize));
1064 strcpy (dt, path_trunc (d, msize));
1065 message (1, MSG_ERROR,
1066 _(" `%s' and `%s' are the same file "), st, dt);
1067 do_refresh ();
1068 return FILE_SKIP;
1071 if (S_ISDIR (dst_stats.st_mode)) {
1072 message (1, MSG_ERROR,
1073 _(" Cannot overwrite directory `%s' "), d);
1074 do_refresh ();
1075 return FILE_SKIP;
1078 if (confirm_overwrite) {
1079 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1080 if (return_status != FILE_CONT)
1081 return return_status;
1083 /* Ok to overwrite */
1086 if (!ctx->do_append) {
1087 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks) {
1088 if ((return_status = make_symlink (ctx, s, d)) == FILE_CONT) {
1089 goto retry_src_remove;
1090 } else
1091 return return_status;
1094 if (mc_rename (s, d) == 0) {
1095 return progress_update_one (ctx, progress_count,
1096 progress_bytes,
1097 src_stats.st_size, 1);
1100 #if 0
1101 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1102 one nfs to the same, but on the server it is on two different
1103 filesystems. Then nfs returns EIO instead of EXDEV.
1104 Hope it will not hurt if we always in case of error try to copy/delete. */
1105 else
1106 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1108 if (errno != EXDEV) {
1109 return_status =
1110 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s,
1112 if (return_status == FILE_RETRY)
1113 goto retry_rename;
1114 return return_status;
1116 #endif
1118 /* Failed because filesystem boundary -> copy the file instead */
1119 return_status =
1120 copy_file_file (ctx, s, d, 0, progress_count, progress_bytes, 1);
1121 if (return_status != FILE_CONT)
1122 return return_status;
1124 copy_done = TRUE;
1126 if ((return_status =
1127 file_progress_show_source (ctx, NULL)) != FILE_CONT
1128 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1129 return return_status;
1131 mc_refresh ();
1133 retry_src_remove:
1134 if (mc_unlink (s)) {
1135 return_status =
1136 file_error (_(" Cannot remove file \"%s\" \n %s "), s);
1137 if (return_status == FILE_RETRY)
1138 goto retry_src_remove;
1139 return return_status;
1142 if (!copy_done) {
1143 return_status = progress_update_one (ctx,
1144 progress_count,
1145 progress_bytes,
1146 src_stats.st_size, 1);
1149 return return_status;
1153 move_dir_dir (FileOpContext *ctx, const char *s, const char *d,
1154 off_t *progress_count, double *progress_bytes)
1156 struct stat sbuf, dbuf, destbuf;
1157 struct link *lp;
1158 char *destdir;
1159 int return_status;
1160 int move_over = 0;
1162 if (file_progress_show_source (ctx, s) == FILE_ABORT ||
1163 file_progress_show_target (ctx, d) == FILE_ABORT)
1164 return FILE_ABORT;
1166 mc_refresh ();
1168 mc_stat (s, &sbuf);
1169 if (mc_stat (d, &dbuf))
1170 destdir = g_strdup (d); /* destination doesn't exist */
1171 else if (!ctx->dive_into_subdirs) {
1172 destdir = g_strdup (d);
1173 move_over = 1;
1174 } else
1175 destdir = concat_dir_and_file (d, x_basename (s));
1177 if (sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino) {
1178 int msize = COLS - 36;
1179 char st[MC_MAXPATHLEN];
1180 char dt[MC_MAXPATHLEN];
1182 if (msize < 0)
1183 msize = 40;
1184 msize /= 2;
1186 strcpy (st, path_trunc (s, msize));
1187 strcpy (dt, path_trunc (d, msize));
1188 message (1, MSG_ERROR,
1189 _(" `%s' and `%s' are the same directory "), st, dt);
1190 do_refresh ();
1191 return FILE_SKIP;
1194 /* Check if the user inputted an existing dir */
1195 retry_dst_stat:
1196 if (!mc_stat (destdir, &destbuf)) {
1197 if (move_over) {
1198 return_status = copy_dir_dir (ctx, s, destdir, 0, 1, 1, 0,
1199 progress_count, progress_bytes);
1201 if (return_status != FILE_CONT)
1202 goto ret;
1203 goto oktoret;
1204 } else {
1205 if (S_ISDIR (destbuf.st_mode))
1206 return_status =
1207 file_error (_
1208 (" Cannot overwrite directory \"%s\" %s "),
1209 destdir);
1210 else
1211 return_status =
1212 file_error (_(" Cannot overwrite file \"%s\" %s "),
1213 destdir);
1214 if (return_status == FILE_RETRY)
1215 goto retry_dst_stat;
1217 g_free (destdir);
1218 return return_status;
1221 retry_rename:
1222 if (mc_rename (s, destdir) == 0) {
1223 return_status = FILE_CONT;
1224 goto ret;
1227 if (errno != EXDEV) {
1228 return_status =
1229 files_error (_
1230 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1231 s, d);
1232 if (return_status == FILE_RETRY)
1233 goto retry_rename;
1234 goto ret;
1236 /* Failed because of filesystem boundary -> copy dir instead */
1237 return_status =
1238 copy_dir_dir (ctx, s, destdir, 0, 0, 1, 0, progress_count,
1239 progress_bytes);
1241 if (return_status != FILE_CONT)
1242 goto ret;
1243 oktoret:
1244 if ((return_status =
1245 file_progress_show_source (ctx, NULL)) != FILE_CONT
1246 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1247 goto ret;
1249 mc_refresh ();
1250 if (ctx->erase_at_end) {
1251 for (; erase_list && return_status != FILE_ABORT;) {
1252 if (S_ISDIR (erase_list->st_mode)) {
1253 return_status =
1254 erase_dir_iff_empty (ctx, erase_list->name);
1255 } else
1256 return_status =
1257 erase_file (ctx, erase_list->name, 0, 0, 0);
1258 lp = erase_list;
1259 erase_list = erase_list->next;
1260 g_free (lp);
1263 erase_dir_iff_empty (ctx, s);
1265 ret:
1266 g_free (destdir);
1267 while (erase_list) {
1268 lp = erase_list;
1269 erase_list = erase_list->next;
1270 g_free (lp);
1272 return return_status;
1275 /* }}} */
1277 /* {{{ Erase routines */
1278 /* Don't update progress status if progress_count==NULL */
1279 static int
1280 erase_file (FileOpContext *ctx, const char *s, off_t *progress_count,
1281 double *progress_bytes, int is_toplevel_file)
1283 int return_status;
1284 struct stat buf;
1286 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1287 return FILE_ABORT;
1288 mc_refresh ();
1290 if (progress_count && mc_lstat (s, &buf)) {
1291 /* ignore, most likely the mc_unlink fails, too */
1292 buf.st_size = 0;
1295 while (mc_unlink (s)) {
1296 return_status =
1297 file_error (_(" Cannot delete file \"%s\" \n %s "), s);
1298 if (return_status != FILE_RETRY)
1299 return return_status;
1302 if (progress_count)
1303 return progress_update_one (ctx, progress_count, progress_bytes,
1304 buf.st_size, is_toplevel_file);
1305 else
1306 return FILE_CONT;
1309 static int
1310 recursive_erase (FileOpContext *ctx, const char *s, off_t *progress_count,
1311 double *progress_bytes)
1313 struct dirent *next;
1314 struct stat buf;
1315 DIR *reading;
1316 char *path;
1317 int return_status = FILE_CONT;
1319 if (!strcmp (s, ".."))
1320 return 1;
1322 reading = mc_opendir (s);
1324 if (!reading)
1325 return 1;
1327 while ((next = mc_readdir (reading)) && return_status == FILE_CONT) {
1328 if (!strcmp (next->d_name, "."))
1329 continue;
1330 if (!strcmp (next->d_name, ".."))
1331 continue;
1332 path = concat_dir_and_file (s, next->d_name);
1333 if (mc_lstat (path, &buf)) {
1334 g_free (path);
1335 mc_closedir (reading);
1336 return 1;
1338 if (S_ISDIR (buf.st_mode))
1339 return_status =
1340 (recursive_erase
1341 (ctx, path, progress_count, progress_bytes)
1342 != FILE_CONT);
1343 else
1344 return_status =
1345 erase_file (ctx, path, progress_count, progress_bytes, 0);
1346 g_free (path);
1348 mc_closedir (reading);
1349 if (return_status != FILE_CONT)
1350 return return_status;
1351 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1352 return FILE_ABORT;
1353 mc_refresh ();
1355 while (my_rmdir (s)) {
1356 return_status =
1357 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1358 if (return_status != FILE_RETRY)
1359 return return_status;
1362 return FILE_CONT;
1365 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1366 in the directory path points to, 0 else. */
1367 static int
1368 check_dir_is_empty (const char *path)
1370 DIR *dir;
1371 struct dirent *d;
1372 int i;
1374 dir = mc_opendir (path);
1375 if (!dir)
1376 return -1;
1378 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir)) {
1379 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1380 (d->d_name[1] == '.'
1381 && d->d_name[2] == '\0')))
1382 continue; /* "." or ".." */
1383 i = 0;
1384 break;
1387 mc_closedir (dir);
1388 return i;
1392 erase_dir (FileOpContext *ctx, const char *s, off_t *progress_count,
1393 double *progress_bytes)
1395 int error;
1397 if (strcmp (s, "..") == 0)
1398 return FILE_SKIP;
1400 if (strcmp (s, ".") == 0)
1401 return FILE_SKIP;
1403 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1404 return FILE_ABORT;
1405 mc_refresh ();
1407 /* The old way to detect a non empty directory was:
1408 error = my_rmdir (s);
1409 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1410 For the linux user space nfs server (nfs-server-2.2beta29-2)
1411 we would have to check also for EIO. I hope the new way is
1412 fool proof. (Norbert)
1414 error = check_dir_is_empty (s);
1415 if (error == 0) { /* not empty */
1416 error = query_recursive (ctx, s);
1417 if (error == FILE_CONT)
1418 return recursive_erase (ctx, s, progress_count,
1419 progress_bytes);
1420 else
1421 return error;
1424 while (my_rmdir (s) == -1) {
1425 error =
1426 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1427 if (error != FILE_RETRY)
1428 return error;
1431 return FILE_CONT;
1434 static int
1435 erase_dir_iff_empty (FileOpContext *ctx, const char *s)
1437 int error;
1439 if (strcmp (s, "..") == 0)
1440 return FILE_SKIP;
1442 if (strcmp (s, ".") == 0)
1443 return FILE_SKIP;
1445 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1446 return FILE_ABORT;
1447 mc_refresh ();
1449 if (1 != check_dir_is_empty (s)) /* not empty or error */
1450 return FILE_CONT;
1452 while (my_rmdir (s)) {
1453 error =
1454 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1455 if (error != FILE_RETRY)
1456 return error;
1459 return FILE_CONT;
1462 /* }}} */
1464 /* {{{ Panel operate routines */
1467 * Return currently selected entry name or the name of the first marked
1468 * entry if there is one.
1470 static char *
1471 panel_get_file (WPanel *panel, struct stat *stat_buf)
1473 int i;
1475 if (get_current_type () == view_tree) {
1476 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
1477 char *tree_name = tree_selected_name (tree);
1479 mc_stat (tree_name, stat_buf);
1480 return tree_name;
1483 if (panel->marked) {
1484 for (i = 0; i < panel->count; i++)
1485 if (panel->dir.list[i].f.marked) {
1486 *stat_buf = panel->dir.list[i].st;
1487 return panel->dir.list[i].fname;
1489 } else {
1490 *stat_buf = panel->dir.list[panel->selected].st;
1491 return panel->dir.list[panel->selected].fname;
1493 g_assert_not_reached ();
1494 return NULL;
1498 * compute_dir_size:
1500 * Computes the number of bytes used by the files in a directory
1502 void
1503 compute_dir_size (const char *dirname, off_t *ret_marked, double *ret_total)
1505 DIR *dir;
1506 struct dirent *dirent;
1508 dir = mc_opendir (dirname);
1510 if (!dir)
1511 return;
1513 while ((dirent = mc_readdir (dir)) != NULL) {
1514 struct stat s;
1515 char *fullname;
1516 int res;
1518 if (strcmp (dirent->d_name, ".") == 0)
1519 continue;
1520 if (strcmp (dirent->d_name, "..") == 0)
1521 continue;
1523 fullname = concat_dir_and_file (dirname, dirent->d_name);
1525 res = mc_lstat (fullname, &s);
1527 if (res != 0) {
1528 g_free (fullname);
1529 continue;
1532 if (S_ISDIR (s.st_mode)) {
1533 off_t subdir_count = 0;
1534 double subdir_bytes = 0;
1536 compute_dir_size (fullname, &subdir_count, &subdir_bytes);
1538 *ret_marked += subdir_count;
1539 *ret_total += subdir_bytes;
1540 } else {
1541 (*ret_marked)++;
1542 *ret_total += s.st_size;
1544 g_free (fullname);
1547 mc_closedir (dir);
1551 * panel_compute_totals:
1553 * compute the number of files and the number of bytes
1554 * used up by the whole selection, recursing directories
1555 * as required. In addition, it checks to see if it will
1556 * overwrite any files by doing the copy.
1558 static void
1559 panel_compute_totals (WPanel *panel, off_t *ret_marked, double *ret_total)
1561 int i;
1563 *ret_marked = 0;
1564 *ret_total = 0.0;
1566 for (i = 0; i < panel->count; i++) {
1567 struct stat *s;
1569 if (!panel->dir.list[i].f.marked)
1570 continue;
1572 s = &panel->dir.list[i].st;
1574 if (S_ISDIR (s->st_mode)) {
1575 char *dir_name;
1576 off_t subdir_count = 0;
1577 double subdir_bytes = 0;
1579 dir_name =
1580 concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1581 compute_dir_size (dir_name, &subdir_count, &subdir_bytes);
1583 *ret_marked += subdir_count;
1584 *ret_total += subdir_bytes;
1585 g_free (dir_name);
1586 } else {
1587 (*ret_marked)++;
1588 *ret_total += s->st_size;
1594 * This array introduced to avoid translation problems. The former (op_names)
1595 * is assumed to be nouns, suitable in dialog box titles; this one should
1596 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1597 * Notice first symbol - it is to fool gettext and force these strings to
1598 * be different for it. First symbol is skipped while building a prompt.
1599 * (I don't use spaces around the words, because someday they could be
1600 * dropped, when widgets get smarter)
1602 static const char *op_names1[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1603 #define FMD_XLEN 64
1605 int fmd_xlen = FMD_XLEN;
1608 * These are formats for building a prompt. Parts encoded as follows:
1609 * %o - operation from op_names1
1610 * %f - file/files or files/directories, as appropriate
1611 * %m - "with source mask" or question mark for delete
1612 * %s - source name (truncated)
1613 * %d - number of marked files
1614 * %e - "to:" or question mark for delete
1616 * xgettext:no-c-format */
1617 static const char *one_format = N_("%o %f \"%s\"%m");
1618 /* xgettext:no-c-format */
1619 static const char *many_format = N_("%o %d %f%m");
1620 static const char *prompt_parts[] = {
1621 N_("file"), N_("files"), N_("directory"), N_("directories"),
1622 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1626 * Generate user prompt for panel operation.
1627 * single_source is the name if the source entry or NULL for multiple
1628 * entries.
1629 * src_stat is only used when single_source is not NULL.
1631 static void
1632 panel_operate_generate_prompt (const WPanel *panel, const int operation,
1633 const char *single_source,
1634 const struct stat *src_stat)
1636 register const char *sp, *cp;
1637 register int i;
1638 char format_string[BUF_MEDIUM];
1639 char *dp = format_string;
1641 #ifdef ENABLE_NLS
1642 static int i18n_flag = 0;
1643 if (!i18n_flag) {
1644 fmd_init_i18n (FALSE); /* to get proper fmd_xlen */
1646 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1647 op_names1[i] = _(op_names1[i]);
1649 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1650 prompt_parts[i] = _(prompt_parts[i]);
1652 one_format = _(one_format);
1653 many_format = _(many_format);
1654 i18n_flag = 1;
1656 #endif /* ENABLE_NLS */
1658 sp = single_source ? one_format : many_format;
1660 while (*sp) {
1661 switch (*sp) {
1662 case '%':
1663 cp = NULL;
1664 switch (sp[1]) {
1665 case 'o':
1666 cp = op_names1[operation] + 1;
1667 break;
1668 case 'm':
1669 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1670 break;
1671 case 'e':
1672 cp = operation == OP_DELETE ? "?" : prompt_parts[6];
1673 break;
1674 case 'f':
1675 if (single_source) {
1676 cp = S_ISDIR (src_stat->
1677 st_mode) ? prompt_parts[2] :
1678 prompt_parts[0];
1679 } else {
1680 cp = (panel->marked == panel->dirs_marked)
1681 ? prompt_parts[3]
1682 : (panel->dirs_marked ? prompt_parts[4]
1683 : prompt_parts[1]);
1685 break;
1686 default:
1687 *dp++ = *sp++;
1689 if (cp) {
1690 sp += 2;
1691 while (*cp)
1692 *dp++ = *cp++;
1694 break;
1695 default:
1696 *dp++ = *sp++;
1699 *dp = '\0';
1701 if (single_source) {
1702 i = fmd_xlen - strlen (format_string) - 4;
1703 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1704 name_trunc (single_source, i));
1705 } else {
1706 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1707 panel->marked);
1708 i = strlen (cmd_buf) + 6 - fmd_xlen;
1709 if (i > 0) {
1710 fmd_xlen += i;
1711 fmd_init_i18n (TRUE); /* to recalculate positions of child widgets */
1717 * panel_operate:
1719 * Performs one of the operations on the selection on the source_panel
1720 * (copy, delete, move).
1722 * Returns 1 if did change the directory
1723 * structure, Returns 0 if user aborted
1725 * force_single forces operation on the current entry and affects
1726 * default destination. Current filename is used as default.
1729 panel_operate (void *source_panel, FileOperation operation,
1730 int force_single)
1732 WPanel *panel = source_panel;
1733 #ifdef WITH_FULL_PATHS
1734 char *source_with_path = NULL;
1735 #else
1736 # define source_with_path source
1737 #endif /* !WITH_FULL_PATHS */
1738 char *source = NULL;
1739 char *dest = NULL;
1740 const char *temp = NULL;
1741 char *save_cwd = NULL, *save_dest = NULL;
1742 int single_entry = (get_current_type () == view_tree)
1743 || (panel->marked <= 1) || force_single;
1744 struct stat src_stat, dst_stat;
1745 int i, value;
1746 FileOpContext *ctx;
1748 off_t count = 0;
1749 double bytes = 0;
1751 int dst_result;
1752 int do_bg = 0; /* do background operation? */
1754 free_linklist (&linklist);
1755 free_linklist (&dest_dirs);
1757 if (single_entry) {
1758 if (force_single) {
1759 source = selection (panel)->fname;
1760 src_stat = selection (panel)->st;
1761 } else {
1762 source = panel_get_file (panel, &src_stat);
1765 if (!strcmp (source, "..")) {
1766 message (1, MSG_ERROR, _(" Cannot operate on \"..\"! "));
1767 return 0;
1771 /* Generate confirmation prompt */
1772 panel_operate_generate_prompt (panel, operation, source, &src_stat);
1774 ctx = file_op_context_new (operation);
1776 /* Show confirmation dialog */
1777 if (operation == OP_DELETE && confirm_delete) {
1778 if (safe_delete)
1779 query_set_sel (1);
1781 i = query_dialog (_(op_names[operation]), cmd_buf, D_ERROR, 2,
1782 _("&Yes"), _("&No"));
1784 if (i != 0) {
1785 file_op_context_destroy (ctx);
1786 return 0;
1788 } else if (operation != OP_DELETE) {
1789 char *dest_dir;
1791 /* Forced single operations default to the original name */
1792 if (force_single)
1793 dest_dir = source;
1794 else if (get_other_type () == view_listing)
1795 dest_dir = other_panel->cwd;
1796 else
1797 dest_dir = panel->cwd;
1799 dest =
1800 file_mask_dialog (ctx, operation, cmd_buf, dest_dir,
1801 single_entry, &do_bg);
1802 if (!dest) {
1803 file_op_context_destroy (ctx);
1804 return 0;
1806 if (!*dest) {
1807 file_op_context_destroy (ctx);
1808 g_free (dest);
1809 return 0;
1812 #ifdef WITH_BACKGROUND
1813 /* Did the user select to do a background operation? */
1814 if (do_bg) {
1815 int v;
1817 v = do_background (ctx,
1818 g_strconcat (op_names[operation], ": ",
1819 panel->cwd, NULL));
1820 if (v == -1) {
1821 message (1, MSG_ERROR,
1822 _(" Sorry, I could not put the job in background "));
1825 /* If we are the parent */
1826 if (v == 1) {
1827 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
1828 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
1829 /* file_op_context_destroy (ctx); */
1830 return 0;
1833 #endif /* WITH_BACKGROUND */
1835 /* Initialize things */
1836 /* We do not want to trash cache every time file is
1837 created/touched. However, this will make our cache contain
1838 invalid data. */
1839 if (dest) {
1840 if (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1))
1841 save_dest = g_strdup (dest);
1843 if (panel->cwd) {
1844 if (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1))
1845 save_cwd = g_strdup (panel->cwd);
1848 /* Now, let's do the job */
1850 if (do_bg)
1851 ctx->ui = NULL;
1852 else
1853 file_op_context_create_ui (ctx, 1);
1855 /* This code is only called by the tree and panel code */
1856 if (single_entry) {
1857 /* We now have ETA in all cases */
1859 /* One file: FIXME mc_chdir will take user out of any vfs */
1860 if (operation != OP_COPY && get_current_type () == view_tree)
1861 mc_chdir (PATH_SEP_STR);
1863 /* The source and src_stat variables have been initialized before */
1864 #ifdef WITH_FULL_PATHS
1865 source_with_path = concat_dir_and_file (panel->cwd, source);
1866 #endif /* WITH_FULL_PATHS */
1868 if (operation == OP_DELETE) {
1869 if (S_ISDIR (src_stat.st_mode))
1870 value = erase_dir (ctx, source_with_path, &count, &bytes);
1871 else
1872 value =
1873 erase_file (ctx, source_with_path, &count, &bytes, 1);
1874 } else {
1875 temp = transform_source (ctx, source_with_path);
1877 if (temp == NULL) {
1878 value = transform_error;
1879 } else {
1880 char *temp2 = concat_dir_and_file (dest, temp);
1881 g_free (dest);
1882 dest = temp2;
1883 temp = NULL;
1885 switch (operation) {
1886 case OP_COPY:
1888 * we use file_mask_op_follow_links only with OP_COPY,
1890 (*ctx->stat_func) (source_with_path, &src_stat);
1892 if (S_ISDIR (src_stat.st_mode))
1893 value =
1894 copy_dir_dir (ctx, source_with_path, dest, 1,
1895 0, 0, 0, &count, &bytes);
1896 else
1897 value =
1898 copy_file_file (ctx, source_with_path, dest, 1,
1899 &count, &bytes, 1);
1900 break;
1902 case OP_MOVE:
1903 if (S_ISDIR (src_stat.st_mode))
1904 value =
1905 move_dir_dir (ctx, source_with_path, dest,
1906 &count, &bytes);
1907 else
1908 value =
1909 move_file_file (ctx, source_with_path, dest,
1910 &count, &bytes);
1911 break;
1913 default:
1914 /* Unknown file operation */
1915 abort ();
1918 } /* Copy or move operation */
1920 if ((value == FILE_CONT) && !force_single)
1921 unmark_files (panel);
1922 } else {
1923 /* Many files */
1924 /* Check destination for copy or move operation */
1925 if (operation != OP_DELETE) {
1926 retry_many_dst_stat:
1927 dst_result = mc_stat (dest, &dst_stat);
1928 if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)) {
1929 if (file_error
1930 (_(" Destination \"%s\" must be a directory \n %s "),
1931 dest) == FILE_RETRY)
1932 goto retry_many_dst_stat;
1933 goto clean_up;
1937 /* Initialize variables for progress bars */
1938 if (operation != OP_MOVE && verbose && file_op_compute_totals) {
1939 panel_compute_totals (panel, &ctx->progress_count,
1940 &ctx->progress_bytes);
1941 ctx->progress_totals_computed = 1;
1942 } else {
1943 ctx->progress_totals_computed = 0;
1944 ctx->progress_count = panel->marked;
1945 ctx->progress_bytes = panel->total;
1948 /* Loop for every file, perform the actual copy operation */
1949 for (i = 0; i < panel->count; i++) {
1950 if (!panel->dir.list[i].f.marked)
1951 continue; /* Skip the unmarked ones */
1953 source = panel->dir.list[i].fname;
1954 src_stat = panel->dir.list[i].st;
1956 #ifdef WITH_FULL_PATHS
1957 g_free (source_with_path);
1958 source_with_path = concat_dir_and_file (panel->cwd, source);
1959 #endif /* WITH_FULL_PATHS */
1961 if (operation == OP_DELETE) {
1962 if (S_ISDIR (src_stat.st_mode))
1963 value =
1964 erase_dir (ctx, source_with_path, &count, &bytes);
1965 else
1966 value =
1967 erase_file (ctx, source_with_path, &count, &bytes,
1969 } else {
1970 temp = transform_source (ctx, source_with_path);
1971 if (temp == NULL)
1972 value = transform_error;
1973 else {
1974 char *temp2 = concat_dir_and_file (dest, temp);
1976 source_with_path = mhl_shell_unescape_buf(source_with_path);
1977 temp2 = mhl_shell_unescape_buf(temp2);
1979 switch (operation) {
1980 case OP_COPY:
1982 * we use file_mask_op_follow_links only with OP_COPY
1984 (*ctx->stat_func) (source_with_path, &src_stat);
1985 if (S_ISDIR (src_stat.st_mode))
1986 value =
1987 copy_dir_dir (ctx, source_with_path, temp2,
1988 1, 0, 0, 0, &count, &bytes);
1989 else
1990 value =
1991 copy_file_file (ctx, source_with_path,
1992 temp2, 1, &count, &bytes,
1994 free_linklist (&dest_dirs);
1995 break;
1997 case OP_MOVE:
1998 if (S_ISDIR (src_stat.st_mode))
1999 value =
2000 move_dir_dir (ctx, source_with_path, temp2,
2001 &count, &bytes);
2002 else
2003 value =
2004 move_file_file (ctx, source_with_path,
2005 temp2, &count, &bytes);
2006 break;
2008 default:
2009 /* Unknown file operation */
2010 abort ();
2012 g_free (temp2);
2014 } /* Copy or move operation */
2016 if (value == FILE_ABORT)
2017 goto clean_up;
2019 if (value == FILE_CONT)
2020 do_file_mark (panel, i, 0);
2022 if (file_progress_show_count (ctx, count, ctx->progress_count)
2023 == FILE_ABORT)
2024 goto clean_up;
2026 if (verbose
2027 && file_progress_show_bytes (ctx, bytes,
2028 ctx->progress_bytes) ==
2029 FILE_ABORT)
2030 goto clean_up;
2032 if (operation != OP_DELETE && verbose
2033 && file_progress_show (ctx, 0, 0) == FILE_ABORT)
2034 goto clean_up;
2036 mc_refresh ();
2037 } /* Loop for every file */
2038 } /* Many entries */
2039 clean_up:
2040 /* Clean up */
2042 if (save_cwd) {
2043 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2044 g_free (save_cwd);
2046 if (save_dest) {
2047 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2048 g_free (save_dest);
2051 free_linklist (&linklist);
2052 free_linklist (&dest_dirs);
2053 #ifdef WITH_FULL_PATHS
2054 g_free (source_with_path);
2055 #endif /* WITH_FULL_PATHS */
2056 g_free (dest);
2057 g_free (ctx->dest_mask);
2058 ctx->dest_mask = NULL;
2059 #ifdef WITH_BACKGROUND
2060 /* Let our parent know we are saying bye bye */
2061 if (we_are_background) {
2062 vfs_shut ();
2063 _exit (0);
2065 #endif /* WITH_BACKGROUND */
2067 file_op_context_destroy (ctx);
2068 return 1;
2071 /* }}} */
2073 /* {{{ Query/status report routines */
2075 static int
2076 real_do_file_error (enum OperationMode mode, const char *error)
2078 int result;
2079 const char *msg;
2081 msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
2082 result =
2083 query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"),
2084 _("&Abort"));
2086 switch (result) {
2087 case 0:
2088 do_refresh ();
2089 return FILE_SKIP;
2091 case 1:
2092 do_refresh ();
2093 return FILE_RETRY;
2095 case 2:
2096 default:
2097 return FILE_ABORT;
2101 /* Report error with one file */
2103 file_error (const char *format, const char *file)
2105 g_snprintf (cmd_buf, sizeof (cmd_buf), format,
2106 path_trunc (file, 30), unix_error_string (errno));
2108 return do_file_error (cmd_buf);
2111 /* Report error with two files */
2112 static int
2113 files_error (const char *format, const char *file1, const char *file2)
2115 char nfile1[16];
2116 char nfile2[16];
2118 strcpy (nfile1, path_trunc (file1, 15));
2119 strcpy (nfile2, path_trunc (file2, 15));
2121 g_snprintf (cmd_buf, sizeof (cmd_buf), format, nfile1, nfile2,
2122 unix_error_string (errno));
2124 return do_file_error (cmd_buf);
2127 static int
2128 real_query_recursive (FileOpContext *ctx, enum OperationMode mode, const char *s)
2130 gchar *text;
2132 if (ctx->recursive_result < RECURSIVE_ALWAYS) {
2133 const char *msg =
2134 mode ==
2135 Foreground ?
2136 _("\n Directory not empty. \n"
2137 " Delete it recursively? ")
2138 : _("\n Background process: Directory not empty \n"
2139 " Delete it recursively? ");
2140 text = g_strconcat (_(" Delete: "), path_trunc (s, 30), " ", (char *) NULL);
2142 if (safe_delete)
2143 query_set_sel (1);
2144 ctx->recursive_result = query_dialog (text, msg, D_ERROR, 5,
2145 _("&Yes"), _("&No"),
2146 _("A&ll"), _("Non&e"),
2147 _("&Abort"));
2149 if (ctx->recursive_result != RECURSIVE_ABORT)
2150 do_refresh ();
2151 g_free (text);
2154 switch (ctx->recursive_result) {
2155 case RECURSIVE_YES:
2156 case RECURSIVE_ALWAYS:
2157 return FILE_CONT;
2159 case RECURSIVE_NO:
2160 case RECURSIVE_NEVER:
2161 return FILE_SKIP;
2163 case RECURSIVE_ABORT:
2165 default:
2166 return FILE_ABORT;
2170 #ifdef WITH_BACKGROUND
2171 static int
2172 do_file_error (const char *str)
2174 if (we_are_background)
2175 return parent_call (real_do_file_error, NULL, 1, strlen (str),
2176 str);
2177 else
2178 return real_do_file_error (Foreground, str);
2181 static int
2182 query_recursive (FileOpContext *ctx, const char *s)
2184 if (we_are_background)
2185 return parent_call (real_query_recursive, ctx, 1, strlen (s), s);
2186 else
2187 return real_query_recursive (ctx, Foreground, s);
2190 static int
2191 query_replace (FileOpContext *ctx, const char *destname, struct stat *_s_stat,
2192 struct stat *_d_stat)
2194 if (we_are_background)
2195 return parent_call ((void *) file_progress_real_query_replace,
2196 ctx,
2198 strlen (destname), destname,
2199 sizeof (struct stat), _s_stat,
2200 sizeof (struct stat), _d_stat);
2201 else
2202 return file_progress_real_query_replace (ctx, Foreground, destname,
2203 _s_stat, _d_stat);
2206 #else
2207 static int
2208 do_file_error (const char *str)
2210 return real_do_file_error (Foreground, str);
2213 static int
2214 query_recursive (FileOpContext *ctx, const char *s)
2216 return real_query_recursive (ctx, Foreground, s);
2219 static int
2220 query_replace (FileOpContext *ctx, const char *destname, struct stat *_s_stat,
2221 struct stat *_d_stat)
2223 return file_progress_real_query_replace (ctx, Foreground, destname,
2224 _s_stat, _d_stat);
2227 #endif /* !WITH_BACKGROUND */
2230 Cause emacs to enter folding mode for this file:
2231 Local variables:
2232 end: