Add $ and ` for escaping and reorder it according to the ascii values
[midnight-commander.git] / src / file.c
blob8936e68980450d047d6ad60b3516aff9c11045a4
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 "global.h"
54 #include "tty.h"
55 #include "eregex.h"
56 #include "setup.h"
57 #include "color.h"
58 #include "win.h"
59 #include "dialog.h"
60 #include "widget.h"
61 #include "main.h" /* cmd_buf */
62 #include "layout.h"
63 #include "widget.h"
64 #include "wtools.h"
65 #include "background.h" /* we_are_background */
66 #include "util.h"
68 /* Needed for current_panel, other_panel and WTree */
69 #include "dir.h"
70 #include "panel.h"
71 #include "file.h"
72 #include "filegui.h"
73 #include "tree.h"
74 #include "key.h"
75 #include "../vfs/vfs-impl.h"
77 /* }}} */
79 /* Hack: the vfs code should not rely on this */
80 #define WITH_FULL_PATHS 1
82 int verbose = 1;
85 * Whether the Midnight Commander tries to provide more
86 * information about copy/move sizes and bytes transfered
87 * at the expense of some speed
89 int file_op_compute_totals = 1;
91 /* This is a hard link cache */
92 struct link {
93 struct link *next;
94 struct vfs_class *vfs;
95 dev_t dev;
96 ino_t ino;
97 short linkcount;
98 mode_t st_mode;
99 char name[1];
102 /* the hard link cache */
103 static struct link *linklist = NULL;
105 /* the files-to-be-erased list */
106 static struct link *erase_list;
109 * In copy_dir_dir we use two additional single linked lists: The first -
110 * variable name `parent_dirs' - holds information about already copied
111 * directories and is used to detect cyclic symbolic links.
112 * The second (`dest_dirs' below) holds information about just created
113 * target directories and is used to detect when an directory is copied
114 * into itself (we don't want to copy infinitly).
115 * Both lists don't use the linkcount and name structure members of struct
116 * link.
118 static struct link *dest_dirs = 0;
120 const char *op_names[3] = {
121 N_(" Copy "),
122 N_(" Move "),
123 N_(" Delete ")
126 /* }}} */
128 static int query_replace (FileOpContext * ctx, const char *destname,
129 struct stat *_s_stat, struct stat *_d_stat);
130 static int query_recursive (FileOpContext * ctx, const char *s);
131 static int do_file_error (const char *str);
132 static int erase_dir_iff_empty (FileOpContext *ctx, const char *s);
133 static int erase_file (FileOpContext *ctx, const char *s,
134 off_t *progress_count, double *progress_bytes,
135 int is_toplevel_file);
136 static int files_error (const char *format, const char *file1,
137 const char *file2);
140 enum CaseConvs { NO_CONV = 0, UP_CHAR = 1, LOW_CHAR = 2, UP_SECT =
141 4, LOW_SECT = 8 };
143 static char
144 convert_case (char c, enum CaseConvs *conversion)
146 if (*conversion & UP_CHAR) {
147 *conversion &= ~UP_CHAR;
148 return toupper ((unsigned char) c);
149 } else if (*conversion & LOW_CHAR) {
150 *conversion &= ~LOW_CHAR;
151 return tolower ((unsigned char) c);
152 } else if (*conversion & UP_SECT) {
153 return toupper ((unsigned char) c);
154 } else if (*conversion & LOW_SECT) {
155 return tolower ((unsigned char) c);
156 } else
157 return c;
160 static int transform_error = 0;
162 static const char *
163 do_transform_source (FileOpContext *ctx, const char *source)
165 size_t j, k, l, len;
166 const char *fnsource = x_basename (source);
167 int next_reg;
168 enum CaseConvs case_conv = NO_CONV;
169 static char fntarget[MC_MAXPATHLEN];
171 len = strlen (fnsource);
172 j = re_match (&ctx->rx, fnsource, len, 0, &ctx->regs);
173 if (j != len) {
174 transform_error = FILE_SKIP;
175 return NULL;
177 for (next_reg = 1, j = 0, k = 0; j < strlen (ctx->dest_mask); j++) {
178 switch (ctx->dest_mask[j]) {
179 case '\\':
180 j++;
181 if (!isdigit ((unsigned char) ctx->dest_mask[j])) {
182 /* Backslash followed by non-digit */
183 switch (ctx->dest_mask[j]) {
184 case 'U':
185 case_conv |= UP_SECT;
186 case_conv &= ~LOW_SECT;
187 break;
188 case 'u':
189 case_conv |= UP_CHAR;
190 break;
191 case 'L':
192 case_conv |= LOW_SECT;
193 case_conv &= ~UP_SECT;
194 break;
195 case 'l':
196 case_conv |= LOW_CHAR;
197 break;
198 case 'E':
199 case_conv = NO_CONV;
200 break;
201 default:
202 /* Backslash as quote mark */
203 fntarget[k++] =
204 convert_case (ctx->dest_mask[j], &case_conv);
206 break;
207 } else {
208 /* Backslash followed by digit */
209 next_reg = ctx->dest_mask[j] - '0';
210 /* Fall through */
213 case '*':
214 if (next_reg < 0 || next_reg >= RE_NREGS
215 || ctx->regs.start[next_reg] < 0) {
216 message (1, MSG_ERROR, _(" Invalid target mask "));
217 transform_error = FILE_ABORT;
218 return NULL;
220 for (l = (size_t) ctx->regs.start[next_reg];
221 l < (size_t) ctx->regs.end[next_reg]; l++)
222 fntarget[k++] = convert_case (fnsource[l], &case_conv);
223 next_reg++;
224 break;
226 default:
227 fntarget[k++] = convert_case (ctx->dest_mask[j], &case_conv);
228 break;
231 fntarget[k] = 0;
232 return fntarget;
235 static const char *
236 transform_source (FileOpContext *ctx, const char *source)
238 char *s = g_strdup (source);
239 char *q;
240 const char *p;
242 /* We remove \n from the filename since regex routines would use \n as an anchor */
243 /* this is just to be allowed to maniupulate file names with \n on it */
244 for (q = s; *q; q++) {
245 if (*q == '\n')
246 *q = ' ';
248 p = do_transform_source (ctx, s);
249 g_free (s);
250 return p;
253 static void
254 free_linklist (struct link **linklist)
256 struct link *lp, *lp2;
258 for (lp = *linklist; lp != NULL; lp = lp2) {
259 lp2 = lp->next;
260 g_free (lp);
262 *linklist = NULL;
265 static int
266 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
268 ino_t ino = sb->st_ino;
269 dev_t dev = sb->st_dev;
270 #ifdef USE_VFS
271 struct vfs_class *vfs = vfs_get_class (path);
272 #endif /* USE_VFS */
274 while (lp) {
275 #ifdef USE_VFS
276 if (lp->vfs == vfs)
277 #endif /* USE_VFS */
278 if (lp->ino == ino && lp->dev == dev)
279 return 1;
280 lp = lp->next;
282 return 0;
286 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
287 * and a hardlink was succesfully made
289 static int
290 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
292 struct link *lp;
293 struct vfs_class *my_vfs = vfs_get_class (src_name);
294 ino_t ino = pstat->st_ino;
295 dev_t dev = pstat->st_dev;
296 struct stat link_stat;
297 const char *p;
299 if (vfs_file_class_flags (src_name) & VFSF_NOLINKS)
300 return 0;
302 for (lp = linklist; lp != NULL; lp = lp->next)
303 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev) {
304 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
305 && link_stat.st_dev == dev
306 && vfs_get_class (lp->name) == my_vfs) {
307 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
308 was copied to */
309 if (vfs_get_class (dst_name) == vfs_get_class (p)) {
310 if (!mc_stat (p, &link_stat)) {
311 if (!mc_link (p, dst_name))
312 return 1;
316 message (1, MSG_ERROR, _(" Cannot make the hardlink "));
317 return 0;
319 lp = (struct link *) g_malloc (sizeof (struct link) + strlen (src_name)
320 + strlen (dst_name) + 1);
321 if (lp) {
322 char *lpdstname;
323 lp->vfs = my_vfs;
324 lp->ino = ino;
325 lp->dev = dev;
326 strcpy (lp->name, src_name);
327 lpdstname = lp->name + strlen(lp->name) + 1;
328 strcpy (lpdstname, dst_name);
329 lp->next = linklist;
330 linklist = lp;
332 return 0;
336 * Duplicate the contents of the symbolic link src_path in dst_path.
337 * Try to make a stable symlink if the option "stable symlink" was
338 * set in the file mask dialog.
339 * If dst_path is an existing symlink it will be deleted silently
340 * (upper levels take already care of existing files at dst_path).
342 static int
343 make_symlink (FileOpContext *ctx, const char *src_path, const char *dst_path)
345 char link_target[MC_MAXPATHLEN];
346 int len;
347 int return_status;
348 struct stat sb;
349 int dst_is_symlink;
351 if (mc_lstat (dst_path, &sb) == 0 && S_ISLNK (sb.st_mode))
352 dst_is_symlink = 1;
353 else
354 dst_is_symlink = 0;
356 retry_src_readlink:
357 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
358 if (len < 0) {
359 return_status =
360 file_error (_(" Cannot read source link \"%s\" \n %s "),
361 src_path);
362 if (return_status == FILE_RETRY)
363 goto retry_src_readlink;
364 return return_status;
366 link_target[len] = 0;
368 if (ctx->stable_symlinks)
369 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path)) {
370 message (1, MSG_ERROR,
371 _(" Cannot make stable symlinks across "
372 "non-local filesystems: \n\n"
373 " Option Stable Symlinks will be disabled "));
374 ctx->stable_symlinks = 0;
377 if (ctx->stable_symlinks && *link_target != PATH_SEP) {
378 char *p, *q, *s;
380 const char *r = strrchr (src_path, PATH_SEP);
382 if (r) {
383 p = g_strndup (src_path, r - src_path + 1);
384 if (*dst_path == PATH_SEP)
385 q = g_strdup (dst_path);
386 else
387 q = g_strconcat (p, dst_path, (char *) NULL);
388 s = strrchr (q, PATH_SEP);
389 if (s) {
390 s[1] = 0;
391 s = g_strconcat (p, link_target, (char *) NULL);
392 g_free (p);
393 g_strlcpy (link_target, s, sizeof (link_target));
394 g_free (s);
395 s = diff_two_paths (q, link_target);
396 if (s) {
397 g_strlcpy (link_target, s, sizeof (link_target));
398 g_free (s);
400 } else
401 g_free (p);
402 g_free (q);
405 retry_dst_symlink:
406 if (mc_symlink (link_target, dst_path) == 0)
407 /* Success */
408 return FILE_CONT;
410 * if dst_exists, it is obvious that this had failed.
411 * We can delete the old symlink and try again...
413 if (dst_is_symlink) {
414 if (!mc_unlink (dst_path))
415 if (mc_symlink (link_target, dst_path) == 0)
416 /* Success */
417 return FILE_CONT;
419 return_status =
420 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
421 dst_path);
422 if (return_status == FILE_RETRY)
423 goto retry_dst_symlink;
424 return return_status;
427 static int
428 progress_update_one (FileOpContext *ctx,
429 off_t *progress_count,
430 double *progress_bytes, off_t add, int is_toplevel_file)
432 int ret;
434 if (is_toplevel_file || ctx->progress_totals_computed) {
435 (*progress_count)++;
436 (*progress_bytes) += add;
439 /* Apply some heuristic here to not call the update stuff very often */
440 ret =
441 file_progress_show_count (ctx, *progress_count,
442 ctx->progress_count);
443 if (ret != FILE_CONT)
444 return ret;
445 ret =
446 file_progress_show_bytes (ctx, *progress_bytes,
447 ctx->progress_bytes);
449 return ret;
452 /* Status of the destination file */
453 enum {
454 DEST_NONE, /* Not created */
455 DEST_SHORT, /* Created, not fully copied */
456 DEST_FULL /* Created, fully copied */
460 copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path,
461 int ask_overwrite, off_t *progress_count,
462 double *progress_bytes, int is_toplevel_file)
464 uid_t src_uid = (uid_t) - 1;
465 gid_t src_gid = (gid_t) - 1;
467 char *buf = NULL;
468 int buf_size = BUF_8K;
469 int src_desc, dest_desc = -1;
470 int n_read, n_written;
471 mode_t src_mode = 0; /* The mode of the source file */
472 struct stat sb, sb2;
473 struct utimbuf utb;
474 int dst_exists = 0, appending = 0;
475 off_t n_read_total = 0, file_size = -1;
476 int return_status, temp_status;
477 struct timeval tv_transfer_start;
478 int dst_status = DEST_NONE; /* 1 if the file is not fully copied */
480 /* FIXME: We should not be using global variables! */
481 ctx->do_reget = 0;
482 return_status = FILE_RETRY;
484 if (file_progress_show_source (ctx, src_path) == FILE_ABORT ||
485 file_progress_show_target (ctx, dst_path) == FILE_ABORT)
486 return FILE_ABORT;
488 mc_refresh ();
490 while (mc_stat (dst_path, &sb2) == 0) {
491 if (S_ISDIR (sb2.st_mode)) {
492 return_status =
493 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
494 dst_path);
495 if (return_status == FILE_RETRY)
496 continue;
497 return return_status;
499 dst_exists = 1;
500 break;
503 while ((*ctx->stat_func) (src_path, &sb)) {
504 return_status =
505 file_error (_(" Cannot stat source file \"%s\" \n %s "),
506 src_path);
507 if (return_status != FILE_RETRY)
508 return return_status;
511 if (dst_exists) {
512 /* Destination already exists */
513 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino) {
514 message (1, MSG_ERROR,
515 _(" `%s' and `%s' are the same file "), src_path, dst_path);
516 do_refresh ();
517 return FILE_SKIP;
520 /* Should we replace destination? */
521 if (ask_overwrite) {
522 ctx->do_reget = 0;
523 return_status = query_replace (ctx, dst_path, &sb, &sb2);
524 if (return_status != FILE_CONT)
525 return return_status;
529 if (!ctx->do_append) {
530 /* Check the hardlinks */
531 if (!ctx->follow_links && sb.st_nlink > 1 &&
532 check_hardlinks (src_path, dst_path, &sb) == 1) {
533 /* We have made a hardlink - no more processing is necessary */
534 return FILE_CONT;
537 if (S_ISLNK (sb.st_mode)) {
538 int retval;
540 retval = make_symlink (ctx, src_path, dst_path);
541 return retval;
544 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
545 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) ||
546 S_ISSOCK (sb.st_mode)) {
547 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill,
548 sb.st_rdev) < 0) {
549 return_status = file_error (
550 _(" Cannot create special file \"%s\" \n %s "), dst_path);
551 if (return_status == FILE_RETRY)
552 continue;
553 return return_status;
555 /* Success */
557 while (ctx->preserve_uidgid
558 && mc_chown (dst_path, sb.st_uid, sb.st_gid)) {
559 temp_status = file_error (
560 _(" Cannot chown target file \"%s\" \n %s "), dst_path);
561 if (temp_status == FILE_RETRY)
562 continue;
563 return temp_status;
565 while (ctx->preserve &&
566 mc_chmod (dst_path, sb.st_mode & ctx->umask_kill)) {
567 temp_status = file_error (
568 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
569 if (temp_status == FILE_RETRY)
570 continue;
571 return temp_status;
573 return FILE_CONT;
577 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
579 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0) {
580 return_status = file_error (
581 _(" Cannot open source file \"%s\" \n %s "), src_path);
582 if (return_status == FILE_RETRY)
583 continue;
584 ctx->do_append = 0;
585 return return_status;
588 if (ctx->do_reget) {
589 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget) {
590 message (1, _("Warning"),
591 _(" Reget failed, about to overwrite file "));
592 ctx->do_reget = ctx->do_append = 0;
596 while (mc_fstat (src_desc, &sb)) {
597 return_status = file_error (
598 _(" Cannot fstat source file \"%s\" \n %s "), src_path);
599 if (return_status == FILE_RETRY)
600 continue;
601 ctx->do_append = 0;
602 goto ret;
604 src_mode = sb.st_mode;
605 src_uid = sb.st_uid;
606 src_gid = sb.st_gid;
607 utb.actime = sb.st_atime;
608 utb.modtime = sb.st_mtime;
609 file_size = sb.st_size;
611 /* Create the new regular file with small permissions initially,
612 do not create a security hole. FIXME: You have security hole
613 here, btw. Imagine copying to /tmp and symlink attack :-( */
615 while ((dest_desc = mc_open (dst_path, O_WRONLY | (ctx->do_append ?
616 O_APPEND : (O_CREAT | O_TRUNC)), 0600)) < 0) {
617 return_status = file_error (
618 _(" Cannot create target file \"%s\" \n %s "), dst_path);
619 if (return_status == FILE_RETRY)
620 continue;
621 ctx->do_append = 0;
622 goto ret;
624 dst_status = DEST_SHORT; /* file opened, but not fully copied */
626 appending = ctx->do_append;
627 ctx->do_append = 0;
629 /* Find out the optimal buffer size. */
630 while (mc_fstat (dest_desc, &sb)) {
631 return_status = file_error (
632 _(" Cannot fstat target file \"%s\" \n %s "), dst_path);
633 if (return_status == FILE_RETRY)
634 continue;
635 goto ret;
637 buf = g_malloc (buf_size);
639 ctx->eta_secs = 0.0;
640 ctx->bps = 0;
642 return_status = file_progress_show (ctx, 0, file_size);
644 mc_refresh ();
646 if (return_status != FILE_CONT)
647 goto ret;
650 struct timeval tv_current, tv_last_update, tv_last_input;
651 int secs, update_secs;
652 long dt;
653 const char *stalled_msg;
655 tv_last_update = tv_transfer_start;
657 for (;;) {
658 /* src_read */
659 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
660 n_read = -1;
661 else
662 while ((n_read = mc_read (src_desc, buf, buf_size)) < 0) {
663 return_status = file_error (
664 _(" Cannot read source file \"%s\" \n %s "), src_path);
665 if (return_status == FILE_RETRY)
666 continue;
667 goto ret;
669 if (n_read == 0)
670 break;
672 gettimeofday (&tv_current, NULL);
674 if (n_read > 0) {
675 char *t = buf;
676 n_read_total += n_read;
678 /* Windows NT ftp servers report that files have no
679 * permissions: -------, so if we happen to have actually
680 * read something, we should fix the permissions.
682 if (!(src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)))
683 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
684 gettimeofday (&tv_last_input, NULL);
686 /* dst_write */
687 while ((n_written =
688 mc_write (dest_desc, t, n_read)) < n_read) {
689 if (n_written > 0) {
690 n_read -= n_written;
691 t += n_written;
692 continue;
694 return_status =
695 file_error (_(" Cannot write target file \"%s\" \n %s "),
696 dst_path);
697 if (return_status != FILE_RETRY)
698 goto ret;
702 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
703 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
704 if (secs > 2) {
705 rotate_dash ();
706 tv_last_update = tv_current;
709 /* 2. Check for a stalled condition */
710 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
711 stalled_msg = "";
712 if (update_secs > 4) {
713 stalled_msg = _("(stalled)");
716 /* 3. Compute ETA */
717 if (secs > 2) {
718 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
720 if (n_read_total) {
721 ctx->eta_secs =
722 ((dt / (double) n_read_total) * file_size) - dt;
723 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
724 } else
725 ctx->eta_secs = 0.0;
728 /* 4. Compute BPS rate */
729 if (secs > 2) {
730 ctx->bps_time =
731 (tv_current.tv_sec - tv_transfer_start.tv_sec);
732 if (ctx->bps_time < 1)
733 ctx->bps_time = 1;
734 ctx->bps = n_read_total / ctx->bps_time;
737 file_progress_set_stalled_label (ctx, stalled_msg);
738 return_status = file_progress_show_bytes (ctx, *progress_bytes +
739 n_read_total + ctx->do_reget, ctx->progress_bytes);
740 if (return_status == FILE_CONT) {
741 return_status =
742 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size);
744 mc_refresh ();
745 if (return_status != FILE_CONT)
746 goto ret;
750 dst_status = DEST_FULL; /* copy successful, don't remove target file */
752 ret:
753 g_free (buf);
755 while (src_desc != -1 && mc_close (src_desc) < 0) {
756 temp_status = file_error (
757 _(" Cannot close source file \"%s\" \n %s "), src_path);
758 if (temp_status == FILE_RETRY)
759 continue;
760 if (temp_status == FILE_ABORT)
761 return_status = temp_status;
762 break;
765 while (dest_desc != -1 && mc_close (dest_desc) < 0) {
766 temp_status = file_error (
767 _(" Cannot close target file \"%s\" \n %s "), dst_path);
768 if (temp_status == FILE_RETRY)
769 continue;
770 return_status = temp_status;
771 break;
774 if (dst_status == DEST_SHORT) {
775 /* Remove short file */
776 int result;
777 result = query_dialog (_("Copy"),
778 _("Incomplete file was retrieved. Keep it?"),
779 D_ERROR, 2, _("&Delete"), _("&Keep"));
780 if (!result)
781 mc_unlink (dst_path);
782 } else if (dst_status == DEST_FULL) {
783 /* Copy has succeeded */
784 if (!appending && ctx->preserve_uidgid) {
785 while (mc_chown (dst_path, src_uid, src_gid)) {
786 temp_status = file_error (
787 _(" Cannot chown target file \"%s\" \n %s "), dst_path);
788 if (temp_status == FILE_RETRY)
789 continue;
790 return_status = temp_status;
791 break;
795 if (!appending && ctx->preserve) {
796 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill))) {
797 temp_status = file_error (
798 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
799 if (temp_status != FILE_RETRY) {
800 return_status = temp_status;
801 break;
804 mc_utime (dst_path, &utb);
808 if (return_status == FILE_CONT)
809 return_status =
810 progress_update_one (ctx, progress_count, progress_bytes,
811 file_size, is_toplevel_file);
813 return return_status;
817 * I think these copy_*_* functions should have a return type.
818 * anyway, this function *must* have two directories as arguments.
820 /* FIXME: This function needs to check the return values of the
821 function calls */
823 copy_dir_dir (FileOpContext *ctx, const char *s, const char *d, int toplevel,
824 int move_over, int delete, struct link *parent_dirs,
825 off_t *progress_count, double *progress_bytes)
827 struct dirent *next;
828 struct stat buf, cbuf;
829 DIR *reading;
830 char *path, *mdpath, *dest_file, *dest_dir;
831 int return_status = FILE_CONT;
832 struct utimbuf utb;
833 struct link *lp;
835 /* First get the mode of the source dir */
836 retry_src_stat:
837 if ((*ctx->stat_func) (s, &cbuf)) {
838 return_status =
839 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s);
840 if (return_status == FILE_RETRY)
841 goto retry_src_stat;
842 return return_status;
845 if (is_in_linklist (dest_dirs, s, &cbuf)) {
846 /* Don't copy a directory we created before (we don't want to copy
847 infinitely if a directory is copied into itself) */
848 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
849 return FILE_CONT;
852 /* Hmm, hardlink to directory??? - Norbert */
853 /* FIXME: In this step we should do something
854 in case the destination already exist */
855 /* Check the hardlinks */
856 if (ctx->preserve && cbuf.st_nlink > 1
857 && check_hardlinks (s, d, &cbuf) == 1) {
858 /* We have made a hardlink - no more processing is necessary */
859 return return_status;
862 if (!S_ISDIR (cbuf.st_mode)) {
863 return_status =
864 file_error (_(" Source \"%s\" is not a directory \n %s "), s);
865 if (return_status == FILE_RETRY)
866 goto retry_src_stat;
867 return return_status;
870 if (is_in_linklist (parent_dirs, s, &cbuf)) {
871 /* we found a cyclic symbolic link */
872 message (1, MSG_ERROR,
873 _(" Cannot copy cyclic symbolic link \n `%s' "), s);
874 return FILE_SKIP;
877 lp = g_new (struct link, 1);
878 lp->vfs = vfs_get_class (s);
879 lp->ino = cbuf.st_ino;
880 lp->dev = cbuf.st_dev;
881 lp->next = parent_dirs;
882 parent_dirs = lp;
884 retry_dst_stat:
885 /* Now, check if the dest dir exists, if not, create it. */
886 if (mc_stat (d, &buf)) {
887 /* Here the dir doesn't exist : make it ! */
889 if (move_over) {
890 if (mc_rename (s, d) == 0) {
891 g_free (parent_dirs);
892 return FILE_CONT;
895 dest_dir = g_strdup (d);
896 } else {
898 * If the destination directory exists, we want to copy the whole
899 * directory, but we only want this to happen once.
901 * Escape sequences added to the * to compiler warnings.
902 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
903 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
905 if (!S_ISDIR (buf.st_mode)) {
906 return_status = file_error(
907 _(" Destination \"%s\" must be a directory \n %s "), d);
908 if (return_status == FILE_RETRY)
909 goto retry_dst_stat;
910 g_free (parent_dirs);
911 return return_status;
913 /* Dive into subdir if exists */
914 if (toplevel && ctx->dive_into_subdirs) {
915 dest_dir = concat_dir_and_file (d, x_basename (s));
916 } else {
917 dest_dir = g_strdup (d);
918 goto dont_mkdir;
921 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU)) {
922 return_status = file_error (
923 _(" Cannot create target directory \"%s\" \n %s "), dest_dir);
924 if (return_status != FILE_RETRY)
925 goto ret;
928 lp = g_new (struct link, 1);
929 mc_stat (dest_dir, &buf);
930 lp->vfs = vfs_get_class (dest_dir);
931 lp->ino = buf.st_ino;
932 lp->dev = buf.st_dev;
933 lp->next = dest_dirs;
934 dest_dirs = lp;
936 if (ctx->preserve_uidgid) {
937 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid)) {
938 return_status = file_error (
939 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir);
940 if (return_status != FILE_RETRY)
941 goto ret;
945 dont_mkdir:
946 /* open the source dir for reading */
947 if ((reading = mc_opendir (s)) == 0) {
948 goto ret;
951 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT) {
953 * Now, we don't want '.' and '..' to be created / copied at any time
955 if (!strcmp (next->d_name, "."))
956 continue;
957 if (!strcmp (next->d_name, ".."))
958 continue;
960 /* get the filename and add it to the src directory */
961 path = concat_dir_and_file (s, next->d_name);
963 (*ctx->stat_func) (path, &buf);
964 if (S_ISDIR (buf.st_mode)) {
965 mdpath = concat_dir_and_file (dest_dir, next->d_name);
967 * From here, we just intend to recursively copy subdirs, not
968 * the double functionality of copying different when the target
969 * dir already exists. So, we give the recursive call the flag 0
970 * meaning no toplevel.
972 return_status = copy_dir_dir (ctx, path, mdpath, 0, 0, delete,
973 parent_dirs, progress_count, progress_bytes);
974 g_free (mdpath);
975 } else {
976 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
977 return_status = copy_file_file (ctx, path, dest_file, 1,
978 progress_count, progress_bytes, 0);
979 g_free (dest_file);
981 if (delete && return_status == FILE_CONT) {
982 if (ctx->erase_at_end) {
983 static struct link *tail;
984 lp = g_malloc (sizeof (struct link) + strlen (path));
985 strcpy (lp->name, path);
986 lp->st_mode = buf.st_mode;
987 lp->next = 0;
988 if (erase_list) {
989 tail->next = lp;
990 tail = lp;
991 } else
992 erase_list = tail = lp;
993 } else {
994 if (S_ISDIR (buf.st_mode)) {
995 return_status = erase_dir_iff_empty (ctx, path);
996 } else
997 return_status = erase_file (ctx, path, 0, 0, 0);
1000 g_free (path);
1002 mc_closedir (reading);
1004 if (ctx->preserve) {
1005 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1006 utb.actime = cbuf.st_atime;
1007 utb.modtime = cbuf.st_mtime;
1008 mc_utime (dest_dir, &utb);
1011 ret:
1012 g_free (dest_dir);
1013 g_free (parent_dirs);
1014 return return_status;
1017 /* }}} */
1019 /* {{{ Move routines */
1021 static int
1022 move_file_file (FileOpContext *ctx, const char *s, const char *d,
1023 off_t *progress_count, double *progress_bytes)
1025 struct stat src_stats, dst_stats;
1026 int return_status = FILE_CONT;
1027 gboolean copy_done = FALSE;
1029 if (file_progress_show_source (ctx, s) == FILE_ABORT
1030 || file_progress_show_target (ctx, d) == FILE_ABORT)
1031 return FILE_ABORT;
1033 mc_refresh ();
1035 while (mc_lstat (s, &src_stats) != 0) {
1036 /* Source doesn't exist */
1037 return_status =
1038 file_error (_(" Cannot stat file \"%s\" \n %s "), s);
1039 if (return_status != FILE_RETRY)
1040 return return_status;
1043 if (mc_lstat (d, &dst_stats) == 0) {
1044 if (src_stats.st_dev == dst_stats.st_dev
1045 && src_stats.st_ino == dst_stats.st_ino) {
1046 int msize = COLS - 36;
1047 char st[MC_MAXPATHLEN];
1048 char dt[MC_MAXPATHLEN];
1050 if (msize < 0)
1051 msize = 40;
1052 msize /= 2;
1054 strcpy (st, path_trunc (s, msize));
1055 strcpy (dt, path_trunc (d, msize));
1056 message (1, MSG_ERROR,
1057 _(" `%s' and `%s' are the same file "), st, dt);
1058 do_refresh ();
1059 return FILE_SKIP;
1062 if (S_ISDIR (dst_stats.st_mode)) {
1063 message (1, MSG_ERROR,
1064 _(" Cannot overwrite directory `%s' "), d);
1065 do_refresh ();
1066 return FILE_SKIP;
1069 if (confirm_overwrite) {
1070 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1071 if (return_status != FILE_CONT)
1072 return return_status;
1074 /* Ok to overwrite */
1077 if (!ctx->do_append) {
1078 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks) {
1079 if ((return_status = make_symlink (ctx, s, d)) == FILE_CONT) {
1080 goto retry_src_remove;
1081 } else
1082 return return_status;
1085 if (mc_rename (s, d) == 0) {
1086 return progress_update_one (ctx, progress_count,
1087 progress_bytes,
1088 src_stats.st_size, 1);
1091 #if 0
1092 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1093 one nfs to the same, but on the server it is on two different
1094 filesystems. Then nfs returns EIO instead of EXDEV.
1095 Hope it will not hurt if we always in case of error try to copy/delete. */
1096 else
1097 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1099 if (errno != EXDEV) {
1100 return_status =
1101 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s,
1103 if (return_status == FILE_RETRY)
1104 goto retry_rename;
1105 return return_status;
1107 #endif
1109 /* Failed because filesystem boundary -> copy the file instead */
1110 return_status =
1111 copy_file_file (ctx, s, d, 0, progress_count, progress_bytes, 1);
1112 if (return_status != FILE_CONT)
1113 return return_status;
1115 copy_done = TRUE;
1117 if ((return_status =
1118 file_progress_show_source (ctx, NULL)) != FILE_CONT
1119 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1120 return return_status;
1122 mc_refresh ();
1124 retry_src_remove:
1125 if (mc_unlink (s)) {
1126 return_status =
1127 file_error (_(" Cannot remove file \"%s\" \n %s "), s);
1128 if (return_status == FILE_RETRY)
1129 goto retry_src_remove;
1130 return return_status;
1133 if (!copy_done) {
1134 return_status = progress_update_one (ctx,
1135 progress_count,
1136 progress_bytes,
1137 src_stats.st_size, 1);
1140 return return_status;
1144 move_dir_dir (FileOpContext *ctx, const char *s, const char *d,
1145 off_t *progress_count, double *progress_bytes)
1147 struct stat sbuf, dbuf, destbuf;
1148 struct link *lp;
1149 char *destdir;
1150 int return_status;
1151 int move_over = 0;
1153 if (file_progress_show_source (ctx, s) == FILE_ABORT ||
1154 file_progress_show_target (ctx, d) == FILE_ABORT)
1155 return FILE_ABORT;
1157 mc_refresh ();
1159 mc_stat (s, &sbuf);
1160 if (mc_stat (d, &dbuf))
1161 destdir = g_strdup (d); /* destination doesn't exist */
1162 else if (!ctx->dive_into_subdirs) {
1163 destdir = g_strdup (d);
1164 move_over = 1;
1165 } else
1166 destdir = concat_dir_and_file (d, x_basename (s));
1168 if (sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino) {
1169 int msize = COLS - 36;
1170 char st[MC_MAXPATHLEN];
1171 char dt[MC_MAXPATHLEN];
1173 if (msize < 0)
1174 msize = 40;
1175 msize /= 2;
1177 strcpy (st, path_trunc (s, msize));
1178 strcpy (dt, path_trunc (d, msize));
1179 message (1, MSG_ERROR,
1180 _(" `%s' and `%s' are the same directory "), st, dt);
1181 do_refresh ();
1182 return FILE_SKIP;
1185 /* Check if the user inputted an existing dir */
1186 retry_dst_stat:
1187 if (!mc_stat (destdir, &destbuf)) {
1188 if (move_over) {
1189 return_status = copy_dir_dir (ctx, s, destdir, 0, 1, 1, 0,
1190 progress_count, progress_bytes);
1192 if (return_status != FILE_CONT)
1193 goto ret;
1194 goto oktoret;
1195 } else {
1196 if (S_ISDIR (destbuf.st_mode))
1197 return_status =
1198 file_error (_
1199 (" Cannot overwrite directory \"%s\" %s "),
1200 destdir);
1201 else
1202 return_status =
1203 file_error (_(" Cannot overwrite file \"%s\" %s "),
1204 destdir);
1205 if (return_status == FILE_RETRY)
1206 goto retry_dst_stat;
1208 g_free (destdir);
1209 return return_status;
1212 retry_rename:
1213 if (mc_rename (s, destdir) == 0) {
1214 return_status = FILE_CONT;
1215 goto ret;
1218 if (errno != EXDEV) {
1219 return_status =
1220 files_error (_
1221 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1222 s, d);
1223 if (return_status == FILE_RETRY)
1224 goto retry_rename;
1225 goto ret;
1227 /* Failed because of filesystem boundary -> copy dir instead */
1228 return_status =
1229 copy_dir_dir (ctx, s, destdir, 0, 0, 1, 0, progress_count,
1230 progress_bytes);
1232 if (return_status != FILE_CONT)
1233 goto ret;
1234 oktoret:
1235 if ((return_status =
1236 file_progress_show_source (ctx, NULL)) != FILE_CONT
1237 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1238 goto ret;
1240 mc_refresh ();
1241 if (ctx->erase_at_end) {
1242 for (; erase_list && return_status != FILE_ABORT;) {
1243 if (S_ISDIR (erase_list->st_mode)) {
1244 return_status =
1245 erase_dir_iff_empty (ctx, erase_list->name);
1246 } else
1247 return_status =
1248 erase_file (ctx, erase_list->name, 0, 0, 0);
1249 lp = erase_list;
1250 erase_list = erase_list->next;
1251 g_free (lp);
1254 erase_dir_iff_empty (ctx, s);
1256 ret:
1257 g_free (destdir);
1258 while (erase_list) {
1259 lp = erase_list;
1260 erase_list = erase_list->next;
1261 g_free (lp);
1263 return return_status;
1266 /* }}} */
1268 /* {{{ Erase routines */
1269 /* Don't update progress status if progress_count==NULL */
1270 static int
1271 erase_file (FileOpContext *ctx, const char *s, off_t *progress_count,
1272 double *progress_bytes, int is_toplevel_file)
1274 int return_status;
1275 struct stat buf;
1277 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1278 return FILE_ABORT;
1279 mc_refresh ();
1281 if (progress_count && mc_lstat (s, &buf)) {
1282 /* ignore, most likely the mc_unlink fails, too */
1283 buf.st_size = 0;
1286 while (mc_unlink (s)) {
1287 return_status =
1288 file_error (_(" Cannot delete file \"%s\" \n %s "), s);
1289 if (return_status != FILE_RETRY)
1290 return return_status;
1293 if (progress_count)
1294 return progress_update_one (ctx, progress_count, progress_bytes,
1295 buf.st_size, is_toplevel_file);
1296 else
1297 return FILE_CONT;
1300 static int
1301 recursive_erase (FileOpContext *ctx, const char *s, off_t *progress_count,
1302 double *progress_bytes)
1304 struct dirent *next;
1305 struct stat buf;
1306 DIR *reading;
1307 char *path;
1308 int return_status = FILE_CONT;
1310 if (!strcmp (s, ".."))
1311 return 1;
1313 reading = mc_opendir (s);
1315 if (!reading)
1316 return 1;
1318 while ((next = mc_readdir (reading)) && return_status == FILE_CONT) {
1319 if (!strcmp (next->d_name, "."))
1320 continue;
1321 if (!strcmp (next->d_name, ".."))
1322 continue;
1323 path = concat_dir_and_file (s, next->d_name);
1324 if (mc_lstat (path, &buf)) {
1325 g_free (path);
1326 mc_closedir (reading);
1327 return 1;
1329 if (S_ISDIR (buf.st_mode))
1330 return_status =
1331 (recursive_erase
1332 (ctx, path, progress_count, progress_bytes)
1333 != FILE_CONT);
1334 else
1335 return_status =
1336 erase_file (ctx, path, progress_count, progress_bytes, 0);
1337 g_free (path);
1339 mc_closedir (reading);
1340 if (return_status != FILE_CONT)
1341 return return_status;
1342 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1343 return FILE_ABORT;
1344 mc_refresh ();
1346 while (my_rmdir (s)) {
1347 return_status =
1348 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1349 if (return_status != FILE_RETRY)
1350 return return_status;
1353 return FILE_CONT;
1356 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1357 in the directory path points to, 0 else. */
1358 static int
1359 check_dir_is_empty (const char *path)
1361 DIR *dir;
1362 struct dirent *d;
1363 int i;
1365 dir = mc_opendir (path);
1366 if (!dir)
1367 return -1;
1369 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir)) {
1370 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1371 (d->d_name[1] == '.'
1372 && d->d_name[2] == '\0')))
1373 continue; /* "." or ".." */
1374 i = 0;
1375 break;
1378 mc_closedir (dir);
1379 return i;
1383 erase_dir (FileOpContext *ctx, const char *s, off_t *progress_count,
1384 double *progress_bytes)
1386 int error;
1388 if (strcmp (s, "..") == 0)
1389 return FILE_SKIP;
1391 if (strcmp (s, ".") == 0)
1392 return FILE_SKIP;
1394 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1395 return FILE_ABORT;
1396 mc_refresh ();
1398 /* The old way to detect a non empty directory was:
1399 error = my_rmdir (s);
1400 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1401 For the linux user space nfs server (nfs-server-2.2beta29-2)
1402 we would have to check also for EIO. I hope the new way is
1403 fool proof. (Norbert)
1405 error = check_dir_is_empty (s);
1406 if (error == 0) { /* not empty */
1407 error = query_recursive (ctx, s);
1408 if (error == FILE_CONT)
1409 return recursive_erase (ctx, s, progress_count,
1410 progress_bytes);
1411 else
1412 return error;
1415 while (my_rmdir (s) == -1) {
1416 error =
1417 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1418 if (error != FILE_RETRY)
1419 return error;
1422 return FILE_CONT;
1425 static int
1426 erase_dir_iff_empty (FileOpContext *ctx, const char *s)
1428 int error;
1430 if (strcmp (s, "..") == 0)
1431 return FILE_SKIP;
1433 if (strcmp (s, ".") == 0)
1434 return FILE_SKIP;
1436 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1437 return FILE_ABORT;
1438 mc_refresh ();
1440 if (1 != check_dir_is_empty (s)) /* not empty or error */
1441 return FILE_CONT;
1443 while (my_rmdir (s)) {
1444 error =
1445 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1446 if (error != FILE_RETRY)
1447 return error;
1450 return FILE_CONT;
1453 /* }}} */
1455 /* {{{ Panel operate routines */
1458 * Return currently selected entry name or the name of the first marked
1459 * entry if there is one.
1461 static char *
1462 panel_get_file (WPanel *panel, struct stat *stat_buf)
1464 int i;
1466 if (get_current_type () == view_tree) {
1467 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
1468 char *tree_name = tree_selected_name (tree);
1470 mc_stat (tree_name, stat_buf);
1471 return tree_name;
1474 if (panel->marked) {
1475 for (i = 0; i < panel->count; i++)
1476 if (panel->dir.list[i].f.marked) {
1477 *stat_buf = panel->dir.list[i].st;
1478 return panel->dir.list[i].fname;
1480 } else {
1481 *stat_buf = panel->dir.list[panel->selected].st;
1482 return panel->dir.list[panel->selected].fname;
1484 g_assert_not_reached ();
1485 return NULL;
1489 * compute_dir_size:
1491 * Computes the number of bytes used by the files in a directory
1493 void
1494 compute_dir_size (const char *dirname, off_t *ret_marked, double *ret_total)
1496 DIR *dir;
1497 struct dirent *dirent;
1499 dir = mc_opendir (dirname);
1501 if (!dir)
1502 return;
1504 while ((dirent = mc_readdir (dir)) != NULL) {
1505 struct stat s;
1506 char *fullname;
1507 int res;
1509 if (strcmp (dirent->d_name, ".") == 0)
1510 continue;
1511 if (strcmp (dirent->d_name, "..") == 0)
1512 continue;
1514 fullname = concat_dir_and_file (dirname, dirent->d_name);
1516 res = mc_lstat (fullname, &s);
1518 if (res != 0) {
1519 g_free (fullname);
1520 continue;
1523 if (S_ISDIR (s.st_mode)) {
1524 off_t subdir_count = 0;
1525 double subdir_bytes = 0;
1527 compute_dir_size (fullname, &subdir_count, &subdir_bytes);
1529 *ret_marked += subdir_count;
1530 *ret_total += subdir_bytes;
1531 } else {
1532 (*ret_marked)++;
1533 *ret_total += s.st_size;
1535 g_free (fullname);
1538 mc_closedir (dir);
1542 * panel_compute_totals:
1544 * compute the number of files and the number of bytes
1545 * used up by the whole selection, recursing directories
1546 * as required. In addition, it checks to see if it will
1547 * overwrite any files by doing the copy.
1549 static void
1550 panel_compute_totals (WPanel *panel, off_t *ret_marked, double *ret_total)
1552 int i;
1554 *ret_marked = 0;
1555 *ret_total = 0.0;
1557 for (i = 0; i < panel->count; i++) {
1558 struct stat *s;
1560 if (!panel->dir.list[i].f.marked)
1561 continue;
1563 s = &panel->dir.list[i].st;
1565 if (S_ISDIR (s->st_mode)) {
1566 char *dir_name;
1567 off_t subdir_count = 0;
1568 double subdir_bytes = 0;
1570 dir_name =
1571 concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1572 compute_dir_size (dir_name, &subdir_count, &subdir_bytes);
1574 *ret_marked += subdir_count;
1575 *ret_total += subdir_bytes;
1576 g_free (dir_name);
1577 } else {
1578 (*ret_marked)++;
1579 *ret_total += s->st_size;
1585 * This array introduced to avoid translation problems. The former (op_names)
1586 * is assumed to be nouns, suitable in dialog box titles; this one should
1587 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1588 * Notice first symbol - it is to fool gettext and force these strings to
1589 * be different for it. First symbol is skipped while building a prompt.
1590 * (I don't use spaces around the words, because someday they could be
1591 * dropped, when widgets get smarter)
1593 static const char *op_names1[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1594 #define FMD_XLEN 64
1596 int fmd_xlen = FMD_XLEN;
1599 * These are formats for building a prompt. Parts encoded as follows:
1600 * %o - operation from op_names1
1601 * %f - file/files or files/directories, as appropriate
1602 * %m - "with source mask" or question mark for delete
1603 * %s - source name (truncated)
1604 * %d - number of marked files
1605 * %e - "to:" or question mark for delete
1607 * xgettext:no-c-format */
1608 static const char *one_format = N_("%o %f \"%s\"%m");
1609 /* xgettext:no-c-format */
1610 static const char *many_format = N_("%o %d %f%m");
1611 static const char *prompt_parts[] = {
1612 N_("file"), N_("files"), N_("directory"), N_("directories"),
1613 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1617 * Generate user prompt for panel operation.
1618 * single_source is the name if the source entry or NULL for multiple
1619 * entries.
1620 * src_stat is only used when single_source is not NULL.
1622 static void
1623 panel_operate_generate_prompt (const WPanel *panel, const int operation,
1624 const char *single_source,
1625 const struct stat *src_stat)
1627 register const char *sp, *cp;
1628 register int i;
1629 char format_string[BUF_MEDIUM];
1630 char *dp = format_string;
1632 #ifdef ENABLE_NLS
1633 static int i18n_flag = 0;
1634 if (!i18n_flag) {
1635 fmd_init_i18n (FALSE); /* to get proper fmd_xlen */
1637 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1638 op_names1[i] = _(op_names1[i]);
1640 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1641 prompt_parts[i] = _(prompt_parts[i]);
1643 one_format = _(one_format);
1644 many_format = _(many_format);
1645 i18n_flag = 1;
1647 #endif /* ENABLE_NLS */
1649 sp = single_source ? one_format : many_format;
1651 while (*sp) {
1652 switch (*sp) {
1653 case '%':
1654 cp = NULL;
1655 switch (sp[1]) {
1656 case 'o':
1657 cp = op_names1[operation] + 1;
1658 break;
1659 case 'm':
1660 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1661 break;
1662 case 'e':
1663 cp = operation == OP_DELETE ? "?" : prompt_parts[6];
1664 break;
1665 case 'f':
1666 if (single_source) {
1667 cp = S_ISDIR (src_stat->
1668 st_mode) ? prompt_parts[2] :
1669 prompt_parts[0];
1670 } else {
1671 cp = (panel->marked == panel->dirs_marked)
1672 ? prompt_parts[3]
1673 : (panel->dirs_marked ? prompt_parts[4]
1674 : prompt_parts[1]);
1676 break;
1677 default:
1678 *dp++ = *sp++;
1680 if (cp) {
1681 sp += 2;
1682 while (*cp)
1683 *dp++ = *cp++;
1685 break;
1686 default:
1687 *dp++ = *sp++;
1690 *dp = '\0';
1692 if (single_source) {
1693 i = fmd_xlen - strlen (format_string) - 4;
1694 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1695 name_trunc (single_source, i));
1696 } else {
1697 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1698 panel->marked);
1699 i = strlen (cmd_buf) + 6 - fmd_xlen;
1700 if (i > 0) {
1701 fmd_xlen += i;
1702 fmd_init_i18n (TRUE); /* to recalculate positions of child widgets */
1708 * panel_operate:
1710 * Performs one of the operations on the selection on the source_panel
1711 * (copy, delete, move).
1713 * Returns 1 if did change the directory
1714 * structure, Returns 0 if user aborted
1716 * force_single forces operation on the current entry and affects
1717 * default destination. Current filename is used as default.
1720 panel_operate (void *source_panel, FileOperation operation,
1721 int force_single)
1723 WPanel *panel = source_panel;
1724 #ifdef WITH_FULL_PATHS
1725 char *source_with_path = NULL;
1726 #else
1727 # define source_with_path source
1728 #endif /* !WITH_FULL_PATHS */
1729 char *source = NULL;
1730 char *dest = NULL;
1731 const char *temp = NULL;
1732 char *save_cwd = NULL, *save_dest = NULL;
1733 int single_entry = (get_current_type () == view_tree)
1734 || (panel->marked <= 1) || force_single;
1735 struct stat src_stat, dst_stat;
1736 int i, value;
1737 FileOpContext *ctx;
1739 off_t count = 0;
1740 double bytes = 0;
1742 int dst_result;
1743 int do_bg = 0; /* do background operation? */
1745 free_linklist (&linklist);
1746 free_linklist (&dest_dirs);
1748 if (single_entry) {
1749 if (force_single) {
1750 source = selection (panel)->fname;
1751 src_stat = selection (panel)->st;
1752 } else {
1753 source = panel_get_file (panel, &src_stat);
1756 if (!strcmp (source, "..")) {
1757 message (1, MSG_ERROR, _(" Cannot operate on \"..\"! "));
1758 return 0;
1762 /* Generate confirmation prompt */
1763 panel_operate_generate_prompt (panel, operation, source, &src_stat);
1765 ctx = file_op_context_new (operation);
1767 /* Show confirmation dialog */
1768 if (operation == OP_DELETE && confirm_delete) {
1769 if (safe_delete)
1770 query_set_sel (1);
1772 i = query_dialog (_(op_names[operation]), cmd_buf, D_ERROR, 2,
1773 _("&Yes"), _("&No"));
1775 if (i != 0) {
1776 file_op_context_destroy (ctx);
1777 return 0;
1779 } else if (operation != OP_DELETE) {
1780 char *dest_dir;
1782 /* Forced single operations default to the original name */
1783 if (force_single)
1784 dest_dir = source;
1785 else if (get_other_type () == view_listing)
1786 dest_dir = other_panel->cwd;
1787 else
1788 dest_dir = panel->cwd;
1790 dest =
1791 file_mask_dialog (ctx, operation, cmd_buf, dest_dir,
1792 single_entry, &do_bg);
1793 if (!dest) {
1794 file_op_context_destroy (ctx);
1795 return 0;
1797 if (!*dest) {
1798 file_op_context_destroy (ctx);
1799 g_free (dest);
1800 return 0;
1803 #ifdef WITH_BACKGROUND
1804 /* Did the user select to do a background operation? */
1805 if (do_bg) {
1806 int v;
1808 v = do_background (ctx,
1809 g_strconcat (op_names[operation], ": ",
1810 panel->cwd, NULL));
1811 if (v == -1) {
1812 message (1, MSG_ERROR,
1813 _(" Sorry, I could not put the job in background "));
1816 /* If we are the parent */
1817 if (v == 1) {
1818 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
1819 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
1820 /* file_op_context_destroy (ctx); */
1821 return 0;
1824 #endif /* WITH_BACKGROUND */
1826 /* Initialize things */
1827 /* We do not want to trash cache every time file is
1828 created/touched. However, this will make our cache contain
1829 invalid data. */
1830 if (dest) {
1831 if (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1))
1832 save_dest = g_strdup (dest);
1834 if (panel->cwd) {
1835 if (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1))
1836 save_cwd = g_strdup (panel->cwd);
1839 /* Now, let's do the job */
1841 if (do_bg)
1842 ctx->ui = NULL;
1843 else
1844 file_op_context_create_ui (ctx, 1);
1846 /* This code is only called by the tree and panel code */
1847 if (single_entry) {
1848 /* We now have ETA in all cases */
1850 /* One file: FIXME mc_chdir will take user out of any vfs */
1851 if (operation != OP_COPY && get_current_type () == view_tree)
1852 mc_chdir (PATH_SEP_STR);
1854 /* The source and src_stat variables have been initialized before */
1855 #ifdef WITH_FULL_PATHS
1856 source_with_path = concat_dir_and_file (panel->cwd, source);
1857 #endif /* WITH_FULL_PATHS */
1859 if (operation == OP_DELETE) {
1860 if (S_ISDIR (src_stat.st_mode))
1861 value = erase_dir (ctx, source_with_path, &count, &bytes);
1862 else
1863 value =
1864 erase_file (ctx, source_with_path, &count, &bytes, 1);
1865 } else {
1866 temp = transform_source (ctx, source_with_path);
1868 if (temp == NULL) {
1869 value = transform_error;
1870 } else {
1871 char *temp2 = concat_dir_and_file (dest, temp);
1872 g_free (dest);
1873 dest = temp2;
1874 temp = NULL;
1876 source_with_path = unescape_string(source_with_path);
1877 dest = unescape_string(dest);
1878 switch (operation) {
1879 case OP_COPY:
1881 * we use file_mask_op_follow_links only with OP_COPY,
1883 (*ctx->stat_func) (source_with_path, &src_stat);
1885 if (S_ISDIR (src_stat.st_mode))
1886 value =
1887 copy_dir_dir (ctx, source_with_path, dest, 1,
1888 0, 0, 0, &count, &bytes);
1889 else
1890 value =
1891 copy_file_file (ctx, source_with_path, dest, 1,
1892 &count, &bytes, 1);
1893 break;
1895 case OP_MOVE:
1896 if (S_ISDIR (src_stat.st_mode))
1897 value =
1898 move_dir_dir (ctx, source_with_path, dest,
1899 &count, &bytes);
1900 else
1901 value =
1902 move_file_file (ctx, source_with_path, dest,
1903 &count, &bytes);
1904 break;
1906 default:
1907 /* Unknown file operation */
1908 abort ();
1911 } /* Copy or move operation */
1913 if ((value == FILE_CONT) && !force_single)
1914 unmark_files (panel);
1915 } else {
1916 /* Many files */
1917 /* Check destination for copy or move operation */
1918 if (operation != OP_DELETE) {
1919 retry_many_dst_stat:
1920 dst_result = mc_stat (dest, &dst_stat);
1921 if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)) {
1922 if (file_error
1923 (_(" Destination \"%s\" must be a directory \n %s "),
1924 dest) == FILE_RETRY)
1925 goto retry_many_dst_stat;
1926 goto clean_up;
1930 /* Initialize variables for progress bars */
1931 if (operation != OP_MOVE && verbose && file_op_compute_totals) {
1932 panel_compute_totals (panel, &ctx->progress_count,
1933 &ctx->progress_bytes);
1934 ctx->progress_totals_computed = 1;
1935 } else {
1936 ctx->progress_totals_computed = 0;
1937 ctx->progress_count = panel->marked;
1938 ctx->progress_bytes = panel->total;
1941 /* Loop for every file, perform the actual copy operation */
1942 for (i = 0; i < panel->count; i++) {
1943 if (!panel->dir.list[i].f.marked)
1944 continue; /* Skip the unmarked ones */
1946 source = panel->dir.list[i].fname;
1947 src_stat = panel->dir.list[i].st;
1949 #ifdef WITH_FULL_PATHS
1950 g_free (source_with_path);
1951 source_with_path = concat_dir_and_file (panel->cwd, source);
1952 #endif /* WITH_FULL_PATHS */
1954 if (operation == OP_DELETE) {
1955 if (S_ISDIR (src_stat.st_mode))
1956 value =
1957 erase_dir (ctx, source_with_path, &count, &bytes);
1958 else
1959 value =
1960 erase_file (ctx, source_with_path, &count, &bytes,
1962 } else {
1963 temp = transform_source (ctx, source_with_path);
1964 if (temp == NULL)
1965 value = transform_error;
1966 else {
1967 char *temp2 = concat_dir_and_file (dest, temp);
1969 source_with_path = unescape_string(source_with_path);
1970 temp2 = unescape_string(temp2);
1972 switch (operation) {
1973 case OP_COPY:
1975 * we use file_mask_op_follow_links only with OP_COPY
1977 (*ctx->stat_func) (source_with_path, &src_stat);
1978 if (S_ISDIR (src_stat.st_mode))
1979 value =
1980 copy_dir_dir (ctx, source_with_path, temp2,
1981 1, 0, 0, 0, &count, &bytes);
1982 else
1983 value =
1984 copy_file_file (ctx, source_with_path,
1985 temp2, 1, &count, &bytes,
1987 free_linklist (&dest_dirs);
1988 break;
1990 case OP_MOVE:
1991 if (S_ISDIR (src_stat.st_mode))
1992 value =
1993 move_dir_dir (ctx, source_with_path, temp2,
1994 &count, &bytes);
1995 else
1996 value =
1997 move_file_file (ctx, source_with_path,
1998 temp2, &count, &bytes);
1999 break;
2001 default:
2002 /* Unknown file operation */
2003 abort ();
2005 g_free (temp2);
2007 } /* Copy or move operation */
2009 if (value == FILE_ABORT)
2010 goto clean_up;
2012 if (value == FILE_CONT)
2013 do_file_mark (panel, i, 0);
2015 if (file_progress_show_count (ctx, count, ctx->progress_count)
2016 == FILE_ABORT)
2017 goto clean_up;
2019 if (verbose
2020 && file_progress_show_bytes (ctx, bytes,
2021 ctx->progress_bytes) ==
2022 FILE_ABORT)
2023 goto clean_up;
2025 if (operation != OP_DELETE && verbose
2026 && file_progress_show (ctx, 0, 0) == FILE_ABORT)
2027 goto clean_up;
2029 mc_refresh ();
2030 } /* Loop for every file */
2031 } /* Many entries */
2032 clean_up:
2033 /* Clean up */
2035 if (save_cwd) {
2036 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2037 g_free (save_cwd);
2039 if (save_dest) {
2040 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2041 g_free (save_dest);
2044 free_linklist (&linklist);
2045 free_linklist (&dest_dirs);
2046 #ifdef WITH_FULL_PATHS
2047 g_free (source_with_path);
2048 #endif /* WITH_FULL_PATHS */
2049 g_free (dest);
2050 g_free (ctx->dest_mask);
2051 ctx->dest_mask = NULL;
2052 #ifdef WITH_BACKGROUND
2053 /* Let our parent know we are saying bye bye */
2054 if (we_are_background) {
2055 vfs_shut ();
2056 _exit (0);
2058 #endif /* WITH_BACKGROUND */
2060 file_op_context_destroy (ctx);
2061 return 1;
2064 /* }}} */
2066 /* {{{ Query/status report routines */
2068 static int
2069 real_do_file_error (enum OperationMode mode, const char *error)
2071 int result;
2072 const char *msg;
2074 msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
2075 result =
2076 query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"),
2077 _("&Abort"));
2079 switch (result) {
2080 case 0:
2081 do_refresh ();
2082 return FILE_SKIP;
2084 case 1:
2085 do_refresh ();
2086 return FILE_RETRY;
2088 case 2:
2089 default:
2090 return FILE_ABORT;
2094 /* Report error with one file */
2096 file_error (const char *format, const char *file)
2098 g_snprintf (cmd_buf, sizeof (cmd_buf), format,
2099 path_trunc (file, 30), unix_error_string (errno));
2101 return do_file_error (cmd_buf);
2104 /* Report error with two files */
2105 static int
2106 files_error (const char *format, const char *file1, const char *file2)
2108 char nfile1[16];
2109 char nfile2[16];
2111 strcpy (nfile1, path_trunc (file1, 15));
2112 strcpy (nfile2, path_trunc (file2, 15));
2114 g_snprintf (cmd_buf, sizeof (cmd_buf), format, nfile1, nfile2,
2115 unix_error_string (errno));
2117 return do_file_error (cmd_buf);
2120 static int
2121 real_query_recursive (FileOpContext *ctx, enum OperationMode mode, const char *s)
2123 gchar *text;
2125 if (ctx->recursive_result < RECURSIVE_ALWAYS) {
2126 const char *msg =
2127 mode ==
2128 Foreground ?
2129 _("\n Directory not empty. \n"
2130 " Delete it recursively? ")
2131 : _("\n Background process: Directory not empty \n"
2132 " Delete it recursively? ");
2133 text = g_strconcat (_(" Delete: "), path_trunc (s, 30), " ", (char *) NULL);
2135 if (safe_delete)
2136 query_set_sel (1);
2137 ctx->recursive_result = query_dialog (text, msg, D_ERROR, 5,
2138 _("&Yes"), _("&No"),
2139 _("A&ll"), _("Non&e"),
2140 _("&Abort"));
2142 if (ctx->recursive_result != RECURSIVE_ABORT)
2143 do_refresh ();
2144 g_free (text);
2147 switch (ctx->recursive_result) {
2148 case RECURSIVE_YES:
2149 case RECURSIVE_ALWAYS:
2150 return FILE_CONT;
2152 case RECURSIVE_NO:
2153 case RECURSIVE_NEVER:
2154 return FILE_SKIP;
2156 case RECURSIVE_ABORT:
2158 default:
2159 return FILE_ABORT;
2163 #ifdef WITH_BACKGROUND
2164 static int
2165 do_file_error (const char *str)
2167 if (we_are_background)
2168 return parent_call (real_do_file_error, NULL, 1, strlen (str),
2169 str);
2170 else
2171 return real_do_file_error (Foreground, str);
2174 static int
2175 query_recursive (FileOpContext *ctx, const char *s)
2177 if (we_are_background)
2178 return parent_call (real_query_recursive, ctx, 1, strlen (s), s);
2179 else
2180 return real_query_recursive (ctx, Foreground, s);
2183 static int
2184 query_replace (FileOpContext *ctx, const char *destname, struct stat *_s_stat,
2185 struct stat *_d_stat)
2187 if (we_are_background)
2188 return parent_call ((void *) file_progress_real_query_replace,
2189 ctx,
2191 strlen (destname), destname,
2192 sizeof (struct stat), _s_stat,
2193 sizeof (struct stat), _d_stat);
2194 else
2195 return file_progress_real_query_replace (ctx, Foreground, destname,
2196 _s_stat, _d_stat);
2199 #else
2200 static int
2201 do_file_error (const char *str)
2203 return real_do_file_error (Foreground, str);
2206 static int
2207 query_recursive (FileOpContext *ctx, const char *s)
2209 return real_query_recursive (ctx, Foreground, s);
2212 static int
2213 query_replace (FileOpContext *ctx, const char *destname, struct stat *_s_stat,
2214 struct stat *_d_stat)
2216 return file_progress_real_query_replace (ctx, Foreground, destname,
2217 _s_stat, _d_stat);
2220 #endif /* !WITH_BACKGROUND */
2223 Cause emacs to enter folding mode for this file:
2224 Local variables:
2225 end: