updated the .TP cleanup for coherency in the key description pages.
[midnight-commander.git] / src / file.c
blobefe074fc03ae79bb731becf6615d26ef6ea5d0ce
1 /* {{{ Copyright */
3 /* File managing. Important notes on this file:
5 About the use of dialogs in this file:
6 If you want to add a new dialog box (or call a routine that pops
7 up a dialog box), you have to provide a wrapper for background
8 operations (ie, background operations have to up-call to the parent
9 process).
11 For example, instead of using the message() routine, in this
12 file, you should use one of the stubs that call message with the
13 proper number of arguments (ie, message_1s, message_2s and so on).
15 Actually, that is a rule that should be followed by any routines
16 that may be called from this module.
20 /* File managing
21 Copyright (C) 1994, 1995, 1996 The Free Software Foundation
23 Written by: 1994, 1995 Janne Kukonlehto
24 1994, 1995 Fred Leeflang
25 1994, 1995, 1996 Miguel de Icaza
26 1995, 1996 Jakub Jelinek
27 1997 Norbert Warmuth
28 1998 Pavel Machek
30 The copy code was based in GNU's cp, and was written by:
31 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
33 The move code was based in GNU's mv, and was written by:
34 Mike Parker and David MacKenzie.
36 Janne Kukonlehto added much error recovery to them for being used
37 in an interactive program.
39 This program is free software; you can redistribute it and/or modify
40 it under the terms of the GNU General Public License as published by
41 the Free Software Foundation; either version 2 of the License, or
42 (at your option) any later version.
44 This program is distributed in the hope that it will be useful,
45 but WITHOUT ANY WARRANTY; without even the implied warranty of
46 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 GNU General Public License for more details.
49 You should have received a copy of the GNU General Public License
50 along with this program; if not, write to the Free Software
51 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
53 /* }}} */
55 /* {{{ Include files */
57 #include <config.h>
58 /* Hack: the vfs code should not rely on this */
59 #define WITH_FULL_PATHS 1
61 #include <sys/types.h>
62 #include <stdio.h>
63 #include <errno.h>
64 #include <ctype.h>
65 #include <string.h>
66 #ifdef HAVE_UNISTD_H
67 # include <unistd.h>
68 #endif /* HAVE_UNISTD_H */
69 #include <sys/stat.h>
71 #include "global.h"
72 #include "tty.h"
73 #include "eregex.h"
74 #include "dialog.h"
75 #include "setup.h"
76 /* Needed by query_replace */
77 #include "color.h"
78 #include "win.h"
79 #include "dlg.h"
80 #include "widget.h"
81 #define WANT_WIDGETS
82 #include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
83 #include "layout.h"
84 #include "widget.h"
85 #include "wtools.h"
87 /* Needed for current_panel, other_panel and WTree */
88 #include "dir.h"
89 #include "panel.h"
90 #include "file.h"
91 #include "filegui.h"
92 #include "tree.h"
93 #include "key.h"
94 #include "../vfs/vfs.h"
96 /* }}} */
98 int verbose = 1;
101 * Whether the Midnight Commander tries to provide more
102 * information about copy/move sizes and bytes transfered
103 * at the expense of some speed
105 int file_op_compute_totals = 1;
107 /* If on, default for "No" in delete operations */
108 int safe_delete = 0;
110 /* This is a hard link cache */
111 struct link {
112 struct link *next;
113 vfs *vfs;
114 dev_t dev;
115 ino_t ino;
116 short linkcount;
117 umode_t st_mode;
118 char name[1];
121 /* the hard link cache */
122 struct link *linklist = NULL;
124 /* the files-to-be-erased list */
125 struct link *erase_list;
128 * In copy_dir_dir we use two additional single linked lists: The first -
129 * variable name `parent_dirs' - holds information about already copied
130 * directories and is used to detect cyclic symbolic links.
131 * The second (`dest_dirs' below) holds information about just created
132 * target directories and is used to detect when an directory is copied
133 * into itself (we don't want to copy infinitly).
134 * Both lists don't use the linkcount and name structure members of struct
135 * link.
137 struct link *dest_dirs = 0;
139 char *op_names[3] = {
140 N_(" Copy "),
141 N_(" Move "),
142 N_(" Delete ")
145 /* }}} */
147 static int query_replace (FileOpContext * ctx, char *destname,
148 struct stat *_s_stat, struct stat *_d_stat);
149 static int query_recursive (FileOpContext * ctx, char *s);
150 static int do_file_error (char *str);
153 enum CaseConvs { NO_CONV = 0, UP_CHAR = 1, LOW_CHAR = 2, UP_SECT =
154 4, LOW_SECT = 8 };
156 static int
157 convert_case (int c, enum CaseConvs *conversion)
159 if (*conversion & UP_CHAR) {
160 *conversion &= ~UP_CHAR;
161 return toupper (c);
162 } else if (*conversion & LOW_CHAR) {
163 *conversion &= ~LOW_CHAR;
164 return tolower (c);
165 } else if (*conversion & UP_SECT) {
166 return toupper (c);
167 } else if (*conversion & LOW_SECT) {
168 return tolower (c);
169 } else
170 return c;
173 static int transform_error = 0;
175 static unsigned char *
176 do_transform_source (FileOpContext *ctx, unsigned char *source)
178 int j, k, l, len;
179 unsigned char *fnsource = x_basename (source);
180 int next_reg;
181 enum CaseConvs case_conv = NO_CONV;
182 static unsigned char fntarget[MC_MAXPATHLEN];
184 len = strlen (fnsource);
185 j = re_match (&ctx->rx, fnsource, len, 0, &ctx->regs);
186 if (j != len) {
187 transform_error = FILE_SKIP;
188 return NULL;
190 for (next_reg = 1, j = 0, k = 0; j < strlen (ctx->dest_mask); j++) {
191 switch (ctx->dest_mask[j]) {
192 case '\\':
193 j++;
194 if (!isdigit ((unsigned char) ctx->dest_mask[j])) {
195 /* Backslash followed by non-digit */
196 switch (ctx->dest_mask[j]) {
197 case 'U':
198 case_conv |= UP_SECT;
199 case_conv &= ~LOW_SECT;
200 break;
201 case 'u':
202 case_conv |= UP_CHAR;
203 break;
204 case 'L':
205 case_conv |= LOW_SECT;
206 case_conv &= ~UP_SECT;
207 break;
208 case 'l':
209 case_conv |= LOW_CHAR;
210 break;
211 case 'E':
212 case_conv = NO_CONV;
213 break;
214 default:
215 /* Backslash as quote mark */
216 fntarget[k++] =
217 convert_case (ctx->dest_mask[j], &case_conv);
219 break;
220 } else {
221 /* Backslash followed by digit */
222 next_reg = ctx->dest_mask[j] - '0';
223 /* Fall through */
226 case '*':
227 if (next_reg < 0 || next_reg >= RE_NREGS
228 || ctx->regs.start[next_reg] < 0) {
229 message_1s (1, MSG_ERROR, _(" Invalid target mask "));
230 transform_error = FILE_ABORT;
231 return NULL;
233 for (l = ctx->regs.start[next_reg];
234 l < ctx->regs.end[next_reg]; l++)
235 fntarget[k++] = convert_case (fnsource[l], &case_conv);
236 next_reg++;
237 break;
239 default:
240 fntarget[k++] = convert_case (ctx->dest_mask[j], &case_conv);
241 break;
244 fntarget[k] = 0;
245 return fntarget;
248 static unsigned char *
249 transform_source (FileOpContext *ctx, unsigned char *source)
251 unsigned char *s = g_strdup (source);
252 unsigned char *q;
254 /* We remove \n from the filename since regex routines would use \n as an anchor */
255 /* this is just to be allowed to maniupulate file names with \n on it */
256 for (q = s; *q; q++) {
257 if (*q == '\n')
258 *q = ' ';
260 q = do_transform_source (ctx, s);
261 g_free (s);
262 return q;
265 static void
266 free_linklist (struct link **linklist)
268 struct link *lp, *lp2;
270 for (lp = *linklist; lp != NULL; lp = lp2) {
271 lp2 = lp->next;
272 g_free (lp);
274 *linklist = NULL;
277 static int
278 is_in_linklist (struct link *lp, char *path, struct stat *sb)
280 ino_t ino = sb->st_ino;
281 dev_t dev = sb->st_dev;
282 #ifdef USE_VFS
283 vfs *vfs = vfs_type (path);
284 #endif /* USE_VFS */
286 while (lp) {
287 #ifdef USE_VFS
288 if (lp->vfs == vfs)
289 #endif /* USE_VFS */
290 if (lp->ino == ino && lp->dev == dev)
291 return 1;
292 lp = lp->next;
294 return 0;
298 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
299 * and a hardlink was succesfully made
301 static int
302 check_hardlinks (char *src_name, char *dst_name, struct stat *pstat)
304 struct link *lp;
305 vfs *my_vfs = vfs_type (src_name);
306 ino_t ino = pstat->st_ino;
307 dev_t dev = pstat->st_dev;
308 struct stat link_stat;
309 char *p;
311 #if 1 /* What will happen if we kill this line? mc_link() will fail on this and it is right behaviour... */
312 if (vfs_file_is_ftp (src_name) || vfs_file_is_smb (src_name))
313 return 0;
314 #endif
315 for (lp = linklist; lp != NULL; lp = lp->next)
316 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev) {
317 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino
318 && link_stat.st_dev == dev
319 && vfs_type (lp->name) == my_vfs) {
320 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
321 was copied to */
322 if (vfs_type (dst_name) == vfs_type (p)) {
323 if (!mc_stat (p, &link_stat)) {
324 if (!mc_link (p, dst_name))
325 return 1;
329 message_1s (1, MSG_ERROR, _(" Could not make the hardlink "));
330 return 0;
332 lp = (struct link *) g_malloc (sizeof (struct link) + strlen (src_name)
333 + strlen (dst_name) + 1);
334 if (lp) {
335 lp->vfs = my_vfs;
336 lp->ino = ino;
337 lp->dev = dev;
338 strcpy (lp->name, src_name);
339 p = strchr (lp->name, 0) + 1;
340 strcpy (p, dst_name);
341 lp->next = linklist;
342 linklist = lp;
344 return 0;
348 * Duplicate the contents of the symbolic link src_path in dst_path.
349 * Try to make a stable symlink if the option "stable symlink" was
350 * set in the file mask dialog.
351 * If dst_path is an existing symlink it will be deleted silently
352 * (upper levels take already care of existing files at dst_path).
354 static int
355 make_symlink (FileOpContext *ctx, char *src_path, char *dst_path)
357 char link_target[MC_MAXPATHLEN];
358 int len;
359 int return_status;
360 struct stat sb;
361 int dst_is_symlink;
363 if (mc_lstat (dst_path, &sb) == 0 && S_ISLNK (sb.st_mode))
364 dst_is_symlink = 1;
365 else
366 dst_is_symlink = 0;
368 retry_src_readlink:
369 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN);
370 if (len < 0) {
371 return_status =
372 file_error (_(" Cannot read source link \"%s\" \n %s "),
373 src_path);
374 if (return_status == FILE_RETRY)
375 goto retry_src_readlink;
376 return return_status;
378 link_target[len] = 0;
380 if (ctx->stable_symlinks)
381 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path)) {
382 message_1s (1, MSG_ERROR,
383 _(" Cannot make stable symlinks across "
384 "non-local filesystems: \n\n"
385 " Option Stable Symlinks will be disabled "));
386 ctx->stable_symlinks = 0;
389 if (ctx->stable_symlinks && *link_target != PATH_SEP) {
390 char *p, *q, *r, *s;
392 p = g_strdup (src_path);
393 r = strrchr (p, PATH_SEP);
395 if (r) {
396 r[1] = 0;
397 if (*dst_path == PATH_SEP)
398 q = g_strdup (dst_path);
399 else
400 q = g_strconcat (p, dst_path, NULL);
401 r = strrchr (q, PATH_SEP);
402 if (r) {
403 r[1] = 0;
404 s = g_strconcat (p, link_target, NULL);
405 strcpy (link_target, s);
406 g_free (s);
407 s = diff_two_paths (q, link_target);
408 if (s) {
409 strcpy (link_target, s);
410 g_free (s);
413 g_free (q);
415 g_free (p);
417 retry_dst_symlink:
418 if (mc_symlink (link_target, dst_path) == 0)
419 /* Success */
420 return FILE_CONT;
422 * if dst_exists, it is obvious that this had failed.
423 * We can delete the old symlink and try again...
425 if (dst_is_symlink) {
426 if (!mc_unlink (dst_path))
427 if (mc_symlink (link_target, dst_path) == 0)
428 /* Success */
429 return FILE_CONT;
431 return_status =
432 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
433 dst_path);
434 if (return_status == FILE_RETRY)
435 goto retry_dst_symlink;
436 return return_status;
439 static int
440 progress_update_one (FileOpContext *ctx,
441 off_t *progress_count,
442 double *progress_bytes, int add, int is_toplevel_file)
444 int ret;
446 if (is_toplevel_file || ctx->progress_totals_computed) {
447 (*progress_count)++;
448 (*progress_bytes) += add;
451 /* Apply some heuristic here to not call the update stuff very often */
452 ret =
453 file_progress_show_count (ctx, *progress_count,
454 ctx->progress_count);
455 if (ret != FILE_CONT)
456 return ret;
457 ret =
458 file_progress_show_bytes (ctx, *progress_bytes,
459 ctx->progress_bytes);
461 return ret;
464 /* Status of the destination file */
465 enum {
466 DEST_NONE, /* Not created */
467 DEST_SHORT, /* Created, not fully copied */
468 DEST_FULL /* Created, fully copied */
472 copy_file_file (FileOpContext *ctx, char *src_path, char *dst_path,
473 int ask_overwrite, off_t *progress_count,
474 double *progress_bytes, int is_toplevel_file)
476 #ifndef NATIVE_WIN32
477 uid_t src_uid = (uid_t) - 1;
478 gid_t src_gid = (gid_t) - 1;
479 #endif /* !NATIVE_WIN32 */
480 char *buf = NULL;
481 int buf_size = BUF_8K;
482 int src_desc, dest_desc = -1;
483 int n_read, n_written;
484 int src_mode = 0; /* The mode of the source file */
485 struct stat sb, sb2;
486 struct utimbuf utb;
487 int dst_exists = 0, appending = 0;
488 off_t n_read_total = 0, file_size = -1;
489 int return_status, temp_status;
490 struct timeval tv_transfer_start;
491 int dst_status = DEST_NONE; /* 1 if the file is not fully copied */
493 /* FIXME: We should not be using global variables! */
494 ctx->do_reget = 0;
495 return_status = FILE_RETRY;
497 if (file_progress_show_source (ctx, src_path) == FILE_ABORT ||
498 file_progress_show_target (ctx, dst_path) == FILE_ABORT)
499 return FILE_ABORT;
501 mc_refresh ();
503 retry_dst_stat:
504 if (mc_stat (dst_path, &sb2) == 0) {
505 if (S_ISDIR (sb2.st_mode)) {
506 return_status =
507 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
508 dst_path);
509 if (return_status == FILE_RETRY)
510 goto retry_dst_stat;
511 return return_status;
513 dst_exists = 1;
516 while ((*ctx->stat_func) (src_path, &sb)) {
517 return_status =
518 file_error (_(" Cannot stat source file \"%s\" \n %s "),
519 src_path);
520 if (return_status != FILE_RETRY)
521 return return_status;
524 if (dst_exists) {
525 /* .ado: For Win32: no st_ino exists, it is better to just try to
526 * overwrite the target file
528 #ifndef NATIVE_WIN32
529 /* Destination already exists */
530 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino) {
531 message_3s (1, MSG_ERROR,
532 _(" `%s' and `%s' are the same file "), src_path,
533 dst_path);
534 do_refresh ();
535 return FILE_SKIP;
537 #endif /* !NATIVE_WIN32 */
539 /* Should we replace destination? */
540 if (ask_overwrite) {
541 ctx->do_reget = 0;
542 return_status = query_replace (ctx, dst_path, &sb, &sb2);
543 if (return_status != FILE_CONT)
544 return return_status;
548 if (!ctx->do_append) {
549 /* .ado: OS2 and NT don't have hardlinks */
550 #ifndef NATIVE_WIN32
551 /* Check the hardlinks */
552 if (!ctx->follow_links && sb.st_nlink > 1 &&
553 check_hardlinks (src_path, dst_path, &sb) == 1) {
554 /* We have made a hardlink - no more processing is necessary */
555 return return_status;
558 if (S_ISLNK (sb.st_mode)) {
559 int retval;
561 retval = make_symlink (ctx, src_path, dst_path);
562 return retval;
564 #endif /* !NATIVE_WIN32 */
566 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode)
567 || S_ISFIFO (sb.st_mode)
568 || S_ISSOCK (sb.st_mode)) {
569 while (mc_mknod
570 (dst_path, sb.st_mode & ctx->umask_kill,
571 sb.st_rdev) < 0) {
572 return_status =
573 file_error (_
574 (" Cannot create special file \"%s\" \n %s "),
575 dst_path);
576 if (return_status == FILE_RETRY)
577 continue;
578 return return_status;
580 /* Success */
582 #ifndef NATIVE_WIN32
583 while (ctx->preserve_uidgid
584 && mc_chown (dst_path, sb.st_uid, sb.st_gid)) {
585 temp_status =
586 file_error (_
587 (" Cannot chown target file \"%s\" \n %s "),
588 dst_path);
589 if (temp_status == FILE_RETRY)
590 continue;
591 return temp_status;
593 #endif /* !NATIVE_WIN32 */
594 while (ctx->preserve &&
595 (mc_chmod (dst_path, sb.st_mode & ctx->umask_kill) <
596 0)) {
597 temp_status =
598 file_error (_
599 (" Cannot chmod target file \"%s\" \n %s "),
600 dst_path);
601 if (temp_status == FILE_RETRY)
602 continue;
603 return temp_status;
605 return FILE_CONT;
609 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
611 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0) {
612 return_status =
613 file_error (_(" Cannot open source file \"%s\" \n %s "),
614 src_path);
615 if (return_status == FILE_RETRY)
616 continue;
617 ctx->do_append = 0;
618 return return_status;
621 if (ctx->do_reget) {
622 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget) {
623 message_1s (1, _("Warning"),
624 _(" Reget failed, about to overwrite file "));
625 ctx->do_reget = ctx->do_append = 0;
629 while (mc_fstat (src_desc, &sb)) {
630 return_status =
631 file_error (_(" Cannot fstat source file \"%s\" \n %s "),
632 src_path);
633 if (return_status == FILE_RETRY)
634 continue;
635 ctx->do_append = 0;
636 goto ret;
638 src_mode = sb.st_mode;
639 #ifndef NATIVE_WIN32
640 src_uid = sb.st_uid;
641 src_gid = sb.st_gid;
642 #endif /* !NATIVE_WIN32 */
643 utb.actime = sb.st_atime;
644 utb.modtime = sb.st_mtime;
645 file_size = sb.st_size;
647 /* Create the new regular file with small permissions initially,
648 do not create a security hole. FIXME: You have security hole
649 here, btw. Imagine copying to /tmp and symlink attack :-( */
651 while ((dest_desc = mc_open (dst_path, O_WRONLY |
652 (ctx->
653 do_append ? O_APPEND : (O_CREAT |
654 O_TRUNC)),
655 0600)) < 0) {
656 return_status =
657 file_error (_(" Cannot create target file \"%s\" \n %s "),
658 dst_path);
659 if (return_status == FILE_RETRY)
660 continue;
661 ctx->do_append = 0;
662 goto ret;
664 dst_status = DEST_SHORT; /* file opened, but not fully copied */
666 appending = ctx->do_append;
667 ctx->do_append = 0;
669 /* Find out the optimal buffer size. */
670 while (mc_fstat (dest_desc, &sb)) {
671 return_status =
672 file_error (_(" Cannot fstat target file \"%s\" \n %s "),
673 dst_path);
674 if (return_status == FILE_RETRY)
675 continue;
676 goto ret;
678 buf = (char *) g_malloc (buf_size);
680 ctx->eta_secs = 0.0;
681 ctx->bps = 0;
683 return_status = file_progress_show (ctx, 0, file_size);
685 mc_refresh ();
687 if (return_status != FILE_CONT)
688 goto ret;
691 struct timeval tv_current, tv_last_update, tv_last_input;
692 int secs, update_secs;
693 long dt;
694 char *stalled_msg;
696 tv_last_update = tv_transfer_start;
698 for (;;) {
699 /* src_read */
700 if (mc_ctl (src_desc, MCCTL_IS_NOTREADY, 0))
701 n_read = -1;
702 else
703 while ((n_read = mc_read (src_desc, buf, buf_size)) < 0) {
704 return_status =
705 file_error (_
706 (" Cannot read source file \"%s\" \n %s "),
707 src_path);
708 if (return_status == FILE_RETRY)
709 continue;
710 goto ret;
712 if (n_read == 0)
713 break;
715 gettimeofday (&tv_current, NULL);
717 if (n_read > 0) {
718 n_read_total += n_read;
720 /* Windows NT ftp servers report that files have no
721 * permissions: -------, so if we happen to have actually
722 * read something, we should fix the permissions.
724 if (!(src_mode & ((S_IRUSR | S_IWUSR | S_IXUSR) /* user */
725 |(S_IXOTH | S_IWOTH | S_IROTH) /* other */
726 |(S_IXGRP | S_IWGRP | S_IRGRP)))) /* group */
727 src_mode = S_IRUSR | S_IWUSR | S_IROTH | S_IRGRP;
728 gettimeofday (&tv_last_input, NULL);
730 /* dst_write */
731 while ((n_written =
732 mc_write (dest_desc, buf, n_read)) < n_read) {
733 if (n_written > 0) {
734 n_read -= n_written;
735 continue;
737 return_status =
738 file_error (_
739 (" Cannot write target file \"%s\" \n %s "),
740 dst_path);
741 if (return_status == FILE_RETRY)
742 continue;
743 goto ret;
747 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
748 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
749 if (secs > 2) {
750 rotate_dash ();
751 tv_last_update = tv_current;
754 /* 2. Check for a stalled condition */
755 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
756 stalled_msg = "";
757 if (update_secs > 4) {
758 stalled_msg = _("(stalled)");
761 /* 3. Compute ETA */
762 if (secs > 2) {
763 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
765 if (n_read_total) {
766 ctx->eta_secs =
767 ((dt / (double) n_read_total) * file_size) - dt;
768 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
769 } else
770 ctx->eta_secs = 0.0;
773 /* 4. Compute BPS rate */
774 if (secs > 2) {
775 ctx->bps_time =
776 (tv_current.tv_sec - tv_transfer_start.tv_sec);
777 if (ctx->bps_time < 1)
778 ctx->bps_time = 1;
779 ctx->bps = n_read_total / ctx->bps_time;
782 file_progress_set_stalled_label (ctx, stalled_msg);
783 return_status =
784 file_progress_show_bytes (ctx,
785 *progress_bytes + n_read_total,
786 ctx->progress_bytes);
787 if (return_status == FILE_CONT) {
788 return_status =
789 file_progress_show (ctx, n_read_total, file_size);
791 mc_refresh ();
792 if (return_status != FILE_CONT)
793 goto ret;
797 dst_status = DEST_FULL; /* copy successful, don't remove target file */
799 ret:
800 if (buf)
801 g_free (buf);
803 while (src_desc != -1 && mc_close (src_desc) < 0) {
804 temp_status =
805 file_error (_(" Cannot close source file \"%s\" \n %s "),
806 src_path);
807 if (temp_status == FILE_RETRY)
808 continue;
809 if (temp_status == FILE_ABORT)
810 return_status = temp_status;
811 break;
814 while (dest_desc != -1 && mc_close (dest_desc) < 0) {
815 temp_status =
816 file_error (_(" Cannot close target file \"%s\" \n %s "),
817 dst_path);
818 if (temp_status == FILE_RETRY)
819 continue;
820 return_status = temp_status;
821 break;
824 if (dst_status == DEST_SHORT) {
825 /* Remove short file */
826 int result;
827 result =
828 query_dialog (_("Copy"),
829 _("Incomplete file was retrieved. Keep it?"),
830 D_ERROR, 2, _("&Delete"), _("&Keep"));
831 if (!result)
832 mc_unlink (dst_path);
833 } else if (dst_status == DEST_FULL) {
834 /* Copy has succeeded */
835 #ifndef NATIVE_WIN32
836 if (!appending && ctx->preserve_uidgid) {
837 while (mc_chown (dst_path, src_uid, src_gid)) {
838 temp_status = file_error
839 (_(" Cannot chown target file \"%s\" \n %s "),
840 dst_path);
841 if (temp_status == FILE_RETRY)
842 continue;
843 return_status = temp_status;
844 break;
847 #endif /* !NATIVE_WIN32 */
850 * .ado: according to the XPG4 standard, the file must be closed before
851 * chmod can be invoked
853 if (!appending && ctx->preserve) {
854 while (mc_chmod (dst_path, src_mode & ctx->umask_kill)) {
855 temp_status =
856 file_error (_
857 (" Cannot chmod target file \"%s\" \n %s "),
858 dst_path);
859 if (temp_status != FILE_RETRY) {
860 return_status = temp_status;
861 break;
864 mc_utime (dst_path, &utb);
868 if (return_status == FILE_CONT)
869 return_status =
870 progress_update_one (ctx, progress_count, progress_bytes,
871 file_size, is_toplevel_file);
873 return return_status;
877 * I think these copy_*_* functions should have a return type.
878 * anyway, this function *must* have two directories as arguments.
880 /* FIXME: This function needs to check the return values of the
881 function calls */
883 copy_dir_dir (FileOpContext *ctx, char *s, char *d, int toplevel,
884 int move_over, int delete,
885 struct link *parent_dirs,
886 off_t *progress_count, double *progress_bytes)
888 struct dirent *next;
889 struct stat buf, cbuf;
890 DIR *reading;
891 char *path, *mdpath, *dest_file, *dest_dir;
892 int return_status = FILE_CONT;
893 struct utimbuf utb;
894 struct link *lp;
896 /* First get the mode of the source dir */
897 retry_src_stat:
898 if ((*ctx->stat_func) (s, &cbuf)) {
899 return_status =
900 file_error (_(" Cannot stat source directory \"%s\" \n %s "),
902 if (return_status == FILE_RETRY)
903 goto retry_src_stat;
904 return return_status;
907 if (is_in_linklist (dest_dirs, s, &cbuf)) {
908 /* Don't copy a directory we created before (we don't want to copy
909 infinitely if a directory is copied into itself) */
910 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
911 return FILE_CONT;
914 /* Hmm, hardlink to directory??? - Norbert */
915 /* FIXME: In this step we should do something
916 in case the destination already exist */
917 /* Check the hardlinks */
918 if (ctx->preserve && cbuf.st_nlink > 1
919 && check_hardlinks (s, d, &cbuf) == 1) {
920 /* We have made a hardlink - no more processing is necessary */
921 return return_status;
924 if (!S_ISDIR (cbuf.st_mode)) {
925 return_status =
926 file_error (_
927 (" Source directory \"%s\" is not a directory \n %s "),
929 if (return_status == FILE_RETRY)
930 goto retry_src_stat;
931 return return_status;
934 if (is_in_linklist (parent_dirs, s, &cbuf)) {
935 /* we found a cyclic symbolic link */
936 message_2s (1, MSG_ERROR,
937 _(" Cannot copy cyclic symbolic link \n `%s' "), s);
938 return FILE_SKIP;
941 lp = g_new (struct link, 1);
942 lp->vfs = vfs_type (s);
943 lp->ino = cbuf.st_ino;
944 lp->dev = cbuf.st_dev;
945 lp->next = parent_dirs;
946 parent_dirs = lp;
948 retry_dst_stat:
949 /* Now, check if the dest dir exists, if not, create it. */
950 if (mc_stat (d, &buf)) {
951 /* Here the dir doesn't exist : make it ! */
953 if (move_over) {
954 if (mc_rename (s, d) == 0) {
955 g_free (parent_dirs);
956 return FILE_CONT;
959 dest_dir = g_strdup (d);
960 } else {
962 * If the destination directory exists, we want to copy the whole
963 * directory, but we only want this to happen once.
965 * Escape sequences added to the * to compiler warnings.
966 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
967 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
969 if (!S_ISDIR (buf.st_mode)) {
970 return_status =
971 file_error (_
972 (" Destination \"%s\" must be a directory \n %s "),
974 if (return_status == FILE_RETRY)
975 goto retry_dst_stat;
976 g_free (parent_dirs);
977 return return_status;
979 #if 1
980 /* Again, I'm getting curious. Is not d already what we wanted, incl.
981 * masked source basename? Is not this just a relict of the past versions?
982 * I'm afraid this will lead into a two level deep dive :(
984 * I think this is indeed the problem. I can not remember any case where
985 * we actually would like that behaviour -miguel
987 * It's a documented feature (option `Dive into subdir if exists' in the
988 * copy/move dialog). -Norbert
990 if (toplevel && ctx->dive_into_subdirs) {
991 dest_dir = concat_dir_and_file (d, x_basename (s));
992 } else
993 #endif
995 dest_dir = g_strdup (d);
996 goto dont_mkdir;
999 retry_dst_mkdir:
1000 if (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU)) {
1001 return_status =
1002 file_error (_(" Cannot create target directory \"%s\" \n %s "),
1003 dest_dir);
1004 if (return_status == FILE_RETRY)
1005 goto retry_dst_mkdir;
1006 goto ret;
1009 lp = g_new (struct link, 1);
1010 mc_stat (dest_dir, &buf);
1011 lp->vfs = vfs_type (dest_dir);
1012 lp->ino = buf.st_ino;
1013 lp->dev = buf.st_dev;
1014 lp->next = dest_dirs;
1015 dest_dirs = lp;
1017 #ifndef NATIVE_WIN32
1018 if (ctx->preserve_uidgid) {
1019 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid)) {
1020 return_status =
1021 file_error (_
1022 (" Cannot chown target directory \"%s\" \n %s "),
1023 dest_dir);
1024 if (return_status != FILE_RETRY)
1025 goto ret;
1028 #endif /* !NATIVE_WIN32 */
1030 dont_mkdir:
1031 /* open the source dir for reading */
1032 if ((reading = mc_opendir (s)) == 0) {
1033 goto ret;
1036 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT) {
1038 * Now, we don't want '.' and '..' to be created / copied at any time
1040 if (!strcmp (next->d_name, "."))
1041 continue;
1042 if (!strcmp (next->d_name, ".."))
1043 continue;
1045 /* get the filename and add it to the src directory */
1046 path = concat_dir_and_file (s, next->d_name);
1048 (*ctx->stat_func) (path, &buf);
1049 if (S_ISDIR (buf.st_mode)) {
1050 mdpath = concat_dir_and_file (dest_dir, next->d_name);
1052 * From here, we just intend to recursively copy subdirs, not
1053 * the double functionality of copying different when the target
1054 * dir already exists. So, we give the recursive call the flag 0
1055 * meaning no toplevel.
1057 return_status = copy_dir_dir (ctx, path, mdpath, 0, 0,
1058 delete, parent_dirs,
1059 progress_count, progress_bytes);
1060 g_free (mdpath);
1061 } else {
1062 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
1063 return_status = copy_file_file (ctx, path, dest_file, 1,
1064 progress_count, progress_bytes,
1066 g_free (dest_file);
1068 if (delete && return_status == FILE_CONT) {
1069 if (ctx->erase_at_end) {
1070 static struct link *tail;
1071 lp = g_malloc (sizeof (struct link) + strlen (path));
1072 strcpy (lp->name, path);
1073 lp->st_mode = buf.st_mode;
1074 lp->next = 0;
1075 if (erase_list) {
1076 tail->next = lp;
1077 tail = lp;
1078 } else
1079 erase_list = tail = lp;
1080 } else {
1081 if (S_ISDIR (buf.st_mode)) {
1082 return_status = erase_dir_iff_empty (ctx, path);
1083 } else
1084 return_status = erase_file (ctx, path, 0, 0, 0);
1088 g_free (path);
1090 mc_closedir (reading);
1092 if (ctx->preserve) {
1093 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1094 utb.actime = cbuf.st_atime;
1095 utb.modtime = cbuf.st_mtime;
1096 mc_utime (dest_dir, &utb);
1099 ret:
1100 g_free (dest_dir);
1101 g_free (parent_dirs);
1102 return return_status;
1105 /* }}} */
1107 /* {{{ Move routines */
1110 move_file_file (FileOpContext *ctx, char *s, char *d,
1111 off_t *progress_count, double *progress_bytes)
1113 struct stat src_stats, dst_stats;
1114 int return_status = FILE_CONT;
1116 if (file_progress_show_source (ctx, s) == FILE_ABORT
1117 || file_progress_show_target (ctx, d) == FILE_ABORT)
1118 return FILE_ABORT;
1120 mc_refresh ();
1122 retry_src_lstat:
1123 if (mc_lstat (s, &src_stats) != 0) {
1124 /* Source doesn't exist */
1125 return_status =
1126 file_error (_(" Cannot stat file \"%s\" \n %s "), s);
1127 if (return_status == FILE_RETRY)
1128 goto retry_src_lstat;
1129 return return_status;
1132 if (mc_lstat (d, &dst_stats) == 0) {
1133 /* Destination already exists */
1134 /* .ado: for Win32, no st_ino exists */
1135 #ifndef NATIVE_WIN32
1136 if (src_stats.st_dev == dst_stats.st_dev
1137 && src_stats.st_ino == dst_stats.st_ino) {
1138 int msize = COLS - 36;
1139 char st[MC_MAXPATHLEN];
1140 char dt[MC_MAXPATHLEN];
1142 if (msize < 0)
1143 msize = 40;
1144 msize /= 2;
1146 strcpy (st, name_trunc (s, msize));
1147 strcpy (dt, name_trunc (d, msize));
1148 message_3s (1, MSG_ERROR,
1149 _(" `%s' and `%s' are the same file "), st, dt);
1150 do_refresh ();
1151 return FILE_SKIP;
1153 #endif /* !NATIVE_WIN32 */
1154 if (S_ISDIR (dst_stats.st_mode)) {
1155 message_2s (1, MSG_ERROR,
1156 _(" Cannot overwrite directory `%s' "), d);
1157 do_refresh ();
1158 return FILE_SKIP;
1161 if (confirm_overwrite) {
1162 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1163 if (return_status != FILE_CONT)
1164 return return_status;
1166 /* Ok to overwrite */
1169 if (!ctx->do_append) {
1170 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks) {
1171 if ((return_status = make_symlink (ctx, s, d)) == FILE_CONT) {
1172 goto retry_src_remove;
1173 } else
1174 return return_status;
1177 if (mc_rename (s, d) == 0) {
1178 return FILE_CONT;
1181 #if 0
1182 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1183 one nfs to the same, but on the server it is on two different
1184 filesystems. Then nfs returns EIO instead of EXDEV.
1185 Hope it will not hurt if we always in case of error try to copy/delete. */
1186 else
1187 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1189 if (errno != EXDEV) {
1190 return_status =
1191 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s,
1193 if (return_status == FILE_RETRY)
1194 goto retry_rename;
1195 return return_status;
1197 #endif
1199 /* Failed because filesystem boundary -> copy the file instead */
1200 return_status =
1201 copy_file_file (ctx, s, d, 0, progress_count, progress_bytes, 1);
1202 if (return_status != FILE_CONT)
1203 return return_status;
1205 if ((return_status =
1206 file_progress_show_source (ctx, NULL)) != FILE_CONT
1207 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1208 return return_status;
1210 mc_refresh ();
1212 retry_src_remove:
1213 if (mc_unlink (s)) {
1214 return_status =
1215 file_error (_(" Cannot remove file \"%s\" \n %s "), s);
1216 if (return_status == FILE_RETRY)
1217 goto retry_src_remove;
1218 return return_status;
1221 if (return_status == FILE_CONT)
1222 return_status = progress_update_one (ctx,
1223 progress_count,
1224 progress_bytes,
1225 src_stats.st_size, 1);
1227 return return_status;
1231 move_dir_dir (FileOpContext *ctx, char *s, char *d,
1232 off_t *progress_count, double *progress_bytes)
1234 struct stat sbuf, dbuf, destbuf;
1235 struct link *lp;
1236 char *destdir;
1237 int return_status;
1238 int move_over = 0;
1240 if (file_progress_show_source (ctx, s) == FILE_ABORT ||
1241 file_progress_show_target (ctx, d) == FILE_ABORT)
1242 return FILE_ABORT;
1244 mc_refresh ();
1246 mc_stat (s, &sbuf);
1247 if (mc_stat (d, &dbuf))
1248 destdir = g_strdup (d); /* destination doesn't exist */
1249 else if (!ctx->dive_into_subdirs) {
1250 destdir = g_strdup (d);
1251 move_over = 1;
1252 } else
1253 destdir = concat_dir_and_file (d, x_basename (s));
1254 #ifndef NATIVE_WIN32
1255 if (sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino) {
1256 int msize = COLS - 36;
1257 char st[MC_MAXPATHLEN];
1258 char dt[MC_MAXPATHLEN];
1260 if (msize < 0)
1261 msize = 40;
1262 msize /= 2;
1264 strcpy (st, name_trunc (s, msize));
1265 strcpy (dt, name_trunc (d, msize));
1266 message_3s (1, MSG_ERROR,
1267 _(" `%s' and `%s' are the same directory "), st, dt);
1268 do_refresh ();
1269 return FILE_SKIP;
1271 #endif /* !NATIVE_WIN32 */
1273 /* Check if the user inputted an existing dir */
1274 retry_dst_stat:
1275 if (!mc_stat (destdir, &destbuf)) {
1276 if (move_over) {
1277 return_status = copy_dir_dir (ctx, s, destdir, 0, 1, 1, 0,
1278 progress_count, progress_bytes);
1280 if (return_status != FILE_CONT)
1281 goto ret;
1282 goto oktoret;
1283 } else {
1284 if (S_ISDIR (destbuf.st_mode))
1285 return_status =
1286 file_error (_
1287 (" Cannot overwrite directory \"%s\" %s "),
1288 destdir);
1289 else
1290 return_status =
1291 file_error (_(" Cannot overwrite file \"%s\" %s "),
1292 destdir);
1293 if (return_status == FILE_RETRY)
1294 goto retry_dst_stat;
1296 g_free (destdir);
1297 return return_status;
1300 retry_rename:
1301 if (mc_rename (s, destdir) == 0) {
1302 return_status = FILE_CONT;
1303 goto ret;
1305 /* .ado: Drive, Do we need this anymore? */
1306 #ifdef WIN32
1307 else {
1308 /* EXDEV: cross device; does not work everywhere */
1309 if (toupper (s[0]) != toupper (destdir[0]))
1310 goto w32try;
1312 #endif /* WIN32 */
1314 if (errno != EXDEV) {
1315 return_status =
1316 files_error (_
1317 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1318 s, d);
1319 if (return_status == FILE_RETRY)
1320 goto retry_rename;
1321 goto ret;
1323 #ifdef WIN32
1324 w32try:
1325 #endif /* WIN32 */
1326 /* Failed because of filesystem boundary -> copy dir instead */
1327 return_status =
1328 copy_dir_dir (ctx, s, destdir, 0, 0, 1, 0, progress_count,
1329 progress_bytes);
1331 if (return_status != FILE_CONT)
1332 goto ret;
1333 oktoret:
1334 if ((return_status =
1335 file_progress_show_source (ctx, NULL)) != FILE_CONT
1336 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1337 goto ret;
1339 mc_refresh ();
1340 if (ctx->erase_at_end) {
1341 for (; erase_list && return_status != FILE_ABORT;) {
1342 if (S_ISDIR (erase_list->st_mode)) {
1343 return_status =
1344 erase_dir_iff_empty (ctx, erase_list->name);
1345 } else
1346 return_status =
1347 erase_file (ctx, erase_list->name, 0, 0, 0);
1348 lp = erase_list;
1349 erase_list = erase_list->next;
1350 g_free (lp);
1353 erase_dir_iff_empty (ctx, s);
1355 ret:
1356 g_free (destdir);
1357 while (erase_list) {
1358 lp = erase_list;
1359 erase_list = erase_list->next;
1360 g_free (lp);
1362 return return_status;
1365 /* }}} */
1367 /* {{{ Erase routines */
1368 /* Don't update progress status if progress_count==NULL */
1370 erase_file (FileOpContext *ctx, char *s, off_t *progress_count,
1371 double *progress_bytes, int is_toplevel_file)
1373 int return_status;
1374 struct stat buf;
1376 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1377 return FILE_ABORT;
1378 mc_refresh ();
1380 if (progress_count && mc_lstat (s, &buf)) {
1381 /* ignore, most likely the mc_unlink fails, too */
1382 buf.st_size = 0;
1385 while (mc_unlink (s)) {
1386 return_status =
1387 file_error (_(" Cannot delete file \"%s\" \n %s "), s);
1388 if (return_status != FILE_RETRY)
1389 return return_status;
1392 if (progress_count)
1393 return progress_update_one (ctx, progress_count, progress_bytes,
1394 buf.st_size, is_toplevel_file);
1395 else
1396 return FILE_CONT;
1399 static int
1400 recursive_erase (FileOpContext *ctx, char *s, off_t *progress_count,
1401 double *progress_bytes)
1403 struct dirent *next;
1404 struct stat buf;
1405 DIR *reading;
1406 char *path;
1407 int return_status = FILE_CONT;
1409 if (!strcmp (s, ".."))
1410 return 1;
1412 reading = mc_opendir (s);
1414 if (!reading)
1415 return 1;
1417 while ((next = mc_readdir (reading)) && return_status == FILE_CONT) {
1418 if (!strcmp (next->d_name, "."))
1419 continue;
1420 if (!strcmp (next->d_name, ".."))
1421 continue;
1422 path = concat_dir_and_file (s, next->d_name);
1423 if (mc_lstat (path, &buf)) {
1424 g_free (path);
1425 mc_closedir (reading);
1426 return 1;
1428 if (S_ISDIR (buf.st_mode))
1429 return_status =
1430 (recursive_erase
1431 (ctx, path, progress_count, progress_bytes)
1432 != FILE_CONT);
1433 else
1434 return_status =
1435 erase_file (ctx, path, progress_count, progress_bytes, 0);
1436 g_free (path);
1438 mc_closedir (reading);
1439 if (return_status != FILE_CONT)
1440 return return_status;
1441 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1442 return FILE_ABORT;
1443 mc_refresh ();
1445 while (my_rmdir (s)) {
1446 return_status =
1447 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1448 if (return_status != FILE_RETRY)
1449 return return_status;
1452 return FILE_CONT;
1455 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1456 in the directory path points to, 0 else. */
1457 static int
1458 check_dir_is_empty (char *path)
1460 DIR *dir;
1461 struct dirent *d;
1462 int i;
1464 dir = mc_opendir (path);
1465 if (!dir)
1466 return -1;
1468 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir)) {
1469 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1470 (d->d_name[1] == '.'
1471 && d->d_name[2] == '\0')))
1472 continue; /* "." or ".." */
1473 i = 0;
1474 break;
1477 mc_closedir (dir);
1478 return i;
1482 erase_dir (FileOpContext *ctx, char *s, off_t *progress_count,
1483 double *progress_bytes)
1485 int error;
1487 if (strcmp (s, "..") == 0)
1488 return FILE_SKIP;
1490 if (strcmp (s, ".") == 0)
1491 return FILE_SKIP;
1493 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1494 return FILE_ABORT;
1495 mc_refresh ();
1497 /* The old way to detect a non empty directory was:
1498 error = my_rmdir (s);
1499 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1500 For the linux user space nfs server (nfs-server-2.2beta29-2)
1501 we would have to check also for EIO. I hope the new way is
1502 fool proof. (Norbert)
1504 error = check_dir_is_empty (s);
1505 if (error == 0) { /* not empty */
1506 error = query_recursive (ctx, s);
1507 if (error == FILE_CONT)
1508 return recursive_erase (ctx, s, progress_count,
1509 progress_bytes);
1510 else
1511 return error;
1514 while (my_rmdir (s) == -1) {
1515 error =
1516 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1517 if (error != FILE_RETRY)
1518 return error;
1521 return FILE_CONT;
1525 erase_dir_iff_empty (FileOpContext *ctx, char *s)
1527 int error;
1529 if (strcmp (s, "..") == 0)
1530 return FILE_SKIP;
1532 if (strcmp (s, ".") == 0)
1533 return FILE_SKIP;
1535 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1536 return FILE_ABORT;
1537 mc_refresh ();
1539 if (1 != check_dir_is_empty (s)) /* not empty or error */
1540 return FILE_CONT;
1542 while (my_rmdir (s)) {
1543 error =
1544 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1545 if (error != FILE_RETRY)
1546 return error;
1549 return FILE_CONT;
1552 /* }}} */
1554 /* {{{ Panel operate routines */
1556 /* Returns currently selected file or the first marked file if there is one */
1557 char *
1558 panel_get_file (WPanel *panel, struct stat *stat_buf)
1560 int i;
1562 /* No problem with Gnome, as get_current_type never returns view_tree there */
1563 if (get_current_type () == view_tree) {
1564 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
1566 mc_stat (tree->selected_ptr->name, stat_buf);
1567 return tree->selected_ptr->name;
1570 if (panel->marked) {
1571 for (i = 0; i < panel->count; i++)
1572 if (panel->dir.list[i].f.marked) {
1573 *stat_buf = panel->dir.list[i].buf;
1574 return panel->dir.list[i].fname;
1576 } else {
1577 *stat_buf = panel->dir.list[panel->selected].buf;
1578 return panel->dir.list[panel->selected].fname;
1580 g_assert_not_reached ();
1581 return NULL;
1585 is_wildcarded (char *p)
1587 for (; *p; p++) {
1588 if (*p == '*')
1589 return 1;
1590 else if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
1591 return 1;
1593 return 0;
1597 * compute_dir_size:
1599 * Computes the number of bytes used by the files in a directory
1601 void
1602 compute_dir_size (char *dirname, off_t *ret_marked, double *ret_total)
1604 DIR *dir;
1605 struct dirent *dirent;
1607 dir = mc_opendir (dirname);
1609 if (!dir)
1610 return;
1612 while ((dirent = mc_readdir (dir)) != NULL) {
1613 struct stat s;
1614 char *fullname;
1615 int res;
1617 if (strcmp (dirent->d_name, ".") == 0)
1618 continue;
1619 if (strcmp (dirent->d_name, "..") == 0)
1620 continue;
1622 fullname = concat_dir_and_file (dirname, dirent->d_name);
1624 res = mc_lstat (fullname, &s);
1626 if (res != 0) {
1627 g_free (fullname);
1628 continue;
1631 if (S_ISDIR (s.st_mode)) {
1632 off_t subdir_count = 0;
1633 double subdir_bytes = 0;
1635 compute_dir_size (fullname, &subdir_count, &subdir_bytes);
1637 *ret_marked += subdir_count;
1638 *ret_total += subdir_bytes;
1639 } else {
1640 (*ret_marked)++;
1641 *ret_total += s.st_size;
1643 g_free (fullname);
1646 mc_closedir (dir);
1650 * panel_compute_totals:
1652 * compute the number of files and the number of bytes
1653 * used up by the whole selection, recursing directories
1654 * as required. In addition, it checks to see if it will
1655 * overwrite any files by doing the copy.
1657 static void
1658 panel_compute_totals (WPanel *panel, off_t *ret_marked, double *ret_total)
1660 int i;
1662 *ret_marked = 0;
1663 *ret_total = 0.0;
1665 for (i = 0; i < panel->count; i++) {
1666 struct stat *s;
1668 if (!panel->dir.list[i].f.marked)
1669 continue;
1671 s = &panel->dir.list[i].buf;
1673 if (S_ISDIR (s->st_mode)) {
1674 char *dir_name;
1675 off_t subdir_count = 0;
1676 double subdir_bytes = 0;
1678 dir_name =
1679 concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1680 compute_dir_size (dir_name, &subdir_count, &subdir_bytes);
1682 *ret_marked += subdir_count;
1683 *ret_total += subdir_bytes;
1684 g_free (dir_name);
1685 } else {
1686 (*ret_marked)++;
1687 *ret_total += s->st_size;
1693 * This array introduced to avoid translation problems. The former (op_names)
1694 * is assumed to be nouns, suitable in dialog box titles; this one should
1695 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1696 * Notice first symbol - it is to fool gettext and force these strings to
1697 * be different for it. First symbol is skipped while building a prompt.
1698 * (I don't use spaces around the words, because someday they could be
1699 * dropped, when widgets get smarter)
1701 static char *op_names1[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1702 #define FMD_XLEN 64
1704 int fmd_xlen = FMD_XLEN;
1707 * These are formats for building a prompt. Parts encoded as follows:
1708 * %o - operation from op_names1
1709 * %f - file/files or files/directories, as appropriate
1710 * %m - "with source mask" or question mark for delete
1711 * %s - source name (truncated)
1712 * %d - number of marked files
1713 * %e - "to:" or question mark for delete
1715 * xgettext:no-c-format */
1716 static char *one_format = N_("%o %f \"%s\"%m");
1717 /* xgettext:no-c-format */
1718 static char *many_format = N_("%o %d %f%m");
1719 static char *prompt_parts[] = {
1720 N_("file"), N_("files"), N_("directory"), N_("directories"),
1721 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1724 static char *
1725 panel_operate_generate_prompt (WPanel *panel, int operation, int only_one,
1726 struct stat *src_stat)
1728 register char *sp, *cp;
1729 register int i;
1730 char format_string[BUF_MEDIUM];
1731 char *dp = format_string;
1732 char *source = NULL;
1734 #ifdef ENABLE_NLS
1735 static int i18n_flag = 0;
1736 if (!i18n_flag) {
1737 fmd_init_i18n (FALSE); /* to get proper fmd_xlen */
1739 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1740 op_names1[i] = _(op_names1[i]);
1742 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1743 prompt_parts[i] = _(prompt_parts[i]);
1745 one_format = _(one_format);
1746 many_format = _(many_format);
1747 i18n_flag = 1;
1749 #endif /* ENABLE_NLS */
1751 sp = only_one ? one_format : many_format;
1753 if (only_one)
1754 source = panel_get_file (panel, src_stat);
1756 while (*sp) {
1757 switch (*sp) {
1758 case '%':
1759 cp = NULL;
1760 switch (sp[1]) {
1761 case 'o':
1762 cp = op_names1[operation] + 1;
1763 break;
1764 case 'm':
1765 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1766 break;
1767 case 'e':
1768 cp = operation == OP_DELETE ? "?" : prompt_parts[6];
1769 break;
1770 case 'f':
1771 if (only_one) {
1772 cp = S_ISDIR (src_stat->st_mode) ?
1773 prompt_parts[2] : prompt_parts[0];
1774 } else {
1775 cp = (panel->marked == panel->dirs_marked)
1776 ? prompt_parts[3]
1777 : (panel->dirs_marked ? prompt_parts[4]
1778 : prompt_parts[1]);
1780 break;
1781 default:
1782 *dp++ = *sp++;
1784 if (cp) {
1785 sp += 2;
1786 while (*cp)
1787 *dp++ = *cp++;
1789 break;
1790 default:
1791 *dp++ = *sp++;
1794 *dp = '\0';
1796 if (only_one) {
1797 i = fmd_xlen - strlen (format_string) - 4;
1798 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1799 name_trunc (source, i));
1800 } else {
1801 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1802 panel->marked);
1803 i = strlen (cmd_buf) + 6 - fmd_xlen;
1804 if (i > 0) {
1805 fmd_xlen += i;
1806 fmd_init_i18n (TRUE); /* to recalculate positions of child widgets */
1810 return source;
1814 * panel_operate:
1816 * Performs one of the operations on the selection on the source_panel
1817 * (copy, delete, move).
1819 * Returns 1 if did change the directory
1820 * structure, Returns 0 if user aborted
1823 panel_operate (void *source_panel, FileOperation operation,
1824 char *thedefault, int ask_user)
1826 WPanel *panel = source_panel;
1827 #ifdef WITH_FULL_PATHS
1828 char *source_with_path = NULL;
1829 #else
1830 # define source_with_path source
1831 #endif /* !WITH_FULL_PATHS */
1832 char *source = NULL;
1833 char *dest = NULL;
1834 char *temp = NULL;
1835 char *save_cwd = NULL, *save_dest = NULL;
1836 int only_one = (get_current_type () == view_tree)
1837 || (panel->marked <= 1);
1838 struct stat src_stat, dst_stat;
1839 int i, value;
1840 FileOpContext *ctx;
1842 off_t count = 0;
1843 double bytes = 0;
1845 int dst_result;
1846 int do_bg; /* do background operation? */
1848 ctx = file_op_context_new ();
1850 do_bg = 0;
1851 ctx->rx.buffer = NULL;
1852 free_linklist (&linklist);
1853 free_linklist (&dest_dirs);
1854 if (get_current_type () == view_listing)
1855 if (!panel->marked && !strcmp (selection (panel)->fname, "..")) {
1856 message (1, MSG_ERROR, _(" Cannot operate on \"..\"! "));
1857 file_op_context_destroy (ctx);
1858 return 0;
1861 if (operation < OP_COPY || operation > OP_DELETE) {
1862 file_op_context_destroy (ctx);
1863 return 0;
1866 /* Generate confirmation prompt */
1867 source =
1868 panel_operate_generate_prompt (panel, operation, only_one,
1869 &src_stat);
1871 /* Show confirmation dialog */
1872 if (operation == OP_DELETE && confirm_delete) {
1873 if (safe_delete)
1874 query_set_sel (1);
1876 i = query_dialog (_(op_names[operation]), cmd_buf,
1877 D_ERROR, 2, _("&Yes"), _("&No"));
1879 if (i != 0) {
1880 file_op_context_destroy (ctx);
1881 return 0;
1883 } else if (operation != OP_DELETE) {
1884 ctx->rx.buffer = (char *) g_malloc (MC_MAXPATHLEN);
1885 ctx->rx.allocated = MC_MAXPATHLEN;
1886 ctx->rx.translate = 0;
1888 if (ask_user) {
1889 char *dest_dir;
1891 if (thedefault != NULL)
1892 dest_dir = thedefault;
1893 else if (get_other_type () == view_listing)
1894 dest_dir = opanel->cwd;
1895 else
1896 dest_dir = panel->cwd;
1898 dest =
1899 file_mask_dialog (ctx, operation, cmd_buf, dest_dir,
1900 only_one, &do_bg);
1901 if (!dest) {
1902 g_free (ctx->rx.buffer);
1903 file_op_context_destroy (ctx);
1904 return 0;
1906 if (!*dest) {
1907 g_free (ctx->rx.buffer);
1908 file_op_context_destroy (ctx);
1909 g_free (dest);
1910 return 0;
1912 } else {
1913 char *all = "^\\(.*\\)$";
1915 re_compile_pattern (all, strlen (all), &ctx->rx);
1916 ctx->dest_mask = g_strdup ("*");
1917 do_bg = FALSE;
1918 dest = g_strdup (thedefault);
1921 #ifdef WITH_BACKGROUND
1922 /* Did the user select to do a background operation? */
1923 if (do_bg) {
1924 int v;
1926 v = do_background (ctx,
1927 g_strconcat (op_names[operation], ": ",
1928 panel->cwd, NULL));
1929 if (v == -1) {
1930 message (1, MSG_ERROR,
1931 _(" Sorry, I could not put the job in background "));
1934 /* If we are the parent */
1935 if (v == 1) {
1936 mc_setctl (panel->cwd, MCCTL_FORGET_ABOUT, NULL);
1937 mc_setctl (dest, MCCTL_FORGET_ABOUT, NULL);
1938 /* file_op_context_destroy (ctx); */
1939 return 0;
1942 #endif /* WITH_BACKGROUND */
1944 /* Initialize things */
1945 /* We do not want to trash cache every time file is
1946 created/touched. However, this will make our cache contain
1947 invalid data. */
1948 if (dest) {
1949 if (mc_setctl (dest, MCCTL_WANT_STALE_DATA, NULL))
1950 save_dest = g_strdup (dest);
1952 if (panel->cwd) {
1953 if (mc_setctl (panel->cwd, MCCTL_WANT_STALE_DATA, NULL))
1954 save_cwd = g_strdup (panel->cwd);
1957 /* Now, let's do the job */
1959 if (do_bg)
1960 ctx->ui = NULL;
1961 else
1962 file_op_context_create_ui (ctx, operation, 1);
1964 /* This code is only called by the tree and panel code */
1965 if (only_one) {
1966 /* We now have ETA in all cases */
1968 /* One file: FIXME mc_chdir will take user out of any vfs */
1969 if (operation != OP_COPY && get_current_type () == view_tree)
1970 mc_chdir (PATH_SEP_STR);
1972 /* The source and src_stat variables have been initialized before */
1973 #ifdef WITH_FULL_PATHS
1974 source_with_path = concat_dir_and_file (panel->cwd, source);
1975 #endif /* WITH_FULL_PATHS */
1977 if (operation == OP_DELETE) {
1978 if (S_ISDIR (src_stat.st_mode))
1979 value = erase_dir (ctx, source_with_path, &count, &bytes);
1980 else
1981 value =
1982 erase_file (ctx, source_with_path, &count, &bytes, 1);
1983 } else {
1984 temp = transform_source (ctx, source_with_path);
1986 if (temp == NULL) {
1987 value = transform_error;
1988 } else {
1989 temp = concat_dir_and_file (dest, temp);
1990 g_free (dest);
1991 dest = temp;
1992 temp = 0;
1994 switch (operation) {
1995 case OP_COPY:
1997 * we use file_mask_op_follow_links only with OP_COPY,
1999 (*ctx->stat_func) (source_with_path, &src_stat);
2001 if (S_ISDIR (src_stat.st_mode))
2002 value =
2003 copy_dir_dir (ctx, source_with_path, dest, 1,
2004 0, 0, 0, &count, &bytes);
2005 else
2006 value =
2007 copy_file_file (ctx, source_with_path, dest, 1,
2008 &count, &bytes, 1);
2009 break;
2011 case OP_MOVE:
2012 if (S_ISDIR (src_stat.st_mode))
2013 value =
2014 move_dir_dir (ctx, source_with_path, dest,
2015 &count, &bytes);
2016 else
2017 value =
2018 move_file_file (ctx, source_with_path, dest,
2019 &count, &bytes);
2020 break;
2022 default:
2023 /* Unknown file operation */
2024 abort ();
2027 } /* Copy or move operation */
2029 if (value == FILE_CONT)
2030 unmark_files (panel);
2031 } else {
2032 /* Many files */
2033 /* Check destination for copy or move operation */
2034 if (operation != OP_DELETE) {
2035 retry_many_dst_stat:
2036 dst_result = mc_stat (dest, &dst_stat);
2037 if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)) {
2038 if (file_error
2039 (_(" Destination \"%s\" must be a directory \n %s "),
2040 dest) == FILE_RETRY)
2041 goto retry_many_dst_stat;
2042 goto clean_up;
2046 /* Initialize variables for progress bars */
2047 if (operation != OP_MOVE && verbose && file_op_compute_totals) {
2048 panel_compute_totals (panel, &ctx->progress_count,
2049 &ctx->progress_bytes);
2050 ctx->progress_totals_computed = 1;
2051 } else {
2052 ctx->progress_totals_computed = 0;
2053 ctx->progress_count = panel->marked;
2054 ctx->progress_bytes = panel->total;
2057 /* Loop for every file, perform the actual copy operation */
2058 for (i = 0; i < panel->count; i++) {
2059 if (!panel->dir.list[i].f.marked)
2060 continue; /* Skip the unmarked ones */
2062 source = panel->dir.list[i].fname;
2063 src_stat = panel->dir.list[i].buf;
2065 #ifdef WITH_FULL_PATHS
2066 if (source_with_path)
2067 g_free (source_with_path);
2068 source_with_path = concat_dir_and_file (panel->cwd, source);
2069 #endif /* WITH_FULL_PATHS */
2071 if (operation == OP_DELETE) {
2072 if (S_ISDIR (src_stat.st_mode))
2073 value =
2074 erase_dir (ctx, source_with_path, &count, &bytes);
2075 else
2076 value =
2077 erase_file (ctx, source_with_path, &count, &bytes,
2079 } else {
2080 if (temp)
2081 g_free (temp);
2083 temp = transform_source (ctx, source_with_path);
2084 if (temp == NULL)
2085 value = transform_error;
2086 else {
2087 temp = concat_dir_and_file (dest, temp);
2089 switch (operation) {
2090 case OP_COPY:
2092 * we use file_mask_op_follow_links only with OP_COPY,
2094 (*ctx->stat_func) (source_with_path, &src_stat);
2095 if (S_ISDIR (src_stat.st_mode))
2096 value =
2097 copy_dir_dir (ctx, source_with_path, temp,
2098 1, 0, 0, 0, &count, &bytes);
2099 else
2100 value =
2101 copy_file_file (ctx, source_with_path,
2102 temp, 1, &count, &bytes,
2104 free_linklist (&dest_dirs);
2105 break;
2107 case OP_MOVE:
2108 if (S_ISDIR (src_stat.st_mode))
2109 value =
2110 move_dir_dir (ctx, source_with_path, temp,
2111 &count, &bytes);
2112 else
2113 value =
2114 move_file_file (ctx, source_with_path,
2115 temp, &count, &bytes);
2116 break;
2118 default:
2119 /* Unknown file operation */
2120 abort ();
2123 } /* Copy or move operation */
2125 if (value == FILE_ABORT)
2126 goto clean_up;
2128 if (value == FILE_CONT)
2129 do_file_mark (panel, i, 0);
2131 if (file_progress_show_count (ctx, count, ctx->progress_count)
2132 == FILE_ABORT)
2133 goto clean_up;
2135 if (verbose
2136 && file_progress_show_bytes (ctx, bytes,
2137 ctx->progress_bytes) ==
2138 FILE_ABORT)
2139 goto clean_up;
2141 if (operation != OP_DELETE && verbose
2142 && file_progress_show (ctx, 0, 0) == FILE_ABORT)
2143 goto clean_up;
2145 mc_refresh ();
2146 } /* Loop for every file */
2147 } /* Many files */
2148 clean_up:
2149 /* Clean up */
2151 if (save_cwd) {
2152 mc_setctl (save_cwd, MCCTL_NO_STALE_DATA, NULL);
2153 g_free (save_cwd);
2155 if (save_dest) {
2156 mc_setctl (save_dest, MCCTL_NO_STALE_DATA, NULL);
2157 g_free (save_dest);
2160 free_linklist (&linklist);
2161 free_linklist (&dest_dirs);
2162 #ifdef WITH_FULL_PATHS
2163 if (source_with_path)
2164 g_free (source_with_path);
2165 #endif /* WITH_FULL_PATHS */
2167 if (dest)
2168 g_free (dest);
2170 if (temp)
2171 g_free (temp);
2173 if (ctx->rx.buffer) {
2174 g_free (ctx->rx.buffer);
2175 ctx->rx.buffer = NULL;
2178 if (ctx->dest_mask) {
2179 g_free (ctx->dest_mask);
2180 ctx->dest_mask = NULL;
2182 #ifdef WITH_BACKGROUND
2183 /* Let our parent know we are saying bye bye */
2184 if (we_are_background) {
2185 vfs_shut ();
2186 tell_parent (MSG_CHILD_EXITING);
2187 _exit (1);
2189 #endif /* WITH_BACKGROUND */
2191 file_op_context_destroy (ctx);
2192 return 1;
2195 /* }}} */
2197 /* {{{ Query/status report routines */
2199 static int
2200 real_do_file_error (enum OperationMode mode, char *error)
2202 int result;
2203 char *msg;
2205 msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
2206 result =
2207 query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"),
2208 _("&Abort"));
2210 switch (result) {
2211 case 0:
2212 do_refresh ();
2213 return FILE_SKIP;
2215 case 1:
2216 do_refresh ();
2217 return FILE_RETRY;
2219 case 2:
2220 default:
2221 return FILE_ABORT;
2225 /* Report error with one file */
2227 file_error (char *format, char *file)
2229 g_snprintf (cmd_buf, sizeof (cmd_buf), format,
2230 name_trunc (file, 30), unix_error_string (errno));
2232 return do_file_error (cmd_buf);
2235 /* Report error with two files */
2237 files_error (char *format, char *file1, char *file2)
2239 char nfile1[16];
2240 char nfile2[16];
2242 strcpy (nfile1, name_trunc (file1, 15));
2243 strcpy (nfile2, name_trunc (file2, 15));
2245 g_snprintf (cmd_buf, sizeof (cmd_buf), format, nfile1, nfile2,
2246 unix_error_string (errno));
2248 return do_file_error (cmd_buf);
2251 static int
2252 real_query_recursive (FileOpContext *ctx, enum OperationMode mode, char *s)
2254 gchar *text;
2256 if (ctx->recursive_result < RECURSIVE_ALWAYS) {
2257 char *msg =
2258 mode ==
2259 Foreground ?
2260 _("\n Directory not empty. \n"
2261 " Delete it recursively? ")
2262 : _("\n Background process: Directory not empty \n"
2263 " Delete it recursively? ");
2264 text = g_strconcat (_(" Delete: "), name_trunc (s, 30), " ", NULL);
2266 if (safe_delete)
2267 query_set_sel (1);
2268 ctx->recursive_result = query_dialog (text, msg, D_ERROR, 5,
2269 _("&Yes"), _("&No"),
2270 _("A&ll"), _("Non&e"),
2271 _("&Abort"));
2273 if (ctx->recursive_result != RECURSIVE_ABORT)
2274 do_refresh ();
2275 g_free (text);
2278 switch (ctx->recursive_result) {
2279 case RECURSIVE_YES:
2280 case RECURSIVE_ALWAYS:
2281 return FILE_CONT;
2283 case RECURSIVE_NO:
2284 case RECURSIVE_NEVER:
2285 return FILE_SKIP;
2287 case RECURSIVE_ABORT:
2289 default:
2290 return FILE_ABORT;
2294 #ifdef WITH_BACKGROUND
2295 static int
2296 do_file_error (char *str)
2298 if (we_are_background)
2299 return parent_call (real_do_file_error, NULL, 1, strlen (str),
2300 str);
2301 else
2302 return real_do_file_error (Foreground, str);
2305 static int
2306 query_recursive (FileOpContext *ctx, char *s)
2308 if (we_are_background)
2309 return parent_call (real_query_recursive, ctx, 1, strlen (s), s);
2310 else
2311 return real_query_recursive (ctx, Foreground, s);
2314 static int
2315 query_replace (FileOpContext *ctx, char *destname, struct stat *_s_stat,
2316 struct stat *_d_stat)
2318 if (we_are_background)
2319 return parent_call ((void *) file_progress_real_query_replace,
2320 ctx,
2322 strlen (destname), destname,
2323 sizeof (struct stat), _s_stat,
2324 sizeof (struct stat), _d_stat);
2325 else
2326 return file_progress_real_query_replace (ctx, Foreground, destname,
2327 _s_stat, _d_stat);
2330 #else
2331 static int
2332 do_file_error (char *str)
2334 return real_do_file_error (Foreground, str);
2337 static int
2338 query_recursive (FileOpContext *ctx, char *s)
2340 return real_query_recursive (ctx, Foreground, s);
2343 static int
2344 query_replace (FileOpContext *ctx, char *destname, struct stat *_s_stat,
2345 struct stat *_d_stat)
2347 return file_progress_real_query_replace (ctx, Foreground, destname,
2348 _s_stat, _d_stat);
2351 #endif /* !WITH_BACKGROUND */
2354 Cause emacs to enter folding mode for this file:
2355 Local variables:
2356 end: