Just a little correction at the it.po file.
[midnight-commander.git] / src / file.c
blob7c37e2ebbfdfe412494fb617e6bf69190d48dff5
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, _(" Cannot 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 char *t = buf;
719 n_read_total += n_read;
721 /* Windows NT ftp servers report that files have no
722 * permissions: -------, so if we happen to have actually
723 * read something, we should fix the permissions.
725 if (!(src_mode & ((S_IRUSR | S_IWUSR | S_IXUSR) /* user */
726 |(S_IXOTH | S_IWOTH | S_IROTH) /* other */
727 |(S_IXGRP | S_IWGRP | S_IRGRP)))) /* group */
728 src_mode = S_IRUSR | S_IWUSR | S_IROTH | S_IRGRP;
729 gettimeofday (&tv_last_input, NULL);
731 /* dst_write */
732 while ((n_written =
733 mc_write (dest_desc, t, n_read)) < n_read) {
734 if (n_written > 0) {
735 n_read -= n_written;
736 t += n_written;
737 continue;
739 return_status =
740 file_error (_
741 (" Cannot write target file \"%s\" \n %s "),
742 dst_path);
743 if (return_status != FILE_RETRY)
744 goto ret;
748 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
749 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
750 if (secs > 2) {
751 rotate_dash ();
752 tv_last_update = tv_current;
755 /* 2. Check for a stalled condition */
756 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
757 stalled_msg = "";
758 if (update_secs > 4) {
759 stalled_msg = _("(stalled)");
762 /* 3. Compute ETA */
763 if (secs > 2) {
764 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
766 if (n_read_total) {
767 ctx->eta_secs =
768 ((dt / (double) n_read_total) * file_size) - dt;
769 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
770 } else
771 ctx->eta_secs = 0.0;
774 /* 4. Compute BPS rate */
775 if (secs > 2) {
776 ctx->bps_time =
777 (tv_current.tv_sec - tv_transfer_start.tv_sec);
778 if (ctx->bps_time < 1)
779 ctx->bps_time = 1;
780 ctx->bps = n_read_total / ctx->bps_time;
783 file_progress_set_stalled_label (ctx, stalled_msg);
784 return_status =
785 file_progress_show_bytes (ctx,
786 *progress_bytes + n_read_total,
787 ctx->progress_bytes);
788 if (return_status == FILE_CONT) {
789 return_status =
790 file_progress_show (ctx, n_read_total, file_size);
792 mc_refresh ();
793 if (return_status != FILE_CONT)
794 goto ret;
798 dst_status = DEST_FULL; /* copy successful, don't remove target file */
800 ret:
801 if (buf)
802 g_free (buf);
804 while (src_desc != -1 && mc_close (src_desc) < 0) {
805 temp_status =
806 file_error (_(" Cannot close source file \"%s\" \n %s "),
807 src_path);
808 if (temp_status == FILE_RETRY)
809 continue;
810 if (temp_status == FILE_ABORT)
811 return_status = temp_status;
812 break;
815 while (dest_desc != -1 && mc_close (dest_desc) < 0) {
816 temp_status =
817 file_error (_(" Cannot close target file \"%s\" \n %s "),
818 dst_path);
819 if (temp_status == FILE_RETRY)
820 continue;
821 return_status = temp_status;
822 break;
825 if (dst_status == DEST_SHORT) {
826 /* Remove short file */
827 int result;
828 result =
829 query_dialog (_("Copy"),
830 _("Incomplete file was retrieved. Keep it?"),
831 D_ERROR, 2, _("&Delete"), _("&Keep"));
832 if (!result)
833 mc_unlink (dst_path);
834 } else if (dst_status == DEST_FULL) {
835 /* Copy has succeeded */
836 #ifndef NATIVE_WIN32
837 if (!appending && ctx->preserve_uidgid) {
838 while (mc_chown (dst_path, src_uid, src_gid)) {
839 temp_status = file_error
840 (_(" Cannot chown target file \"%s\" \n %s "),
841 dst_path);
842 if (temp_status == FILE_RETRY)
843 continue;
844 return_status = temp_status;
845 break;
848 #endif /* !NATIVE_WIN32 */
851 * .ado: according to the XPG4 standard, the file must be closed before
852 * chmod can be invoked
854 if (!appending && ctx->preserve) {
855 while (mc_chmod (dst_path, src_mode & ctx->umask_kill)) {
856 temp_status =
857 file_error (_
858 (" Cannot chmod target file \"%s\" \n %s "),
859 dst_path);
860 if (temp_status != FILE_RETRY) {
861 return_status = temp_status;
862 break;
865 mc_utime (dst_path, &utb);
869 if (return_status == FILE_CONT)
870 return_status =
871 progress_update_one (ctx, progress_count, progress_bytes,
872 file_size, is_toplevel_file);
874 return return_status;
878 * I think these copy_*_* functions should have a return type.
879 * anyway, this function *must* have two directories as arguments.
881 /* FIXME: This function needs to check the return values of the
882 function calls */
884 copy_dir_dir (FileOpContext *ctx, char *s, char *d, int toplevel,
885 int move_over, int delete,
886 struct link *parent_dirs,
887 off_t *progress_count, double *progress_bytes)
889 struct dirent *next;
890 struct stat buf, cbuf;
891 DIR *reading;
892 char *path, *mdpath, *dest_file, *dest_dir;
893 int return_status = FILE_CONT;
894 struct utimbuf utb;
895 struct link *lp;
897 /* First get the mode of the source dir */
898 retry_src_stat:
899 if ((*ctx->stat_func) (s, &cbuf)) {
900 return_status =
901 file_error (_(" Cannot stat source directory \"%s\" \n %s "),
903 if (return_status == FILE_RETRY)
904 goto retry_src_stat;
905 return return_status;
908 if (is_in_linklist (dest_dirs, s, &cbuf)) {
909 /* Don't copy a directory we created before (we don't want to copy
910 infinitely if a directory is copied into itself) */
911 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
912 return FILE_CONT;
915 /* Hmm, hardlink to directory??? - Norbert */
916 /* FIXME: In this step we should do something
917 in case the destination already exist */
918 /* Check the hardlinks */
919 if (ctx->preserve && cbuf.st_nlink > 1
920 && check_hardlinks (s, d, &cbuf) == 1) {
921 /* We have made a hardlink - no more processing is necessary */
922 return return_status;
925 if (!S_ISDIR (cbuf.st_mode)) {
926 return_status =
927 file_error (_
928 (" Source directory \"%s\" is not a directory \n %s "),
930 if (return_status == FILE_RETRY)
931 goto retry_src_stat;
932 return return_status;
935 if (is_in_linklist (parent_dirs, s, &cbuf)) {
936 /* we found a cyclic symbolic link */
937 message_2s (1, MSG_ERROR,
938 _(" Cannot copy cyclic symbolic link \n `%s' "), s);
939 return FILE_SKIP;
942 lp = g_new (struct link, 1);
943 lp->vfs = vfs_type (s);
944 lp->ino = cbuf.st_ino;
945 lp->dev = cbuf.st_dev;
946 lp->next = parent_dirs;
947 parent_dirs = lp;
949 retry_dst_stat:
950 /* Now, check if the dest dir exists, if not, create it. */
951 if (mc_stat (d, &buf)) {
952 /* Here the dir doesn't exist : make it ! */
954 if (move_over) {
955 if (mc_rename (s, d) == 0) {
956 g_free (parent_dirs);
957 return FILE_CONT;
960 dest_dir = g_strdup (d);
961 } else {
963 * If the destination directory exists, we want to copy the whole
964 * directory, but we only want this to happen once.
966 * Escape sequences added to the * to compiler warnings.
967 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
968 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
970 if (!S_ISDIR (buf.st_mode)) {
971 return_status =
972 file_error (_
973 (" Destination \"%s\" must be a directory \n %s "),
975 if (return_status == FILE_RETRY)
976 goto retry_dst_stat;
977 g_free (parent_dirs);
978 return return_status;
980 #if 1
981 /* Again, I'm getting curious. Is not d already what we wanted, incl.
982 * masked source basename? Is not this just a relict of the past versions?
983 * I'm afraid this will lead into a two level deep dive :(
985 * I think this is indeed the problem. I cannot remember any case where
986 * we actually would like that behaviour -miguel
988 * It's a documented feature (option `Dive into subdir if exists' in the
989 * copy/move dialog). -Norbert
991 if (toplevel && ctx->dive_into_subdirs) {
992 dest_dir = concat_dir_and_file (d, x_basename (s));
993 } else
994 #endif
996 dest_dir = g_strdup (d);
997 goto dont_mkdir;
1000 retry_dst_mkdir:
1001 if (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU)) {
1002 return_status =
1003 file_error (_(" Cannot create target directory \"%s\" \n %s "),
1004 dest_dir);
1005 if (return_status == FILE_RETRY)
1006 goto retry_dst_mkdir;
1007 goto ret;
1010 lp = g_new (struct link, 1);
1011 mc_stat (dest_dir, &buf);
1012 lp->vfs = vfs_type (dest_dir);
1013 lp->ino = buf.st_ino;
1014 lp->dev = buf.st_dev;
1015 lp->next = dest_dirs;
1016 dest_dirs = lp;
1018 #ifndef NATIVE_WIN32
1019 if (ctx->preserve_uidgid) {
1020 while (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid)) {
1021 return_status =
1022 file_error (_
1023 (" Cannot chown target directory \"%s\" \n %s "),
1024 dest_dir);
1025 if (return_status != FILE_RETRY)
1026 goto ret;
1029 #endif /* !NATIVE_WIN32 */
1031 dont_mkdir:
1032 /* open the source dir for reading */
1033 if ((reading = mc_opendir (s)) == 0) {
1034 goto ret;
1037 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT) {
1039 * Now, we don't want '.' and '..' to be created / copied at any time
1041 if (!strcmp (next->d_name, "."))
1042 continue;
1043 if (!strcmp (next->d_name, ".."))
1044 continue;
1046 /* get the filename and add it to the src directory */
1047 path = concat_dir_and_file (s, next->d_name);
1049 (*ctx->stat_func) (path, &buf);
1050 if (S_ISDIR (buf.st_mode)) {
1051 mdpath = concat_dir_and_file (dest_dir, next->d_name);
1053 * From here, we just intend to recursively copy subdirs, not
1054 * the double functionality of copying different when the target
1055 * dir already exists. So, we give the recursive call the flag 0
1056 * meaning no toplevel.
1058 return_status = copy_dir_dir (ctx, path, mdpath, 0, 0,
1059 delete, parent_dirs,
1060 progress_count, progress_bytes);
1061 g_free (mdpath);
1062 } else {
1063 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
1064 return_status = copy_file_file (ctx, path, dest_file, 1,
1065 progress_count, progress_bytes,
1067 g_free (dest_file);
1069 if (delete && return_status == FILE_CONT) {
1070 if (ctx->erase_at_end) {
1071 static struct link *tail;
1072 lp = g_malloc (sizeof (struct link) + strlen (path));
1073 strcpy (lp->name, path);
1074 lp->st_mode = buf.st_mode;
1075 lp->next = 0;
1076 if (erase_list) {
1077 tail->next = lp;
1078 tail = lp;
1079 } else
1080 erase_list = tail = lp;
1081 } else {
1082 if (S_ISDIR (buf.st_mode)) {
1083 return_status = erase_dir_iff_empty (ctx, path);
1084 } else
1085 return_status = erase_file (ctx, path, 0, 0, 0);
1089 g_free (path);
1091 mc_closedir (reading);
1093 if (ctx->preserve) {
1094 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1095 utb.actime = cbuf.st_atime;
1096 utb.modtime = cbuf.st_mtime;
1097 mc_utime (dest_dir, &utb);
1100 ret:
1101 g_free (dest_dir);
1102 g_free (parent_dirs);
1103 return return_status;
1106 /* }}} */
1108 /* {{{ Move routines */
1111 move_file_file (FileOpContext *ctx, char *s, char *d,
1112 off_t *progress_count, double *progress_bytes)
1114 struct stat src_stats, dst_stats;
1115 int return_status = FILE_CONT;
1117 if (file_progress_show_source (ctx, s) == FILE_ABORT
1118 || file_progress_show_target (ctx, d) == FILE_ABORT)
1119 return FILE_ABORT;
1121 mc_refresh ();
1123 while (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 return return_status;
1131 if (mc_lstat (d, &dst_stats) == 0) {
1132 /* Destination already exists */
1133 /* .ado: for Win32, no st_ino exists */
1134 #ifndef NATIVE_WIN32
1135 if (src_stats.st_dev == dst_stats.st_dev
1136 && src_stats.st_ino == dst_stats.st_ino) {
1137 int msize = COLS - 36;
1138 char st[MC_MAXPATHLEN];
1139 char dt[MC_MAXPATHLEN];
1141 if (msize < 0)
1142 msize = 40;
1143 msize /= 2;
1145 strcpy (st, name_trunc (s, msize));
1146 strcpy (dt, name_trunc (d, msize));
1147 message_3s (1, MSG_ERROR,
1148 _(" `%s' and `%s' are the same file "), st, dt);
1149 do_refresh ();
1150 return FILE_SKIP;
1152 #endif /* !NATIVE_WIN32 */
1153 if (S_ISDIR (dst_stats.st_mode)) {
1154 message_2s (1, MSG_ERROR,
1155 _(" Cannot overwrite directory `%s' "), d);
1156 do_refresh ();
1157 return FILE_SKIP;
1160 if (confirm_overwrite) {
1161 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1162 if (return_status != FILE_CONT)
1163 return return_status;
1165 /* Ok to overwrite */
1168 if (!ctx->do_append) {
1169 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks) {
1170 if ((return_status = make_symlink (ctx, s, d)) == FILE_CONT) {
1171 goto retry_src_remove;
1172 } else
1173 return return_status;
1176 if (mc_rename (s, d) == 0) {
1177 return FILE_CONT;
1180 #if 0
1181 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1182 one nfs to the same, but on the server it is on two different
1183 filesystems. Then nfs returns EIO instead of EXDEV.
1184 Hope it will not hurt if we always in case of error try to copy/delete. */
1185 else
1186 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1188 if (errno != EXDEV) {
1189 return_status =
1190 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s,
1192 if (return_status == FILE_RETRY)
1193 goto retry_rename;
1194 return return_status;
1196 #endif
1198 /* Failed because filesystem boundary -> copy the file instead */
1199 return_status =
1200 copy_file_file (ctx, s, d, 0, progress_count, progress_bytes, 1);
1201 if (return_status != FILE_CONT)
1202 return return_status;
1204 if ((return_status =
1205 file_progress_show_source (ctx, NULL)) != FILE_CONT
1206 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1207 return return_status;
1209 mc_refresh ();
1211 retry_src_remove:
1212 if (mc_unlink (s)) {
1213 return_status =
1214 file_error (_(" Cannot remove file \"%s\" \n %s "), s);
1215 if (return_status == FILE_RETRY)
1216 goto retry_src_remove;
1217 return return_status;
1220 if (return_status == FILE_CONT)
1221 return_status = progress_update_one (ctx,
1222 progress_count,
1223 progress_bytes,
1224 src_stats.st_size, 1);
1226 return return_status;
1230 move_dir_dir (FileOpContext *ctx, char *s, char *d,
1231 off_t *progress_count, double *progress_bytes)
1233 struct stat sbuf, dbuf, destbuf;
1234 struct link *lp;
1235 char *destdir;
1236 int return_status;
1237 int move_over = 0;
1239 if (file_progress_show_source (ctx, s) == FILE_ABORT ||
1240 file_progress_show_target (ctx, d) == FILE_ABORT)
1241 return FILE_ABORT;
1243 mc_refresh ();
1245 mc_stat (s, &sbuf);
1246 if (mc_stat (d, &dbuf))
1247 destdir = g_strdup (d); /* destination doesn't exist */
1248 else if (!ctx->dive_into_subdirs) {
1249 destdir = g_strdup (d);
1250 move_over = 1;
1251 } else
1252 destdir = concat_dir_and_file (d, x_basename (s));
1253 #ifndef NATIVE_WIN32
1254 if (sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino) {
1255 int msize = COLS - 36;
1256 char st[MC_MAXPATHLEN];
1257 char dt[MC_MAXPATHLEN];
1259 if (msize < 0)
1260 msize = 40;
1261 msize /= 2;
1263 strcpy (st, name_trunc (s, msize));
1264 strcpy (dt, name_trunc (d, msize));
1265 message_3s (1, MSG_ERROR,
1266 _(" `%s' and `%s' are the same directory "), st, dt);
1267 do_refresh ();
1268 return FILE_SKIP;
1270 #endif /* !NATIVE_WIN32 */
1272 /* Check if the user inputted an existing dir */
1273 retry_dst_stat:
1274 if (!mc_stat (destdir, &destbuf)) {
1275 if (move_over) {
1276 return_status = copy_dir_dir (ctx, s, destdir, 0, 1, 1, 0,
1277 progress_count, progress_bytes);
1279 if (return_status != FILE_CONT)
1280 goto ret;
1281 goto oktoret;
1282 } else {
1283 if (S_ISDIR (destbuf.st_mode))
1284 return_status =
1285 file_error (_
1286 (" Cannot overwrite directory \"%s\" %s "),
1287 destdir);
1288 else
1289 return_status =
1290 file_error (_(" Cannot overwrite file \"%s\" %s "),
1291 destdir);
1292 if (return_status == FILE_RETRY)
1293 goto retry_dst_stat;
1295 g_free (destdir);
1296 return return_status;
1299 retry_rename:
1300 if (mc_rename (s, destdir) == 0) {
1301 return_status = FILE_CONT;
1302 goto ret;
1304 /* .ado: Drive, Do we need this anymore? */
1305 #ifdef WIN32
1306 else {
1307 /* EXDEV: cross device; does not work everywhere */
1308 if (toupper (s[0]) != toupper (destdir[0]))
1309 goto w32try;
1311 #endif /* WIN32 */
1313 if (errno != EXDEV) {
1314 return_status =
1315 files_error (_
1316 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1317 s, d);
1318 if (return_status == FILE_RETRY)
1319 goto retry_rename;
1320 goto ret;
1322 #ifdef WIN32
1323 w32try:
1324 #endif /* WIN32 */
1325 /* Failed because of filesystem boundary -> copy dir instead */
1326 return_status =
1327 copy_dir_dir (ctx, s, destdir, 0, 0, 1, 0, progress_count,
1328 progress_bytes);
1330 if (return_status != FILE_CONT)
1331 goto ret;
1332 oktoret:
1333 if ((return_status =
1334 file_progress_show_source (ctx, NULL)) != FILE_CONT
1335 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1336 goto ret;
1338 mc_refresh ();
1339 if (ctx->erase_at_end) {
1340 for (; erase_list && return_status != FILE_ABORT;) {
1341 if (S_ISDIR (erase_list->st_mode)) {
1342 return_status =
1343 erase_dir_iff_empty (ctx, erase_list->name);
1344 } else
1345 return_status =
1346 erase_file (ctx, erase_list->name, 0, 0, 0);
1347 lp = erase_list;
1348 erase_list = erase_list->next;
1349 g_free (lp);
1352 erase_dir_iff_empty (ctx, s);
1354 ret:
1355 g_free (destdir);
1356 while (erase_list) {
1357 lp = erase_list;
1358 erase_list = erase_list->next;
1359 g_free (lp);
1361 return return_status;
1364 /* }}} */
1366 /* {{{ Erase routines */
1367 /* Don't update progress status if progress_count==NULL */
1369 erase_file (FileOpContext *ctx, char *s, off_t *progress_count,
1370 double *progress_bytes, int is_toplevel_file)
1372 int return_status;
1373 struct stat buf;
1375 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1376 return FILE_ABORT;
1377 mc_refresh ();
1379 if (progress_count && mc_lstat (s, &buf)) {
1380 /* ignore, most likely the mc_unlink fails, too */
1381 buf.st_size = 0;
1384 while (mc_unlink (s)) {
1385 return_status =
1386 file_error (_(" Cannot delete file \"%s\" \n %s "), s);
1387 if (return_status != FILE_RETRY)
1388 return return_status;
1391 if (progress_count)
1392 return progress_update_one (ctx, progress_count, progress_bytes,
1393 buf.st_size, is_toplevel_file);
1394 else
1395 return FILE_CONT;
1398 static int
1399 recursive_erase (FileOpContext *ctx, char *s, off_t *progress_count,
1400 double *progress_bytes)
1402 struct dirent *next;
1403 struct stat buf;
1404 DIR *reading;
1405 char *path;
1406 int return_status = FILE_CONT;
1408 if (!strcmp (s, ".."))
1409 return 1;
1411 reading = mc_opendir (s);
1413 if (!reading)
1414 return 1;
1416 while ((next = mc_readdir (reading)) && return_status == FILE_CONT) {
1417 if (!strcmp (next->d_name, "."))
1418 continue;
1419 if (!strcmp (next->d_name, ".."))
1420 continue;
1421 path = concat_dir_and_file (s, next->d_name);
1422 if (mc_lstat (path, &buf)) {
1423 g_free (path);
1424 mc_closedir (reading);
1425 return 1;
1427 if (S_ISDIR (buf.st_mode))
1428 return_status =
1429 (recursive_erase
1430 (ctx, path, progress_count, progress_bytes)
1431 != FILE_CONT);
1432 else
1433 return_status =
1434 erase_file (ctx, path, progress_count, progress_bytes, 0);
1435 g_free (path);
1437 mc_closedir (reading);
1438 if (return_status != FILE_CONT)
1439 return return_status;
1440 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1441 return FILE_ABORT;
1442 mc_refresh ();
1444 while (my_rmdir (s)) {
1445 return_status =
1446 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1447 if (return_status != FILE_RETRY)
1448 return return_status;
1451 return FILE_CONT;
1454 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1455 in the directory path points to, 0 else. */
1456 static int
1457 check_dir_is_empty (char *path)
1459 DIR *dir;
1460 struct dirent *d;
1461 int i;
1463 dir = mc_opendir (path);
1464 if (!dir)
1465 return -1;
1467 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir)) {
1468 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1469 (d->d_name[1] == '.'
1470 && d->d_name[2] == '\0')))
1471 continue; /* "." or ".." */
1472 i = 0;
1473 break;
1476 mc_closedir (dir);
1477 return i;
1481 erase_dir (FileOpContext *ctx, char *s, off_t *progress_count,
1482 double *progress_bytes)
1484 int error;
1486 if (strcmp (s, "..") == 0)
1487 return FILE_SKIP;
1489 if (strcmp (s, ".") == 0)
1490 return FILE_SKIP;
1492 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1493 return FILE_ABORT;
1494 mc_refresh ();
1496 /* The old way to detect a non empty directory was:
1497 error = my_rmdir (s);
1498 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1499 For the linux user space nfs server (nfs-server-2.2beta29-2)
1500 we would have to check also for EIO. I hope the new way is
1501 fool proof. (Norbert)
1503 error = check_dir_is_empty (s);
1504 if (error == 0) { /* not empty */
1505 error = query_recursive (ctx, s);
1506 if (error == FILE_CONT)
1507 return recursive_erase (ctx, s, progress_count,
1508 progress_bytes);
1509 else
1510 return error;
1513 while (my_rmdir (s) == -1) {
1514 error =
1515 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1516 if (error != FILE_RETRY)
1517 return error;
1520 return FILE_CONT;
1524 erase_dir_iff_empty (FileOpContext *ctx, char *s)
1526 int error;
1528 if (strcmp (s, "..") == 0)
1529 return FILE_SKIP;
1531 if (strcmp (s, ".") == 0)
1532 return FILE_SKIP;
1534 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1535 return FILE_ABORT;
1536 mc_refresh ();
1538 if (1 != check_dir_is_empty (s)) /* not empty or error */
1539 return FILE_CONT;
1541 while (my_rmdir (s)) {
1542 error =
1543 file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1544 if (error != FILE_RETRY)
1545 return error;
1548 return FILE_CONT;
1551 /* }}} */
1553 /* {{{ Panel operate routines */
1555 /* Returns currently selected file or the first marked file if there is one */
1556 static char *
1557 panel_get_file (WPanel *panel, struct stat *stat_buf)
1559 int i;
1561 if (get_current_type () == view_tree) {
1562 WTree *tree = (WTree *) get_panel_widget (get_current_index ());
1563 char *tree_name = tree_selected_name (tree);
1565 mc_stat (tree_name, stat_buf);
1566 return tree_name;
1569 if (panel->marked) {
1570 for (i = 0; i < panel->count; i++)
1571 if (panel->dir.list[i].f.marked) {
1572 *stat_buf = panel->dir.list[i].buf;
1573 return panel->dir.list[i].fname;
1575 } else {
1576 *stat_buf = panel->dir.list[panel->selected].buf;
1577 return panel->dir.list[panel->selected].fname;
1579 g_assert_not_reached ();
1580 return NULL;
1584 * compute_dir_size:
1586 * Computes the number of bytes used by the files in a directory
1588 void
1589 compute_dir_size (char *dirname, off_t *ret_marked, double *ret_total)
1591 DIR *dir;
1592 struct dirent *dirent;
1594 dir = mc_opendir (dirname);
1596 if (!dir)
1597 return;
1599 while ((dirent = mc_readdir (dir)) != NULL) {
1600 struct stat s;
1601 char *fullname;
1602 int res;
1604 if (strcmp (dirent->d_name, ".") == 0)
1605 continue;
1606 if (strcmp (dirent->d_name, "..") == 0)
1607 continue;
1609 fullname = concat_dir_and_file (dirname, dirent->d_name);
1611 res = mc_lstat (fullname, &s);
1613 if (res != 0) {
1614 g_free (fullname);
1615 continue;
1618 if (S_ISDIR (s.st_mode)) {
1619 off_t subdir_count = 0;
1620 double subdir_bytes = 0;
1622 compute_dir_size (fullname, &subdir_count, &subdir_bytes);
1624 *ret_marked += subdir_count;
1625 *ret_total += subdir_bytes;
1626 } else {
1627 (*ret_marked)++;
1628 *ret_total += s.st_size;
1630 g_free (fullname);
1633 mc_closedir (dir);
1637 * panel_compute_totals:
1639 * compute the number of files and the number of bytes
1640 * used up by the whole selection, recursing directories
1641 * as required. In addition, it checks to see if it will
1642 * overwrite any files by doing the copy.
1644 static void
1645 panel_compute_totals (WPanel *panel, off_t *ret_marked, double *ret_total)
1647 int i;
1649 *ret_marked = 0;
1650 *ret_total = 0.0;
1652 for (i = 0; i < panel->count; i++) {
1653 struct stat *s;
1655 if (!panel->dir.list[i].f.marked)
1656 continue;
1658 s = &panel->dir.list[i].buf;
1660 if (S_ISDIR (s->st_mode)) {
1661 char *dir_name;
1662 off_t subdir_count = 0;
1663 double subdir_bytes = 0;
1665 dir_name =
1666 concat_dir_and_file (panel->cwd, panel->dir.list[i].fname);
1667 compute_dir_size (dir_name, &subdir_count, &subdir_bytes);
1669 *ret_marked += subdir_count;
1670 *ret_total += subdir_bytes;
1671 g_free (dir_name);
1672 } else {
1673 (*ret_marked)++;
1674 *ret_total += s->st_size;
1680 * This array introduced to avoid translation problems. The former (op_names)
1681 * is assumed to be nouns, suitable in dialog box titles; this one should
1682 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1683 * Notice first symbol - it is to fool gettext and force these strings to
1684 * be different for it. First symbol is skipped while building a prompt.
1685 * (I don't use spaces around the words, because someday they could be
1686 * dropped, when widgets get smarter)
1688 static char *op_names1[] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1689 #define FMD_XLEN 64
1691 int fmd_xlen = FMD_XLEN;
1694 * These are formats for building a prompt. Parts encoded as follows:
1695 * %o - operation from op_names1
1696 * %f - file/files or files/directories, as appropriate
1697 * %m - "with source mask" or question mark for delete
1698 * %s - source name (truncated)
1699 * %d - number of marked files
1700 * %e - "to:" or question mark for delete
1702 * xgettext:no-c-format */
1703 static char *one_format = N_("%o %f \"%s\"%m");
1704 /* xgettext:no-c-format */
1705 static char *many_format = N_("%o %d %f%m");
1706 static char *prompt_parts[] = {
1707 N_("file"), N_("files"), N_("directory"), N_("directories"),
1708 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1711 static char *
1712 panel_operate_generate_prompt (WPanel *panel, int operation, int only_one,
1713 struct stat *src_stat)
1715 register char *sp, *cp;
1716 register int i;
1717 char format_string[BUF_MEDIUM];
1718 char *dp = format_string;
1719 char *source = NULL;
1721 #ifdef ENABLE_NLS
1722 static int i18n_flag = 0;
1723 if (!i18n_flag) {
1724 fmd_init_i18n (FALSE); /* to get proper fmd_xlen */
1726 for (i = sizeof (op_names1) / sizeof (op_names1[0]); i--;)
1727 op_names1[i] = _(op_names1[i]);
1729 for (i = sizeof (prompt_parts) / sizeof (prompt_parts[0]); i--;)
1730 prompt_parts[i] = _(prompt_parts[i]);
1732 one_format = _(one_format);
1733 many_format = _(many_format);
1734 i18n_flag = 1;
1736 #endif /* ENABLE_NLS */
1738 sp = only_one ? one_format : many_format;
1740 if (only_one)
1741 source = panel_get_file (panel, src_stat);
1743 while (*sp) {
1744 switch (*sp) {
1745 case '%':
1746 cp = NULL;
1747 switch (sp[1]) {
1748 case 'o':
1749 cp = op_names1[operation] + 1;
1750 break;
1751 case 'm':
1752 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1753 break;
1754 case 'e':
1755 cp = operation == OP_DELETE ? "?" : prompt_parts[6];
1756 break;
1757 case 'f':
1758 if (only_one) {
1759 cp = S_ISDIR (src_stat->st_mode) ?
1760 prompt_parts[2] : prompt_parts[0];
1761 } else {
1762 cp = (panel->marked == panel->dirs_marked)
1763 ? prompt_parts[3]
1764 : (panel->dirs_marked ? prompt_parts[4]
1765 : prompt_parts[1]);
1767 break;
1768 default:
1769 *dp++ = *sp++;
1771 if (cp) {
1772 sp += 2;
1773 while (*cp)
1774 *dp++ = *cp++;
1776 break;
1777 default:
1778 *dp++ = *sp++;
1781 *dp = '\0';
1783 if (only_one) {
1784 i = fmd_xlen - strlen (format_string) - 4;
1785 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1786 name_trunc (source, i));
1787 } else {
1788 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string,
1789 panel->marked);
1790 i = strlen (cmd_buf) + 6 - fmd_xlen;
1791 if (i > 0) {
1792 fmd_xlen += i;
1793 fmd_init_i18n (TRUE); /* to recalculate positions of child widgets */
1797 return source;
1801 * panel_operate:
1803 * Performs one of the operations on the selection on the source_panel
1804 * (copy, delete, move).
1806 * Returns 1 if did change the directory
1807 * structure, Returns 0 if user aborted
1810 panel_operate (void *source_panel, FileOperation operation,
1811 char *thedefault, int ask_user)
1813 WPanel *panel = source_panel;
1814 #ifdef WITH_FULL_PATHS
1815 char *source_with_path = NULL;
1816 #else
1817 # define source_with_path source
1818 #endif /* !WITH_FULL_PATHS */
1819 char *source = NULL;
1820 char *dest = NULL;
1821 char *temp = NULL;
1822 char *save_cwd = NULL, *save_dest = NULL;
1823 int only_one = (get_current_type () == view_tree)
1824 || (panel->marked <= 1);
1825 struct stat src_stat, dst_stat;
1826 int i, value;
1827 FileOpContext *ctx;
1829 off_t count = 0;
1830 double bytes = 0;
1832 int dst_result;
1833 int do_bg; /* do background operation? */
1835 ctx = file_op_context_new ();
1837 do_bg = 0;
1838 free_linklist (&linklist);
1839 free_linklist (&dest_dirs);
1840 if (get_current_type () == view_listing)
1841 if (!panel->marked && !strcmp (selection (panel)->fname, "..")) {
1842 message (1, MSG_ERROR, _(" Cannot operate on \"..\"! "));
1843 file_op_context_destroy (ctx);
1844 return 0;
1847 /* Generate confirmation prompt */
1848 source =
1849 panel_operate_generate_prompt (panel, operation, only_one,
1850 &src_stat);
1852 /* Show confirmation dialog */
1853 if (operation == OP_DELETE && confirm_delete) {
1854 if (safe_delete)
1855 query_set_sel (1);
1857 i = query_dialog (_(op_names[operation]), cmd_buf,
1858 D_ERROR, 2, _("&Yes"), _("&No"));
1860 if (i != 0) {
1861 file_op_context_destroy (ctx);
1862 return 0;
1864 } else if (operation != OP_DELETE) {
1865 if (ask_user) {
1866 char *dest_dir;
1868 if (thedefault != NULL)
1869 dest_dir = thedefault;
1870 else if (get_other_type () == view_listing)
1871 dest_dir = opanel->cwd;
1872 else
1873 dest_dir = panel->cwd;
1875 dest =
1876 file_mask_dialog (ctx, operation, cmd_buf, dest_dir,
1877 only_one, &do_bg);
1878 if (!dest) {
1879 file_op_context_destroy (ctx);
1880 return 0;
1882 if (!*dest) {
1883 file_op_context_destroy (ctx);
1884 g_free (dest);
1885 return 0;
1887 } else {
1888 char *all = "^\\(.*\\)$";
1890 re_compile_pattern (all, strlen (all), &ctx->rx);
1891 ctx->dest_mask = g_strdup ("*");
1892 do_bg = FALSE;
1893 dest = g_strdup (thedefault);
1896 #ifdef WITH_BACKGROUND
1897 /* Did the user select to do a background operation? */
1898 if (do_bg) {
1899 int v;
1901 v = do_background (ctx,
1902 g_strconcat (op_names[operation], ": ",
1903 panel->cwd, NULL));
1904 if (v == -1) {
1905 message (1, MSG_ERROR,
1906 _(" Sorry, I could not put the job in background "));
1909 /* If we are the parent */
1910 if (v == 1) {
1911 mc_setctl (panel->cwd, MCCTL_FORGET_ABOUT, NULL);
1912 mc_setctl (dest, MCCTL_FORGET_ABOUT, NULL);
1913 /* file_op_context_destroy (ctx); */
1914 return 0;
1917 #endif /* WITH_BACKGROUND */
1919 /* Initialize things */
1920 /* We do not want to trash cache every time file is
1921 created/touched. However, this will make our cache contain
1922 invalid data. */
1923 if (dest) {
1924 if (mc_setctl (dest, MCCTL_WANT_STALE_DATA, NULL))
1925 save_dest = g_strdup (dest);
1927 if (panel->cwd) {
1928 if (mc_setctl (panel->cwd, MCCTL_WANT_STALE_DATA, NULL))
1929 save_cwd = g_strdup (panel->cwd);
1932 /* Now, let's do the job */
1934 if (do_bg)
1935 ctx->ui = NULL;
1936 else
1937 file_op_context_create_ui (ctx, operation, 1);
1939 /* This code is only called by the tree and panel code */
1940 if (only_one) {
1941 /* We now have ETA in all cases */
1943 /* One file: FIXME mc_chdir will take user out of any vfs */
1944 if (operation != OP_COPY && get_current_type () == view_tree)
1945 mc_chdir (PATH_SEP_STR);
1947 /* The source and src_stat variables have been initialized before */
1948 #ifdef WITH_FULL_PATHS
1949 source_with_path = concat_dir_and_file (panel->cwd, source);
1950 #endif /* WITH_FULL_PATHS */
1952 if (operation == OP_DELETE) {
1953 if (S_ISDIR (src_stat.st_mode))
1954 value = erase_dir (ctx, source_with_path, &count, &bytes);
1955 else
1956 value =
1957 erase_file (ctx, source_with_path, &count, &bytes, 1);
1958 } else {
1959 temp = transform_source (ctx, source_with_path);
1961 if (temp == NULL) {
1962 value = transform_error;
1963 } else {
1964 temp = concat_dir_and_file (dest, temp);
1965 g_free (dest);
1966 dest = temp;
1967 temp = 0;
1969 switch (operation) {
1970 case OP_COPY:
1972 * we use file_mask_op_follow_links only with OP_COPY,
1974 (*ctx->stat_func) (source_with_path, &src_stat);
1976 if (S_ISDIR (src_stat.st_mode))
1977 value =
1978 copy_dir_dir (ctx, source_with_path, dest, 1,
1979 0, 0, 0, &count, &bytes);
1980 else
1981 value =
1982 copy_file_file (ctx, source_with_path, dest, 1,
1983 &count, &bytes, 1);
1984 break;
1986 case OP_MOVE:
1987 if (S_ISDIR (src_stat.st_mode))
1988 value =
1989 move_dir_dir (ctx, source_with_path, dest,
1990 &count, &bytes);
1991 else
1992 value =
1993 move_file_file (ctx, source_with_path, dest,
1994 &count, &bytes);
1995 break;
1997 default:
1998 /* Unknown file operation */
1999 abort ();
2002 } /* Copy or move operation */
2004 if (value == FILE_CONT)
2005 unmark_files (panel);
2006 } else {
2007 /* Many files */
2008 /* Check destination for copy or move operation */
2009 if (operation != OP_DELETE) {
2010 retry_many_dst_stat:
2011 dst_result = mc_stat (dest, &dst_stat);
2012 if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)) {
2013 if (file_error
2014 (_(" Destination \"%s\" must be a directory \n %s "),
2015 dest) == FILE_RETRY)
2016 goto retry_many_dst_stat;
2017 goto clean_up;
2021 /* Initialize variables for progress bars */
2022 if (operation != OP_MOVE && verbose && file_op_compute_totals) {
2023 panel_compute_totals (panel, &ctx->progress_count,
2024 &ctx->progress_bytes);
2025 ctx->progress_totals_computed = 1;
2026 } else {
2027 ctx->progress_totals_computed = 0;
2028 ctx->progress_count = panel->marked;
2029 ctx->progress_bytes = panel->total;
2032 /* Loop for every file, perform the actual copy operation */
2033 for (i = 0; i < panel->count; i++) {
2034 if (!panel->dir.list[i].f.marked)
2035 continue; /* Skip the unmarked ones */
2037 source = panel->dir.list[i].fname;
2038 src_stat = panel->dir.list[i].buf;
2040 #ifdef WITH_FULL_PATHS
2041 if (source_with_path)
2042 g_free (source_with_path);
2043 source_with_path = concat_dir_and_file (panel->cwd, source);
2044 #endif /* WITH_FULL_PATHS */
2046 if (operation == OP_DELETE) {
2047 if (S_ISDIR (src_stat.st_mode))
2048 value =
2049 erase_dir (ctx, source_with_path, &count, &bytes);
2050 else
2051 value =
2052 erase_file (ctx, source_with_path, &count, &bytes,
2054 } else {
2055 if (temp)
2056 g_free (temp);
2058 temp = transform_source (ctx, source_with_path);
2059 if (temp == NULL)
2060 value = transform_error;
2061 else {
2062 temp = concat_dir_and_file (dest, temp);
2064 switch (operation) {
2065 case OP_COPY:
2067 * we use file_mask_op_follow_links only with OP_COPY,
2069 (*ctx->stat_func) (source_with_path, &src_stat);
2070 if (S_ISDIR (src_stat.st_mode))
2071 value =
2072 copy_dir_dir (ctx, source_with_path, temp,
2073 1, 0, 0, 0, &count, &bytes);
2074 else
2075 value =
2076 copy_file_file (ctx, source_with_path,
2077 temp, 1, &count, &bytes,
2079 free_linklist (&dest_dirs);
2080 break;
2082 case OP_MOVE:
2083 if (S_ISDIR (src_stat.st_mode))
2084 value =
2085 move_dir_dir (ctx, source_with_path, temp,
2086 &count, &bytes);
2087 else
2088 value =
2089 move_file_file (ctx, source_with_path,
2090 temp, &count, &bytes);
2091 break;
2093 default:
2094 /* Unknown file operation */
2095 abort ();
2098 } /* Copy or move operation */
2100 if (value == FILE_ABORT)
2101 goto clean_up;
2103 if (value == FILE_CONT)
2104 do_file_mark (panel, i, 0);
2106 if (file_progress_show_count (ctx, count, ctx->progress_count)
2107 == FILE_ABORT)
2108 goto clean_up;
2110 if (verbose
2111 && file_progress_show_bytes (ctx, bytes,
2112 ctx->progress_bytes) ==
2113 FILE_ABORT)
2114 goto clean_up;
2116 if (operation != OP_DELETE && verbose
2117 && file_progress_show (ctx, 0, 0) == FILE_ABORT)
2118 goto clean_up;
2120 mc_refresh ();
2121 } /* Loop for every file */
2122 } /* Many files */
2123 clean_up:
2124 /* Clean up */
2126 if (save_cwd) {
2127 mc_setctl (save_cwd, MCCTL_NO_STALE_DATA, NULL);
2128 g_free (save_cwd);
2130 if (save_dest) {
2131 mc_setctl (save_dest, MCCTL_NO_STALE_DATA, NULL);
2132 g_free (save_dest);
2135 free_linklist (&linklist);
2136 free_linklist (&dest_dirs);
2137 #ifdef WITH_FULL_PATHS
2138 if (source_with_path)
2139 g_free (source_with_path);
2140 #endif /* WITH_FULL_PATHS */
2142 if (dest)
2143 g_free (dest);
2145 if (temp)
2146 g_free (temp);
2148 if (ctx->dest_mask) {
2149 g_free (ctx->dest_mask);
2150 ctx->dest_mask = NULL;
2152 #ifdef WITH_BACKGROUND
2153 /* Let our parent know we are saying bye bye */
2154 if (we_are_background) {
2155 vfs_shut ();
2156 tell_parent (MSG_CHILD_EXITING);
2157 _exit (1);
2159 #endif /* WITH_BACKGROUND */
2161 file_op_context_destroy (ctx);
2162 return 1;
2165 /* }}} */
2167 /* {{{ Query/status report routines */
2169 static int
2170 real_do_file_error (enum OperationMode mode, char *error)
2172 int result;
2173 char *msg;
2175 msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
2176 result =
2177 query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"),
2178 _("&Abort"));
2180 switch (result) {
2181 case 0:
2182 do_refresh ();
2183 return FILE_SKIP;
2185 case 1:
2186 do_refresh ();
2187 return FILE_RETRY;
2189 case 2:
2190 default:
2191 return FILE_ABORT;
2195 /* Report error with one file */
2197 file_error (char *format, char *file)
2199 g_snprintf (cmd_buf, sizeof (cmd_buf), format,
2200 name_trunc (file, 30), unix_error_string (errno));
2202 return do_file_error (cmd_buf);
2205 /* Report error with two files */
2207 files_error (char *format, char *file1, char *file2)
2209 char nfile1[16];
2210 char nfile2[16];
2212 strcpy (nfile1, name_trunc (file1, 15));
2213 strcpy (nfile2, name_trunc (file2, 15));
2215 g_snprintf (cmd_buf, sizeof (cmd_buf), format, nfile1, nfile2,
2216 unix_error_string (errno));
2218 return do_file_error (cmd_buf);
2221 static int
2222 real_query_recursive (FileOpContext *ctx, enum OperationMode mode, char *s)
2224 gchar *text;
2226 if (ctx->recursive_result < RECURSIVE_ALWAYS) {
2227 char *msg =
2228 mode ==
2229 Foreground ?
2230 _("\n Directory not empty. \n"
2231 " Delete it recursively? ")
2232 : _("\n Background process: Directory not empty \n"
2233 " Delete it recursively? ");
2234 text = g_strconcat (_(" Delete: "), name_trunc (s, 30), " ", NULL);
2236 if (safe_delete)
2237 query_set_sel (1);
2238 ctx->recursive_result = query_dialog (text, msg, D_ERROR, 5,
2239 _("&Yes"), _("&No"),
2240 _("A&ll"), _("Non&e"),
2241 _("&Abort"));
2243 if (ctx->recursive_result != RECURSIVE_ABORT)
2244 do_refresh ();
2245 g_free (text);
2248 switch (ctx->recursive_result) {
2249 case RECURSIVE_YES:
2250 case RECURSIVE_ALWAYS:
2251 return FILE_CONT;
2253 case RECURSIVE_NO:
2254 case RECURSIVE_NEVER:
2255 return FILE_SKIP;
2257 case RECURSIVE_ABORT:
2259 default:
2260 return FILE_ABORT;
2264 #ifdef WITH_BACKGROUND
2265 static int
2266 do_file_error (char *str)
2268 if (we_are_background)
2269 return parent_call (real_do_file_error, NULL, 1, strlen (str),
2270 str);
2271 else
2272 return real_do_file_error (Foreground, str);
2275 static int
2276 query_recursive (FileOpContext *ctx, char *s)
2278 if (we_are_background)
2279 return parent_call (real_query_recursive, ctx, 1, strlen (s), s);
2280 else
2281 return real_query_recursive (ctx, Foreground, s);
2284 static int
2285 query_replace (FileOpContext *ctx, char *destname, struct stat *_s_stat,
2286 struct stat *_d_stat)
2288 if (we_are_background)
2289 return parent_call ((void *) file_progress_real_query_replace,
2290 ctx,
2292 strlen (destname), destname,
2293 sizeof (struct stat), _s_stat,
2294 sizeof (struct stat), _d_stat);
2295 else
2296 return file_progress_real_query_replace (ctx, Foreground, destname,
2297 _s_stat, _d_stat);
2300 #else
2301 static int
2302 do_file_error (char *str)
2304 return real_do_file_error (Foreground, str);
2307 static int
2308 query_recursive (FileOpContext *ctx, char *s)
2310 return real_query_recursive (ctx, Foreground, s);
2313 static int
2314 query_replace (FileOpContext *ctx, char *destname, struct stat *_s_stat,
2315 struct stat *_d_stat)
2317 return file_progress_real_query_replace (ctx, Foreground, destname,
2318 _s_stat, _d_stat);
2321 #endif /* !WITH_BACKGROUND */
2324 Cause emacs to enter folding mode for this file:
2325 Local variables:
2326 end: