Not only comment it out but removing it
[midnight-commander.git] / src / file.c
bloba19633a410338b0d47073f0df3704957e6d122bf
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 */
67 /* Needed for current_panel, other_panel and WTree */
68 #include "dir.h"
69 #include "panel.h"
70 #include "file.h"
71 #include "filegui.h"
72 #include "tree.h"
73 #include "key.h"
74 #include "../vfs/vfs-impl.h"
76 /* }}} */
78 /* Hack: the vfs code should not rely on this */
79 #define WITH_FULL_PATHS 1
81 int verbose = 1;
84 * Whether the Midnight Commander tries to provide more
85 * information about copy/move sizes and bytes transfered
86 * at the expense of some speed
88 int file_op_compute_totals = 1;
90 /* This is a hard link cache */
91 struct link {
92 struct link *next;
93 struct vfs_class *vfs;
94 dev_t dev;
95 ino_t ino;
96 short linkcount;
97 mode_t st_mode;
98 char name[1];
101 /* the hard link cache */
102 static struct link *linklist = NULL;
104 /* the files-to-be-erased list */
105 static struct link *erase_list;
108 * In copy_dir_dir we use two additional single linked lists: The first -
109 * variable name `parent_dirs' - holds information about already copied
110 * directories and is used to detect cyclic symbolic links.
111 * The second (`dest_dirs' below) holds information about just created
112 * target directories and is used to detect when an directory is copied
113 * into itself (we don't want to copy infinitly).
114 * Both lists don't use the linkcount and name structure members of struct
115 * link.
117 static struct link *dest_dirs = 0;
119 const char *op_names[3] = {
120 N_(" Copy "),
121 N_(" Move "),
122 N_(" Delete ")
125 /* }}} */
127 static int query_replace (FileOpContext * ctx, const char *destname,
128 struct stat *_s_stat, struct stat *_d_stat);
129 static int query_recursive (FileOpContext * ctx, const char *s);
130 static int do_file_error (const char *str);
131 static int erase_dir_iff_empty (FileOpContext *ctx, const char *s);
132 static int erase_file (FileOpContext *ctx, const char *s,
133 off_t *progress_count, double *progress_bytes,
134 int is_toplevel_file);
135 static int files_error (const char *format, const char *file1,
136 const char *file2);
139 enum CaseConvs { NO_CONV = 0, UP_CHAR = 1, LOW_CHAR = 2, UP_SECT =
140 4, LOW_SECT = 8 };
142 static char
143 convert_case (char c, enum CaseConvs *conversion)
145 if (*conversion & UP_CHAR) {
146 *conversion &= ~UP_CHAR;
147 return toupper ((unsigned char) c);
148 } else if (*conversion & LOW_CHAR) {
149 *conversion &= ~LOW_CHAR;
150 return tolower ((unsigned char) c);
151 } else if (*conversion & UP_SECT) {
152 return toupper ((unsigned char) c);
153 } else if (*conversion & LOW_SECT) {
154 return tolower ((unsigned char) c);
155 } else
156 return c;
159 static int transform_error = 0;
161 static const char *
162 do_transform_source (FileOpContext *ctx, const char *source)
164 size_t j, k, l, len;
165 const char *fnsource = x_basename (source);
166 int next_reg;
167 enum CaseConvs case_conv = NO_CONV;
168 static char fntarget[MC_MAXPATHLEN];
170 len = strlen (fnsource);
171 j = re_match (&ctx->rx, fnsource, len, 0, &ctx->regs);
172 if (j != len) {
173 transform_error = FILE_SKIP;
174 return NULL;
176 for (next_reg = 1, j = 0, k = 0; j < strlen (ctx->dest_mask); j++) {
177 switch (ctx->dest_mask[j]) {
178 case '\\':
179 j++;
180 if (!isdigit ((unsigned char) ctx->dest_mask[j])) {
181 /* Backslash followed by non-digit */
182 switch (ctx->dest_mask[j]) {
183 case 'U':
184 case_conv |= UP_SECT;
185 case_conv &= ~LOW_SECT;
186 break;
187 case 'u':
188 case_conv |= UP_CHAR;
189 break;
190 case 'L':
191 case_conv |= LOW_SECT;
192 case_conv &= ~UP_SECT;
193 break;
194 case 'l':
195 case_conv |= LOW_CHAR;
196 break;
197 case 'E':
198 case_conv = NO_CONV;
199 break;
200 default:
201 /* Backslash as quote mark */
202 fntarget[k++] =
203 convert_case (ctx->dest_mask[j], &case_conv);
205 break;
206 } else {
207 /* Backslash followed by digit */
208 next_reg = ctx->dest_mask[j] - '0';
209 /* Fall through */
212 case '*':
213 if (next_reg < 0 || next_reg >= RE_NREGS
214 || ctx->regs.start[next_reg] < 0) {
215 message (1, MSG_ERROR, _(" Invalid target mask "));
216 transform_error = FILE_ABORT;
217 return NULL;
219 for (l = (size_t) ctx->regs.start[next_reg];
220 l < (size_t) ctx->regs.end[next_reg]; l++)
221 fntarget[k++] = convert_case (fnsource[l], &case_conv);
222 next_reg++;
223 break;
225 default:
226 fntarget[k++] = convert_case (ctx->dest_mask[j], &case_conv);
227 break;
230 fntarget[k] = 0;
231 return fntarget;
234 static const char *
235 transform_source (FileOpContext *ctx, const char *source)
237 char *s = g_strdup (source);
238 char *q;
239 const char *p;
241 /* We remove \n from the filename since regex routines would use \n as an anchor */
242 /* this is just to be allowed to maniupulate file names with \n on it */
243 for (q = s; *q; q++) {
244 if (*q == '\n')
245 *q = ' ';
247 p = do_transform_source (ctx, s);
248 g_free (s);
249 return p;
252 static void
253 free_linklist (struct link **linklist)
255 struct link *lp, *lp2;
257 for (lp = *linklist; lp != NULL; lp = lp2) {
258 lp2 = lp->next;
259 g_free (lp);
261 *linklist = NULL;
264 static int
265 is_in_linklist (struct link *lp, const char *path, struct stat *sb)
267 ino_t ino = sb->st_ino;
268 dev_t dev = sb->st_dev;
269 #ifdef USE_VFS
270 struct vfs_class *vfs = vfs_get_class (path);
271 #endif /* USE_VFS */
273 while (lp) {
274 #ifdef USE_VFS
275 if (lp->vfs == vfs)
276 #endif /* USE_VFS */
277 if (lp->ino == ino && lp->dev == dev)
278 return 1;
279 lp = lp->next;
281 return 0;
285 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
286 * and a hardlink was succesfully made
288 static int
289 check_hardlinks (const char *src_name, const char *dst_name, struct stat *pstat)
291 struct link *lp;
292 struct vfs_class *my_vfs = vfs_get_class (src_name);
293 ino_t ino = pstat->st_ino;
294 dev_t dev = pstat->st_dev;
295 struct stat link_stat;
296 const char *p;
298 if (vfs_file_class_flags (src_name) & VFSF_NOLINKS)
299 return 0;
301 for (lp = linklist; lp != NULL; lp = lp->next)
302 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev) {
303 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
304 && link_stat.st_dev == dev
305 && vfs_get_class (lp->name) == my_vfs) {
306 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
307 was copied to */
308 if (vfs_get_class (dst_name) == vfs_get_class (p)) {
309 if (!mc_stat (p, &link_stat)) {
310 if (!mc_link (p, dst_name))
311 return 1;
315 message (1, MSG_ERROR, _(" Cannot make the hardlink "));
316 return 0;
318 lp = (struct link *) g_malloc (sizeof (struct link) + strlen (src_name)
319 + strlen (dst_name) + 1);
320 if (lp) {
321 char *lpdstname;
322 lp->vfs = my_vfs;
323 lp->ino = ino;
324 lp->dev = dev;
325 strcpy (lp->name, src_name);
326 lpdstname = lp->name + strlen(lp->name) + 1;
327 strcpy (lpdstname, dst_name);
328 lp->next = linklist;
329 linklist = lp;
331 return 0;
335 * Duplicate the contents of the symbolic link src_path in dst_path.
336 * Try to make a stable symlink if the option "stable symlink" was
337 * set in the file mask dialog.
338 * If dst_path is an existing symlink it will be deleted silently
339 * (upper levels take already care of existing files at dst_path).
341 static int
342 make_symlink (FileOpContext *ctx, const char *src_path, const char *dst_path)
344 char link_target[MC_MAXPATHLEN];
345 int len;
346 int return_status;
347 struct stat sb;
348 int dst_is_symlink;
350 if (mc_lstat (dst_path, &sb) == 0 && S_ISLNK (sb.st_mode))
351 dst_is_symlink = 1;
352 else
353 dst_is_symlink = 0;
355 retry_src_readlink:
356 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1);
357 if (len < 0) {
358 return_status =
359 file_error (_(" Cannot read source link \"%s\" \n %s "),
360 src_path);
361 if (return_status == FILE_RETRY)
362 goto retry_src_readlink;
363 return return_status;
365 link_target[len] = 0;
367 if (ctx->stable_symlinks)
368 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path)) {
369 message (1, MSG_ERROR,
370 _(" Cannot make stable symlinks across "
371 "non-local filesystems: \n\n"
372 " Option Stable Symlinks will be disabled "));
373 ctx->stable_symlinks = 0;
376 if (ctx->stable_symlinks && *link_target != PATH_SEP) {
377 char *p, *q, *s;
379 const char *r = strrchr (src_path, PATH_SEP);
381 if (r) {
382 p = g_strndup (src_path, r - src_path + 1);
383 if (*dst_path == PATH_SEP)
384 q = g_strdup (dst_path);
385 else
386 q = g_strconcat (p, dst_path, (char *) NULL);
387 s = strrchr (q, PATH_SEP);
388 if (s) {
389 s[1] = 0;
390 s = g_strconcat (p, link_target, (char *) NULL);
391 g_free (p);
392 g_strlcpy (link_target, s, sizeof (link_target));
393 g_free (s);
394 s = diff_two_paths (q, link_target);
395 if (s) {
396 g_strlcpy (link_target, s, sizeof (link_target));
397 g_free (s);
399 } else
400 g_free (p);
401 g_free (q);
404 retry_dst_symlink:
405 if (mc_symlink (link_target, dst_path) == 0)
406 /* Success */
407 return FILE_CONT;
409 * if dst_exists, it is obvious that this had failed.
410 * We can delete the old symlink and try again...
412 if (dst_is_symlink) {
413 if (!mc_unlink (dst_path))
414 if (mc_symlink (link_target, dst_path) == 0)
415 /* Success */
416 return FILE_CONT;
418 return_status =
419 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
420 dst_path);
421 if (return_status == FILE_RETRY)
422 goto retry_dst_symlink;
423 return return_status;
426 static int
427 progress_update_one (FileOpContext *ctx,
428 off_t *progress_count,
429 double *progress_bytes, off_t add, int is_toplevel_file)
431 int ret;
433 if (is_toplevel_file || ctx->progress_totals_computed) {
434 (*progress_count)++;
435 (*progress_bytes) += add;
438 /* Apply some heuristic here to not call the update stuff very often */
439 ret =
440 file_progress_show_count (ctx, *progress_count,
441 ctx->progress_count);
442 if (ret != FILE_CONT)
443 return ret;
444 ret =
445 file_progress_show_bytes (ctx, *progress_bytes,
446 ctx->progress_bytes);
448 return ret;
451 /* Status of the destination file */
452 enum {
453 DEST_NONE, /* Not created */
454 DEST_SHORT, /* Created, not fully copied */
455 DEST_FULL /* Created, fully copied */
459 copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path,
460 int ask_overwrite, off_t *progress_count,
461 double *progress_bytes, int is_toplevel_file)
463 uid_t src_uid = (uid_t) - 1;
464 gid_t src_gid = (gid_t) - 1;
466 char *buf = NULL;
467 int buf_size = BUF_8K;
468 int src_desc, dest_desc = -1;
469 int n_read, n_written;
470 mode_t src_mode = 0; /* The mode of the source file */
471 struct stat sb, sb2;
472 struct utimbuf utb;
473 int dst_exists = 0, appending = 0;
474 off_t n_read_total = 0, file_size = -1;
475 int return_status, temp_status;
476 struct timeval tv_transfer_start;
477 int dst_status = DEST_NONE; /* 1 if the file is not fully copied */
479 /* FIXME: We should not be using global variables! */
480 ctx->do_reget = 0;
481 return_status = FILE_RETRY;
483 if (file_progress_show_source (ctx, src_path) == FILE_ABORT ||
484 file_progress_show_target (ctx, dst_path) == FILE_ABORT)
485 return FILE_ABORT;
487 mc_refresh ();
489 while (mc_stat (dst_path, &sb2) == 0) {
490 if (S_ISDIR (sb2.st_mode)) {
491 return_status =
492 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
493 dst_path);
494 if (return_status == FILE_RETRY)
495 continue;
496 return return_status;
498 dst_exists = 1;
499 break;
502 while ((*ctx->stat_func) (src_path, &sb)) {
503 return_status =
504 file_error (_(" Cannot stat source file \"%s\" \n %s "),
505 src_path);
506 if (return_status != FILE_RETRY)
507 return return_status;
510 if (dst_exists) {
511 /* Destination already exists */
512 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino) {
513 message (1, MSG_ERROR,
514 _(" `%s' and `%s' are the same file "), src_path, dst_path);
515 do_refresh ();
516 return FILE_SKIP;
519 /* Should we replace destination? */
520 if (ask_overwrite) {
521 ctx->do_reget = 0;
522 return_status = query_replace (ctx, dst_path, &sb, &sb2);
523 if (return_status != FILE_CONT)
524 return return_status;
528 if (!ctx->do_append) {
529 /* Check the hardlinks */
530 if (!ctx->follow_links && sb.st_nlink > 1 &&
531 check_hardlinks (src_path, dst_path, &sb) == 1) {
532 /* We have made a hardlink - no more processing is necessary */
533 return FILE_CONT;
536 if (S_ISLNK (sb.st_mode)) {
537 int retval;
539 retval = make_symlink (ctx, src_path, dst_path);
540 return retval;
543 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) ||
544 S_ISFIFO (sb.st_mode) || S_ISNAM (sb.st_mode) ||
545 S_ISSOCK (sb.st_mode)) {
546 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill,
547 sb.st_rdev) < 0) {
548 return_status = file_error (
549 _(" Cannot create special file \"%s\" \n %s "), dst_path);
550 if (return_status == FILE_RETRY)
551 continue;
552 return return_status;
554 /* Success */
556 while (ctx->preserve_uidgid
557 && mc_chown (dst_path, sb.st_uid, sb.st_gid)) {
558 temp_status = file_error (
559 _(" Cannot chown target file \"%s\" \n %s "), dst_path);
560 if (temp_status == FILE_RETRY)
561 continue;
562 return temp_status;
564 while (ctx->preserve &&
565 mc_chmod (dst_path, sb.st_mode & ctx->umask_kill)) {
566 temp_status = file_error (
567 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
568 if (temp_status == FILE_RETRY)
569 continue;
570 return temp_status;
572 return FILE_CONT;
576 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
578 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0) {
579 return_status = file_error (
580 _(" Cannot open source file \"%s\" \n %s "), src_path);
581 if (return_status == FILE_RETRY)
582 continue;
583 ctx->do_append = 0;
584 return return_status;
587 if (ctx->do_reget) {
588 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget) {
589 message (1, _("Warning"),
590 _(" Reget failed, about to overwrite file "));
591 ctx->do_reget = ctx->do_append = 0;
595 while (mc_fstat (src_desc, &sb)) {
596 return_status = file_error (
597 _(" Cannot fstat source file \"%s\" \n %s "), src_path);
598 if (return_status == FILE_RETRY)
599 continue;
600 ctx->do_append = 0;
601 goto ret;
603 src_mode = sb.st_mode;
604 src_uid = sb.st_uid;
605 src_gid = sb.st_gid;
606 utb.actime = sb.st_atime;
607 utb.modtime = sb.st_mtime;
608 file_size = sb.st_size;
610 /* Create the new regular file with small permissions initially,
611 do not create a security hole. FIXME: You have security hole
612 here, btw. Imagine copying to /tmp and symlink attack :-( */
614 while ((dest_desc = mc_open (dst_path, O_WRONLY | (ctx->do_append ?
615 O_APPEND : (O_CREAT | O_TRUNC)), 0600)) < 0) {
616 return_status = file_error (
617 _(" Cannot create target file \"%s\" \n %s "), dst_path);
618 if (return_status == FILE_RETRY)
619 continue;
620 ctx->do_append = 0;
621 goto ret;
623 dst_status = DEST_SHORT; /* file opened, but not fully copied */
625 appending = ctx->do_append;
626 ctx->do_append = 0;
628 /* Find out the optimal buffer size. */
629 while (mc_fstat (dest_desc, &sb)) {
630 return_status = file_error (
631 _(" Cannot fstat target file \"%s\" \n %s "), dst_path);
632 if (return_status == FILE_RETRY)
633 continue;
634 goto ret;
636 buf = g_malloc (buf_size);
638 ctx->eta_secs = 0.0;
639 ctx->bps = 0;
641 return_status = file_progress_show (ctx, 0, file_size);
643 mc_refresh ();
645 if (return_status != FILE_CONT)
646 goto ret;
649 struct timeval tv_current, tv_last_update, tv_last_input;
650 int secs, update_secs;
651 long dt;
652 const char *stalled_msg;
654 tv_last_update = tv_transfer_start;
656 for (;;) {
657 /* src_read */
658 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0))
659 n_read = -1;
660 else
661 while ((n_read = mc_read (src_desc, buf, buf_size)) < 0) {
662 return_status = file_error (
663 _(" Cannot read source file \"%s\" \n %s "), src_path);
664 if (return_status == FILE_RETRY)
665 continue;
666 goto ret;
668 if (n_read == 0)
669 break;
671 gettimeofday (&tv_current, NULL);
673 if (n_read > 0) {
674 char *t = buf;
675 n_read_total += n_read;
677 /* Windows NT ftp servers report that files have no
678 * permissions: -------, so if we happen to have actually
679 * read something, we should fix the permissions.
681 if (!(src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)))
682 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
683 gettimeofday (&tv_last_input, NULL);
685 /* dst_write */
686 while ((n_written =
687 mc_write (dest_desc, t, n_read)) < n_read) {
688 if (n_written > 0) {
689 n_read -= n_written;
690 t += n_written;
691 continue;
693 return_status =
694 file_error (_(" Cannot write target file \"%s\" \n %s "),
695 dst_path);
696 if (return_status != FILE_RETRY)
697 goto ret;
701 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
702 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
703 if (secs > 2) {
704 rotate_dash ();
705 tv_last_update = tv_current;
708 /* 2. Check for a stalled condition */
709 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
710 stalled_msg = "";
711 if (update_secs > 4) {
712 stalled_msg = _("(stalled)");
715 /* 3. Compute ETA */
716 if (secs > 2) {
717 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
719 if (n_read_total) {
720 ctx->eta_secs =
721 ((dt / (double) n_read_total) * file_size) - dt;
722 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
723 } else
724 ctx->eta_secs = 0.0;
727 /* 4. Compute BPS rate */
728 if (secs > 2) {
729 ctx->bps_time =
730 (tv_current.tv_sec - tv_transfer_start.tv_sec);
731 if (ctx->bps_time < 1)
732 ctx->bps_time = 1;
733 ctx->bps = n_read_total / ctx->bps_time;
736 file_progress_set_stalled_label (ctx, stalled_msg);
737 return_status = file_progress_show_bytes (ctx, *progress_bytes +
738 n_read_total + ctx->do_reget, ctx->progress_bytes);
739 if (return_status == FILE_CONT) {
740 return_status =
741 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size);
743 mc_refresh ();
744 if (return_status != FILE_CONT)
745 goto ret;
749 dst_status = DEST_FULL; /* copy successful, don't remove target file */
751 ret:
752 g_free (buf);
754 while (src_desc != -1 && mc_close (src_desc) < 0) {
755 temp_status = file_error (
756 _(" Cannot close source file \"%s\" \n %s "), src_path);
757 if (temp_status == FILE_RETRY)
758 continue;
759 if (temp_status == FILE_ABORT)
760 return_status = temp_status;
761 break;
764 while (dest_desc != -1 && mc_close (dest_desc) < 0) {
765 temp_status = file_error (
766 _(" Cannot close target file \"%s\" \n %s "), dst_path);
767 if (temp_status == FILE_RETRY)
768 continue;
769 return_status = temp_status;
770 break;
773 if (dst_status == DEST_SHORT) {
774 /* Remove short file */
775 int result;
776 result = query_dialog (_("Copy"),
777 _("Incomplete file was retrieved. Keep it?"),
778 D_ERROR, 2, _("&Delete"), _("&Keep"));
779 if (!result)
780 mc_unlink (dst_path);
781 } else if (dst_status == DEST_FULL) {
782 /* Copy has succeeded */
783 if (!appending && ctx->preserve_uidgid) {
784 while (mc_chown (dst_path, src_uid, src_gid)) {
785 temp_status = file_error (
786 _(" Cannot chown target file \"%s\" \n %s "), dst_path);
787 if (temp_status == FILE_RETRY)
788 continue;
789 return_status = temp_status;
790 break;
794 if (!appending) {
795 while (mc_chmod (dst_path, (src_mode & ctx->umask_kill))) {
796 temp_status = file_error (
797 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
798 if (temp_status != FILE_RETRY) {
799 return_status = temp_status;
800 break;
803 mc_utime (dst_path, &utb);
807 if (return_status == FILE_CONT)
808 return_status =
809 progress_update_one (ctx, progress_count, progress_bytes,
810 file_size, is_toplevel_file);
812 return return_status;
816 * I think these copy_*_* functions should have a return type.
817 * anyway, this function *must* have two directories as arguments.
819 /* FIXME: This function needs to check the return values of the
820 function calls */
822 copy_dir_dir (FileOpContext *ctx, const char *s, const char *d, int toplevel,
823 int move_over, int delete, struct link *parent_dirs,
824 off_t *progress_count, double *progress_bytes)
826 struct dirent *next;
827 struct stat buf, cbuf;
828 DIR *reading;
829 char *path, *mdpath, *dest_file, *dest_dir;
830 int return_status = FILE_CONT;
831 struct utimbuf utb;
832 struct link *lp;
834 /* First get the mode of the source dir */
835 retry_src_stat:
836 if ((*ctx->stat_func) (s, &cbuf)) {
837 return_status =
838 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s);
839 if (return_status == FILE_RETRY)
840 goto retry_src_stat;
841 return return_status;
844 if (is_in_linklist (dest_dirs, s, &cbuf)) {
845 /* Don't copy a directory we created before (we don't want to copy
846 infinitely if a directory is copied into itself) */
847 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
848 return FILE_CONT;
851 /* Hmm, hardlink to directory??? - Norbert */
852 /* FIXME: In this step we should do something
853 in case the destination already exist */
854 /* Check the hardlinks */
855 if (ctx->preserve && cbuf.st_nlink > 1
856 && check_hardlinks (s, d, &cbuf) == 1) {
857 /* We have made a hardlink - no more processing is necessary */
858 return return_status;
861 if (!S_ISDIR (cbuf.st_mode)) {
862 return_status =
863 file_error (_(" Source \"%s\" is not a directory \n %s "), s);
864 if (return_status == FILE_RETRY)
865 goto retry_src_stat;
866 return return_status;
869 if (is_in_linklist (parent_dirs, s, &cbuf)) {
870 /* we found a cyclic symbolic link */
871 message (1, MSG_ERROR,
872 _(" Cannot copy cyclic symbolic link \n `%s' "), s);
873 return FILE_SKIP;
876 lp = g_new (struct link, 1);
877 lp->vfs = vfs_get_class (s);
878 lp->ino = cbuf.st_ino;
879 lp->dev = cbuf.st_dev;
880 lp->next = parent_dirs;
881 parent_dirs = lp;
883 retry_dst_stat:
884 /* Now, check if the dest dir exists, if not, create it. */
885 if (mc_stat (d, &buf)) {
886 /* Here the dir doesn't exist : make it ! */
888 if (move_over) {
889 if (mc_rename (s, d) == 0) {
890 g_free (parent_dirs);
891 return FILE_CONT;
894 dest_dir = g_strdup (d);
895 } else {
897 * If the destination directory exists, we want to copy the whole
898 * directory, but we only want this to happen once.
900 * Escape sequences added to the * to compiler warnings.
901 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
902 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
904 if (!S_ISDIR (buf.st_mode)) {
905 return_status = file_error(
906 _(" Destination \"%s\" must be a directory \n %s "), d);
907 if (return_status == FILE_RETRY)
908 goto retry_dst_stat;
909 g_free (parent_dirs);
910 return return_status;
912 /* Dive into subdir if exists */
913 if (toplevel && ctx->dive_into_subdirs) {
914 dest_dir = concat_dir_and_file (d, x_basename (s));
915 } else {
916 dest_dir = g_strdup (d);
917 goto dont_mkdir;
920 while (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU)) {
921 return_status = file_error (
922 _(" Cannot create target directory \"%s\" \n %s "), dest_dir);
923 if (return_status != FILE_RETRY)
924 goto ret;
927 lp = g_new (struct link, 1);
928 mc_stat (dest_dir, &buf);
929 lp->vfs = vfs_get_class (dest_dir);
930 lp->ino = buf.st_ino;
931 lp->dev = buf.st_dev;
932 lp->next = dest_dirs;
933 dest_dirs = lp;
935 if (ctx->preserve_uidgid) {
936 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid)) {
937 return_status = file_error (
938 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir);
939 if (return_status != FILE_RETRY)
940 goto ret;
944 dont_mkdir:
945 /* open the source dir for reading */
946 if ((reading = mc_opendir (s)) == 0) {
947 goto ret;
950 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT) {
952 * Now, we don't want '.' and '..' to be created / copied at any time
954 if (!strcmp (next->d_name, "."))
955 continue;
956 if (!strcmp (next->d_name, ".."))
957 continue;
959 /* get the filename and add it to the src directory */
960 path = concat_dir_and_file (s, next->d_name);
962 (*ctx->stat_func) (path, &buf);
963 if (S_ISDIR (buf.st_mode)) {
964 mdpath = concat_dir_and_file (dest_dir, next->d_name);
966 * From here, we just intend to recursively copy subdirs, not
967 * the double functionality of copying different when the target
968 * dir already exists. So, we give the recursive call the flag 0
969 * meaning no toplevel.
971 return_status = copy_dir_dir (ctx, path, mdpath, 0, 0, delete,
972 parent_dirs, progress_count, progress_bytes);
973 g_free (mdpath);
974 } else {
975 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
976 return_status = copy_file_file (ctx, path, dest_file, 1,
977 progress_count, progress_bytes, 0);
978 g_free (dest_file);
980 if (delete && return_status == FILE_CONT) {
981 if (ctx->erase_at_end) {
982 static struct link *tail;
983 lp = g_malloc (sizeof (struct link) + strlen (path));
984 strcpy (lp->name, path);
985 lp->st_mode = buf.st_mode;
986 lp->next = 0;
987 if (erase_list) {
988 tail->next = lp;
989 tail = lp;
990 } else
991 erase_list = tail = lp;
992 } else {
993 if (S_ISDIR (buf.st_mode)) {
994 return_status = erase_dir_iff_empty (ctx, path);
995 } else
996 return_status = erase_file (ctx, path, 0, 0, 0);
999 g_free (path);
1001 mc_closedir (reading);
1003 if (ctx->preserve) {
1004 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1005 utb.actime = cbuf.st_atime;
1006 utb.modtime = cbuf.st_mtime;
1007 mc_utime (dest_dir, &utb);
1010 ret:
1011 g_free (dest_dir);
1012 g_free (parent_dirs);
1013 return return_status;
1016 /* }}} */
1018 /* {{{ Move routines */
1020 static int
1021 move_file_file (FileOpContext *ctx, const char *s, const char *d,
1022 off_t *progress_count, double *progress_bytes)
1024 struct stat src_stats, dst_stats;
1025 int return_status = FILE_CONT;
1026 gboolean copy_done = FALSE;
1028 if (file_progress_show_source (ctx, s) == FILE_ABORT
1029 || file_progress_show_target (ctx, d) == FILE_ABORT)
1030 return FILE_ABORT;
1032 mc_refresh ();
1034 while (mc_lstat (s, &src_stats) != 0) {
1035 /* Source doesn't exist */
1036 return_status =
1037 file_error (_(" Cannot stat file \"%s\" \n %s "), s);
1038 if (return_status != FILE_RETRY)
1039 return return_status;
1042 if (mc_lstat (d, &dst_stats) == 0) {
1043 if (src_stats.st_dev == dst_stats.st_dev
1044 && src_stats.st_ino == dst_stats.st_ino) {
1045 int msize = COLS - 36;
1046 char st[MC_MAXPATHLEN];
1047 char dt[MC_MAXPATHLEN];
1049 if (msize < 0)
1050 msize = 40;
1051 msize /= 2;
1053 strcpy (st, path_trunc (s, msize));
1054 strcpy (dt, path_trunc (d, msize));
1055 message (1, MSG_ERROR,
1056 _(" `%s' and `%s' are the same file "), st, dt);
1057 do_refresh ();
1058 return FILE_SKIP;
1061 if (S_ISDIR (dst_stats.st_mode)) {
1062 message (1, MSG_ERROR,
1063 _(" Cannot overwrite directory `%s' "), d);
1064 do_refresh ();
1065 return FILE_SKIP;
1068 if (confirm_overwrite) {
1069 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1070 if (return_status != FILE_CONT)
1071 return return_status;
1073 /* Ok to overwrite */
1076 if (!ctx->do_append) {
1077 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks) {
1078 if ((return_status = make_symlink (ctx, s, d)) == FILE_CONT) {
1079 goto retry_src_remove;
1080 } else
1081 return return_status;
1084 if (mc_rename (s, d) == 0) {
1085 return progress_update_one (ctx, progress_count,
1086 progress_bytes,
1087 src_stats.st_size, 1);
1090 #if 0
1091 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1092 one nfs to the same, but on the server it is on two different
1093 filesystems. Then nfs returns EIO instead of EXDEV.
1094 Hope it will not hurt if we always in case of error try to copy/delete. */
1095 else
1096 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1098 if (errno != EXDEV) {
1099 return_status =
1100 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s,
1102 if (return_status == FILE_RETRY)
1103 goto retry_rename;
1104 return return_status;
1106 #endif
1108 /* Failed because filesystem boundary -> copy the file instead */
1109 return_status =
1110 copy_file_file (ctx, s, d, 0, progress_count, progress_bytes, 1);
1111 if (return_status != FILE_CONT)
1112 return return_status;
1114 copy_done = TRUE;
1116 if ((return_status =
1117 file_progress_show_source (ctx, NULL)) != FILE_CONT
1118 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1119 return return_status;
1121 mc_refresh ();
1123 retry_src_remove:
1124 if (mc_unlink (s)) {
1125 return_status =
1126 file_error (_(" Cannot remove file \"%s\" \n %s "), s);
1127 if (return_status == FILE_RETRY)
1128 goto retry_src_remove;
1129 return return_status;
1132 if (!copy_done) {
1133 return_status = progress_update_one (ctx,
1134 progress_count,
1135 progress_bytes,
1136 src_stats.st_size, 1);
1139 return return_status;
1143 move_dir_dir (FileOpContext *ctx, const char *s, const char *d,
1144 off_t *progress_count, double *progress_bytes)
1146 struct stat sbuf, dbuf, destbuf;
1147 struct link *lp;
1148 char *destdir;
1149 int return_status;
1150 int move_over = 0;
1152 if (file_progress_show_source (ctx, s) == FILE_ABORT ||
1153 file_progress_show_target (ctx, d) == FILE_ABORT)
1154 return FILE_ABORT;
1156 mc_refresh ();
1158 mc_stat (s, &sbuf);
1159 if (mc_stat (d, &dbuf))
1160 destdir = g_strdup (d); /* destination doesn't exist */
1161 else if (!ctx->dive_into_subdirs) {
1162 destdir = g_strdup (d);
1163 move_over = 1;
1164 } else
1165 destdir = concat_dir_and_file (d, x_basename (s));
1167 if (sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino) {
1168 int msize = COLS - 36;
1169 char st[MC_MAXPATHLEN];
1170 char dt[MC_MAXPATHLEN];
1172 if (msize < 0)
1173 msize = 40;
1174 msize /= 2;
1176 strcpy (st, path_trunc (s, msize));
1177 strcpy (dt, path_trunc (d, msize));
1178 message (1, MSG_ERROR,
1179 _(" `%s' and `%s' are the same directory "), st, dt);
1180 do_refresh ();
1181 return FILE_SKIP;
1184 /* Check if the user inputted an existing dir */
1185 retry_dst_stat:
1186 if (!mc_stat (destdir, &destbuf)) {
1187 if (move_over) {
1188 return_status = copy_dir_dir (ctx, s, destdir, 0, 1, 1, 0,
1189 progress_count, progress_bytes);
1191 if (return_status != FILE_CONT)
1192 goto ret;
1193 goto oktoret;
1194 } else {
1195 if (S_ISDIR (destbuf.st_mode))
1196 return_status =
1197 file_error (_
1198 (" Cannot overwrite directory \"%s\" %s "),
1199 destdir);
1200 else
1201 return_status =
1202 file_error (_(" Cannot overwrite file \"%s\" %s "),
1203 destdir);
1204 if (return_status == FILE_RETRY)
1205 goto retry_dst_stat;
1207 g_free (destdir);
1208 return return_status;
1211 retry_rename:
1212 if (mc_rename (s, destdir) == 0) {
1213 return_status = FILE_CONT;
1214 goto ret;
1217 if (errno != EXDEV) {
1218 return_status =
1219 files_error (_
1220 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1221 s, d);
1222 if (return_status == FILE_RETRY)
1223 goto retry_rename;
1224 goto ret;
1226 /* Failed because of filesystem boundary -> copy dir instead */
1227 return_status =
1228 copy_dir_dir (ctx, s, destdir, 0, 0, 1, 0, progress_count,
1229 progress_bytes);
1231 if (return_status != FILE_CONT)
1232 goto ret;
1233 oktoret:
1234 if ((return_status =
1235 file_progress_show_source (ctx, NULL)) != FILE_CONT
1236 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1237 goto ret;
1239 mc_refresh ();
1240 if (ctx->erase_at_end) {
1241 for (; erase_list && return_status != FILE_ABORT;) {
1242 if (S_ISDIR (erase_list->st_mode)) {
1243 return_status =
1244 erase_dir_iff_empty (ctx, erase_list->name);
1245 } else
1246 return_status =
1247 erase_file (ctx, erase_list->name, 0, 0, 0);
1248 lp = erase_list;
1249 erase_list = erase_list->next;
1250 g_free (lp);
1253 erase_dir_iff_empty (ctx, s);
1255 ret:
1256 g_free (destdir);
1257 while (erase_list) {
1258 lp = erase_list;
1259 erase_list = erase_list->next;
1260 g_free (lp);
1262 return return_status;
1265 /* }}} */
1267 /* {{{ Erase routines */
1268 /* Don't update progress status if progress_count==NULL */
1269 static int
1270 erase_file (FileOpContext *ctx, const char *s, off_t *progress_count,
1271 double *progress_bytes, int is_toplevel_file)
1273 int return_status;
1274 struct stat buf;
1276 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1277 return FILE_ABORT;
1278 mc_refresh ();
1280 if (progress_count && mc_lstat (s, &buf)) {
1281 /* ignore, most likely the mc_unlink fails, too */
1282 buf.st_size = 0;
1285 while (mc_unlink (s)) {
1286 return_status =
1287 file_error (_(" Cannot delete file \"%s\" \n %s "), s);
1288 if (return_status != FILE_RETRY)
1289 return return_status;
1292 if (progress_count)
1293 return progress_update_one (ctx, progress_count, progress_bytes,
1294 buf.st_size, is_toplevel_file);
1295 else
1296 return FILE_CONT;
1299 static int
1300 recursive_erase (FileOpContext *ctx, const char *s, off_t *progress_count,
1301 double *progress_bytes)
1303 struct dirent *next;
1304 struct stat buf;
1305 DIR *reading;
1306 char *path;
1307 int return_status = FILE_CONT;
1309 if (!strcmp (s, ".."))
1310 return 1;
1312 reading = mc_opendir (s);
1314 if (!reading)
1315 return 1;
1317 while ((next = mc_readdir (reading)) && return_status == FILE_CONT) {
1318 if (!strcmp (next->d_name, "."))
1319 continue;
1320 if (!strcmp (next->d_name, ".."))
1321 continue;
1322 path = concat_dir_and_file (s, next->d_name);
1323 if (mc_lstat (path, &buf)) {
1324 g_free (path);
1325 mc_closedir (reading);
1326 return 1;
1328 if (S_ISDIR (buf.st_mode))
1329 return_status =
1330 (recursive_erase
1331 (ctx, path, progress_count, progress_bytes)
1332 != FILE_CONT);
1333 else
1334 return_status =
1335 erase_file (ctx, path, progress_count, progress_bytes, 0);
1336 g_free (path);
1338 mc_closedir (reading);
1339 if (return_status != FILE_CONT)
1340 return return_status;
1341 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1342 return FILE_ABORT;
1343 mc_refresh ();
1345 while (my_rmdir (s)) {
1346 return_status =
1347 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1348 if (return_status != FILE_RETRY)
1349 return return_status;
1352 return FILE_CONT;
1355 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1356 in the directory path points to, 0 else. */
1357 static int
1358 check_dir_is_empty (const char *path)
1360 DIR *dir;
1361 struct dirent *d;
1362 int i;
1364 dir = mc_opendir (path);
1365 if (!dir)
1366 return -1;
1368 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir)) {
1369 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1370 (d->d_name[1] == '.'
1371 && d->d_name[2] == '\0')))
1372 continue; /* "." or ".." */
1373 i = 0;
1374 break;
1377 mc_closedir (dir);
1378 return i;
1382 erase_dir (FileOpContext *ctx, const char *s, off_t *progress_count,
1383 double *progress_bytes)
1385 int error;
1387 if (strcmp (s, "..") == 0)
1388 return FILE_SKIP;
1390 if (strcmp (s, ".") == 0)
1391 return FILE_SKIP;
1393 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1394 return FILE_ABORT;
1395 mc_refresh ();
1397 /* The old way to detect a non empty directory was:
1398 error = my_rmdir (s);
1399 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1400 For the linux user space nfs server (nfs-server-2.2beta29-2)
1401 we would have to check also for EIO. I hope the new way is
1402 fool proof. (Norbert)
1404 error = check_dir_is_empty (s);
1405 if (error == 0) { /* not empty */
1406 error = query_recursive (ctx, s);
1407 if (error == FILE_CONT)
1408 return recursive_erase (ctx, s, progress_count,
1409 progress_bytes);
1410 else
1411 return error;
1414 while (my_rmdir (s) == -1) {
1415 error =
1416 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1417 if (error != FILE_RETRY)
1418 return error;
1421 return FILE_CONT;
1424 static int
1425 erase_dir_iff_empty (FileOpContext *ctx, const char *s)
1427 int error;
1429 if (strcmp (s, "..") == 0)
1430 return FILE_SKIP;
1432 if (strcmp (s, ".") == 0)
1433 return FILE_SKIP;
1435 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1436 return FILE_ABORT;
1437 mc_refresh ();
1439 if (1 != check_dir_is_empty (s)) /* not empty or error */
1440 return FILE_CONT;
1442 while (my_rmdir (s)) {
1443 error =
1444 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1445 if (error != FILE_RETRY)
1446 return error;
1449 return FILE_CONT;
1452 /* }}} */
1454 /* {{{ Panel operate routines */
1457 * Return currently selected entry name or the name of the first marked
1458 * entry if there is one.
1460 static char *
1461 panel_get_file (WPanel *panel, struct stat *stat_buf)
1463 int i;
1465 if (get_current_type () == view_tree) {
1466 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
1467 char *tree_name = tree_selected_name (tree);
1469 mc_stat (tree_name, stat_buf);
1470 return tree_name;
1473 if (panel->marked) {
1474 for (i = 0; i < panel->count; i++)
1475 if (panel->dir.list[i].f.marked) {
1476 *stat_buf = panel->dir.list[i].st;
1477 return panel->dir.list[i].fname;
1479 } else {
1480 *stat_buf = panel->dir.list[panel->selected].st;
1481 return panel->dir.list[panel->selected].fname;
1483 g_assert_not_reached ();
1484 return NULL;
1488 * compute_dir_size:
1490 * Computes the number of bytes used by the files in a directory
1492 void
1493 compute_dir_size (const char *dirname, off_t *ret_marked, double *ret_total)
1495 DIR *dir;
1496 struct dirent *dirent;
1498 dir = mc_opendir (dirname);
1500 if (!dir)
1501 return;
1503 while ((dirent = mc_readdir (dir)) != NULL) {
1504 struct stat s;
1505 char *fullname;
1506 int res;
1508 if (strcmp (dirent->d_name, ".") == 0)
1509 continue;
1510 if (strcmp (dirent->d_name, "..") == 0)
1511 continue;
1513 fullname = concat_dir_and_file (dirname, dirent->d_name);
1515 res = mc_lstat (fullname, &s);
1517 if (res != 0) {
1518 g_free (fullname);
1519 continue;
1522 if (S_ISDIR (s.st_mode)) {
1523 off_t subdir_count = 0;
1524 double subdir_bytes = 0;
1526 compute_dir_size (fullname, &subdir_count, &subdir_bytes);
1528 *ret_marked += subdir_count;
1529 *ret_total += subdir_bytes;
1530 } else {
1531 (*ret_marked)++;
1532 *ret_total += s.st_size;
1534 g_free (fullname);
1537 mc_closedir (dir);
1541 * panel_compute_totals:
1543 * compute the number of files and the number of bytes
1544 * used up by the whole selection, recursing directories
1545 * as required. In addition, it checks to see if it will
1546 * overwrite any files by doing the copy.
1548 static void
1549 panel_compute_totals (WPanel *panel, off_t *ret_marked, double *ret_total)
1551 int i;
1553 *ret_marked = 0;
1554 *ret_total = 0.0;
1556 for (i = 0; i < panel->count; i++) {
1557 struct stat *s;
1559 if (!panel->dir.list[i].f.marked)
1560 continue;
1562 s = &panel->dir.list[i].st;
1564 if (S_ISDIR (s->st_mode)) {
1565 char *dir_name;
1566 off_t subdir_count = 0;
1567 double subdir_bytes = 0;
1569 dir_name =
1570 concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1571 compute_dir_size (dir_name, &subdir_count, &subdir_bytes);
1573 *ret_marked += subdir_count;
1574 *ret_total += subdir_bytes;
1575 g_free (dir_name);
1576 } else {
1577 (*ret_marked)++;
1578 *ret_total += s->st_size;
1584 * This array introduced to avoid translation problems. The former (op_names)
1585 * is assumed to be nouns, suitable in dialog box titles; this one should
1586 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1587 * Notice first symbol - it is to fool gettext and force these strings to
1588 * be different for it. First symbol is skipped while building a prompt.
1589 * (I don't use spaces around the words, because someday they could be
1590 * dropped, when widgets get smarter)
1592 static const char *op_names1[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1593 #define FMD_XLEN 64
1595 int fmd_xlen = FMD_XLEN;
1598 * These are formats for building a prompt. Parts encoded as follows:
1599 * %o - operation from op_names1
1600 * %f - file/files or files/directories, as appropriate
1601 * %m - "with source mask" or question mark for delete
1602 * %s - source name (truncated)
1603 * %d - number of marked files
1604 * %e - "to:" or question mark for delete
1606 * xgettext:no-c-format */
1607 static const char *one_format = N_("%o %f \"%s\"%m");
1608 /* xgettext:no-c-format */
1609 static const char *many_format = N_("%o %d %f%m");
1610 static const char *prompt_parts[] = {
1611 N_("file"), N_("files"), N_("directory"), N_("directories"),
1612 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1616 * Generate user prompt for panel operation.
1617 * single_source is the name if the source entry or NULL for multiple
1618 * entries.
1619 * src_stat is only used when single_source is not NULL.
1621 static void
1622 panel_operate_generate_prompt (const WPanel *panel, const int operation,
1623 const char *single_source,
1624 const struct stat *src_stat)
1626 register const char *sp, *cp;
1627 register int i;
1628 char format_string[BUF_MEDIUM];
1629 char *dp = format_string;
1631 #ifdef ENABLE_NLS
1632 static int i18n_flag = 0;
1633 if (!i18n_flag) {
1634 fmd_init_i18n (FALSE); /* to get proper fmd_xlen */
1636 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1637 op_names1[i] = _(op_names1[i]);
1639 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1640 prompt_parts[i] = _(prompt_parts[i]);
1642 one_format = _(one_format);
1643 many_format = _(many_format);
1644 i18n_flag = 1;
1646 #endif /* ENABLE_NLS */
1648 sp = single_source ? one_format : many_format;
1650 while (*sp) {
1651 switch (*sp) {
1652 case '%':
1653 cp = NULL;
1654 switch (sp[1]) {
1655 case 'o':
1656 cp = op_names1[operation] + 1;
1657 break;
1658 case 'm':
1659 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1660 break;
1661 case 'e':
1662 cp = operation == OP_DELETE ? "?" : prompt_parts[6];
1663 break;
1664 case 'f':
1665 if (single_source) {
1666 cp = S_ISDIR (src_stat->
1667 st_mode) ? prompt_parts[2] :
1668 prompt_parts[0];
1669 } else {
1670 cp = (panel->marked == panel->dirs_marked)
1671 ? prompt_parts[3]
1672 : (panel->dirs_marked ? prompt_parts[4]
1673 : prompt_parts[1]);
1675 break;
1676 default:
1677 *dp++ = *sp++;
1679 if (cp) {
1680 sp += 2;
1681 while (*cp)
1682 *dp++ = *cp++;
1684 break;
1685 default:
1686 *dp++ = *sp++;
1689 *dp = '\0';
1691 if (single_source) {
1692 i = fmd_xlen - strlen (format_string) - 4;
1693 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1694 name_trunc (single_source, i));
1695 } else {
1696 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1697 panel->marked);
1698 i = strlen (cmd_buf) + 6 - fmd_xlen;
1699 if (i > 0) {
1700 fmd_xlen += i;
1701 fmd_init_i18n (TRUE); /* to recalculate positions of child widgets */
1707 * panel_operate:
1709 * Performs one of the operations on the selection on the source_panel
1710 * (copy, delete, move).
1712 * Returns 1 if did change the directory
1713 * structure, Returns 0 if user aborted
1715 * force_single forces operation on the current entry and affects
1716 * default destination. Current filename is used as default.
1719 panel_operate (void *source_panel, FileOperation operation,
1720 int force_single)
1722 WPanel *panel = source_panel;
1723 #ifdef WITH_FULL_PATHS
1724 char *source_with_path = NULL;
1725 #else
1726 # define source_with_path source
1727 #endif /* !WITH_FULL_PATHS */
1728 char *source = NULL;
1729 char *dest = NULL;
1730 const char *temp = NULL;
1731 char *save_cwd = NULL, *save_dest = NULL;
1732 int single_entry = (get_current_type () == view_tree)
1733 || (panel->marked <= 1) || force_single;
1734 struct stat src_stat, dst_stat;
1735 int i, value;
1736 FileOpContext *ctx;
1738 off_t count = 0;
1739 double bytes = 0;
1741 int dst_result;
1742 int do_bg = 0; /* do background operation? */
1744 free_linklist (&linklist);
1745 free_linklist (&dest_dirs);
1747 if (single_entry) {
1748 if (force_single) {
1749 source = selection (panel)->fname;
1750 src_stat = selection (panel)->st;
1751 } else {
1752 source = panel_get_file (panel, &src_stat);
1755 if (!strcmp (source, "..")) {
1756 message (1, MSG_ERROR, _(" Cannot operate on \"..\"! "));
1757 return 0;
1761 /* Generate confirmation prompt */
1762 panel_operate_generate_prompt (panel, operation, source, &src_stat);
1764 ctx = file_op_context_new (operation);
1766 /* Show confirmation dialog */
1767 if (operation == OP_DELETE && confirm_delete) {
1768 if (safe_delete)
1769 query_set_sel (1);
1771 i = query_dialog (_(op_names[operation]), cmd_buf, D_ERROR, 2,
1772 _("&Yes"), _("&No"));
1774 if (i != 0) {
1775 file_op_context_destroy (ctx);
1776 return 0;
1778 } else if (operation != OP_DELETE) {
1779 char *dest_dir;
1781 /* Forced single operations default to the original name */
1782 if (force_single)
1783 dest_dir = source;
1784 else if (get_other_type () == view_listing)
1785 dest_dir = other_panel->cwd;
1786 else
1787 dest_dir = panel->cwd;
1789 dest =
1790 file_mask_dialog (ctx, operation, cmd_buf, dest_dir,
1791 single_entry, &do_bg);
1792 if (!dest) {
1793 file_op_context_destroy (ctx);
1794 return 0;
1796 if (!*dest) {
1797 file_op_context_destroy (ctx);
1798 g_free (dest);
1799 return 0;
1802 #ifdef WITH_BACKGROUND
1803 /* Did the user select to do a background operation? */
1804 if (do_bg) {
1805 int v;
1807 v = do_background (ctx,
1808 g_strconcat (op_names[operation], ": ",
1809 panel->cwd, NULL));
1810 if (v == -1) {
1811 message (1, MSG_ERROR,
1812 _(" Sorry, I could not put the job in background "));
1815 /* If we are the parent */
1816 if (v == 1) {
1817 mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL);
1818 mc_setctl (dest, VFS_SETCTL_FORGET, NULL);
1819 /* file_op_context_destroy (ctx); */
1820 return 0;
1823 #endif /* WITH_BACKGROUND */
1825 /* Initialize things */
1826 /* We do not want to trash cache every time file is
1827 created/touched. However, this will make our cache contain
1828 invalid data. */
1829 if (dest) {
1830 if (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1))
1831 save_dest = g_strdup (dest);
1833 if (panel->cwd) {
1834 if (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1))
1835 save_cwd = g_strdup (panel->cwd);
1838 /* Now, let's do the job */
1840 if (do_bg)
1841 ctx->ui = NULL;
1842 else
1843 file_op_context_create_ui (ctx, 1);
1845 /* This code is only called by the tree and panel code */
1846 if (single_entry) {
1847 /* We now have ETA in all cases */
1849 /* One file: FIXME mc_chdir will take user out of any vfs */
1850 if (operation != OP_COPY && get_current_type () == view_tree)
1851 mc_chdir (PATH_SEP_STR);
1853 /* The source and src_stat variables have been initialized before */
1854 #ifdef WITH_FULL_PATHS
1855 source_with_path = concat_dir_and_file (panel->cwd, source);
1856 #endif /* WITH_FULL_PATHS */
1858 if (operation == OP_DELETE) {
1859 if (S_ISDIR (src_stat.st_mode))
1860 value = erase_dir (ctx, source_with_path, &count, &bytes);
1861 else
1862 value =
1863 erase_file (ctx, source_with_path, &count, &bytes, 1);
1864 } else {
1865 temp = transform_source (ctx, source_with_path);
1867 if (temp == NULL) {
1868 value = transform_error;
1869 } else {
1870 char *temp2 = concat_dir_and_file (dest, temp);
1871 g_free (dest);
1872 dest = temp2;
1873 temp = NULL;
1875 switch (operation) {
1876 case OP_COPY:
1878 * we use file_mask_op_follow_links only with OP_COPY,
1880 (*ctx->stat_func) (source_with_path, &src_stat);
1882 if (S_ISDIR (src_stat.st_mode))
1883 value =
1884 copy_dir_dir (ctx, source_with_path, dest, 1,
1885 0, 0, 0, &count, &bytes);
1886 else
1887 value =
1888 copy_file_file (ctx, source_with_path, dest, 1,
1889 &count, &bytes, 1);
1890 break;
1892 case OP_MOVE:
1893 if (S_ISDIR (src_stat.st_mode))
1894 value =
1895 move_dir_dir (ctx, source_with_path, dest,
1896 &count, &bytes);
1897 else
1898 value =
1899 move_file_file (ctx, source_with_path, dest,
1900 &count, &bytes);
1901 break;
1903 default:
1904 /* Unknown file operation */
1905 abort ();
1908 } /* Copy or move operation */
1910 if ((value == FILE_CONT) && !force_single)
1911 unmark_files (panel);
1912 } else {
1913 /* Many files */
1914 /* Check destination for copy or move operation */
1915 if (operation != OP_DELETE) {
1916 retry_many_dst_stat:
1917 dst_result = mc_stat (dest, &dst_stat);
1918 if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)) {
1919 if (file_error
1920 (_(" Destination \"%s\" must be a directory \n %s "),
1921 dest) == FILE_RETRY)
1922 goto retry_many_dst_stat;
1923 goto clean_up;
1927 /* Initialize variables for progress bars */
1928 if (operation != OP_MOVE && verbose && file_op_compute_totals) {
1929 panel_compute_totals (panel, &ctx->progress_count,
1930 &ctx->progress_bytes);
1931 ctx->progress_totals_computed = 1;
1932 } else {
1933 ctx->progress_totals_computed = 0;
1934 ctx->progress_count = panel->marked;
1935 ctx->progress_bytes = panel->total;
1938 /* Loop for every file, perform the actual copy operation */
1939 for (i = 0; i < panel->count; i++) {
1940 if (!panel->dir.list[i].f.marked)
1941 continue; /* Skip the unmarked ones */
1943 source = panel->dir.list[i].fname;
1944 src_stat = panel->dir.list[i].st;
1946 #ifdef WITH_FULL_PATHS
1947 g_free (source_with_path);
1948 source_with_path = concat_dir_and_file (panel->cwd, source);
1949 #endif /* WITH_FULL_PATHS */
1951 if (operation == OP_DELETE) {
1952 if (S_ISDIR (src_stat.st_mode))
1953 value =
1954 erase_dir (ctx, source_with_path, &count, &bytes);
1955 else
1956 value =
1957 erase_file (ctx, source_with_path, &count, &bytes,
1959 } else {
1960 temp = transform_source (ctx, source_with_path);
1961 if (temp == NULL)
1962 value = transform_error;
1963 else {
1964 char *temp2 = concat_dir_and_file (dest, temp);
1966 switch (operation) {
1967 case OP_COPY:
1969 * we use file_mask_op_follow_links only with OP_COPY
1971 (*ctx->stat_func) (source_with_path, &src_stat);
1972 if (S_ISDIR (src_stat.st_mode))
1973 value =
1974 copy_dir_dir (ctx, source_with_path, temp2,
1975 1, 0, 0, 0, &count, &bytes);
1976 else
1977 value =
1978 copy_file_file (ctx, source_with_path,
1979 temp2, 1, &count, &bytes,
1981 free_linklist (&dest_dirs);
1982 break;
1984 case OP_MOVE:
1985 if (S_ISDIR (src_stat.st_mode))
1986 value =
1987 move_dir_dir (ctx, source_with_path, temp2,
1988 &count, &bytes);
1989 else
1990 value =
1991 move_file_file (ctx, source_with_path,
1992 temp2, &count, &bytes);
1993 break;
1995 default:
1996 /* Unknown file operation */
1997 abort ();
1999 g_free (temp2);
2001 } /* Copy or move operation */
2003 if (value == FILE_ABORT)
2004 goto clean_up;
2006 if (value == FILE_CONT)
2007 do_file_mark (panel, i, 0);
2009 if (file_progress_show_count (ctx, count, ctx->progress_count)
2010 == FILE_ABORT)
2011 goto clean_up;
2013 if (verbose
2014 && file_progress_show_bytes (ctx, bytes,
2015 ctx->progress_bytes) ==
2016 FILE_ABORT)
2017 goto clean_up;
2019 if (operation != OP_DELETE && verbose
2020 && file_progress_show (ctx, 0, 0) == FILE_ABORT)
2021 goto clean_up;
2023 mc_refresh ();
2024 } /* Loop for every file */
2025 } /* Many entries */
2026 clean_up:
2027 /* Clean up */
2029 if (save_cwd) {
2030 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
2031 g_free (save_cwd);
2033 if (save_dest) {
2034 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
2035 g_free (save_dest);
2038 free_linklist (&linklist);
2039 free_linklist (&dest_dirs);
2040 #ifdef WITH_FULL_PATHS
2041 g_free (source_with_path);
2042 #endif /* WITH_FULL_PATHS */
2043 g_free (dest);
2044 g_free (ctx->dest_mask);
2045 ctx->dest_mask = NULL;
2046 #ifdef WITH_BACKGROUND
2047 /* Let our parent know we are saying bye bye */
2048 if (we_are_background) {
2049 vfs_shut ();
2050 _exit (0);
2052 #endif /* WITH_BACKGROUND */
2054 file_op_context_destroy (ctx);
2055 return 1;
2058 /* }}} */
2060 /* {{{ Query/status report routines */
2062 static int
2063 real_do_file_error (enum OperationMode mode, const char *error)
2065 int result;
2066 const char *msg;
2068 msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
2069 result =
2070 query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"),
2071 _("&Abort"));
2073 switch (result) {
2074 case 0:
2075 do_refresh ();
2076 return FILE_SKIP;
2078 case 1:
2079 do_refresh ();
2080 return FILE_RETRY;
2082 case 2:
2083 default:
2084 return FILE_ABORT;
2088 /* Report error with one file */
2090 file_error (const char *format, const char *file)
2092 g_snprintf (cmd_buf, sizeof (cmd_buf), format,
2093 path_trunc (file, 30), unix_error_string (errno));
2095 return do_file_error (cmd_buf);
2098 /* Report error with two files */
2099 static int
2100 files_error (const char *format, const char *file1, const char *file2)
2102 char nfile1[16];
2103 char nfile2[16];
2105 strcpy (nfile1, path_trunc (file1, 15));
2106 strcpy (nfile2, path_trunc (file2, 15));
2108 g_snprintf (cmd_buf, sizeof (cmd_buf), format, nfile1, nfile2,
2109 unix_error_string (errno));
2111 return do_file_error (cmd_buf);
2114 static int
2115 real_query_recursive (FileOpContext *ctx, enum OperationMode mode, const char *s)
2117 gchar *text;
2119 if (ctx->recursive_result < RECURSIVE_ALWAYS) {
2120 const char *msg =
2121 mode ==
2122 Foreground ?
2123 _("\n Directory not empty. \n"
2124 " Delete it recursively? ")
2125 : _("\n Background process: Directory not empty \n"
2126 " Delete it recursively? ");
2127 text = g_strconcat (_(" Delete: "), path_trunc (s, 30), " ", (char *) NULL);
2129 if (safe_delete)
2130 query_set_sel (1);
2131 ctx->recursive_result = query_dialog (text, msg, D_ERROR, 5,
2132 _("&Yes"), _("&No"),
2133 _("A&ll"), _("Non&e"),
2134 _("&Abort"));
2136 if (ctx->recursive_result != RECURSIVE_ABORT)
2137 do_refresh ();
2138 g_free (text);
2141 switch (ctx->recursive_result) {
2142 case RECURSIVE_YES:
2143 case RECURSIVE_ALWAYS:
2144 return FILE_CONT;
2146 case RECURSIVE_NO:
2147 case RECURSIVE_NEVER:
2148 return FILE_SKIP;
2150 case RECURSIVE_ABORT:
2152 default:
2153 return FILE_ABORT;
2157 #ifdef WITH_BACKGROUND
2158 static int
2159 do_file_error (const char *str)
2161 if (we_are_background)
2162 return parent_call (real_do_file_error, NULL, 1, strlen (str),
2163 str);
2164 else
2165 return real_do_file_error (Foreground, str);
2168 static int
2169 query_recursive (FileOpContext *ctx, const char *s)
2171 if (we_are_background)
2172 return parent_call (real_query_recursive, ctx, 1, strlen (s), s);
2173 else
2174 return real_query_recursive (ctx, Foreground, s);
2177 static int
2178 query_replace (FileOpContext *ctx, const char *destname, struct stat *_s_stat,
2179 struct stat *_d_stat)
2181 if (we_are_background)
2182 return parent_call ((void *) file_progress_real_query_replace,
2183 ctx,
2185 strlen (destname), destname,
2186 sizeof (struct stat), _s_stat,
2187 sizeof (struct stat), _d_stat);
2188 else
2189 return file_progress_real_query_replace (ctx, Foreground, destname,
2190 _s_stat, _d_stat);
2193 #else
2194 static int
2195 do_file_error (const char *str)
2197 return real_do_file_error (Foreground, str);
2200 static int
2201 query_recursive (FileOpContext *ctx, const char *s)
2203 return real_query_recursive (ctx, Foreground, s);
2206 static int
2207 query_replace (FileOpContext *ctx, const char *destname, struct stat *_s_stat,
2208 struct stat *_d_stat)
2210 return file_progress_real_query_replace (ctx, Foreground, destname,
2211 _s_stat, _d_stat);
2214 #endif /* !WITH_BACKGROUND */
2217 Cause emacs to enter folding mode for this file:
2218 Local variables:
2219 end: