*** empty log message ***
[midnight-commander.git] / src / file.c
blob733af300a8bcf9403897f36ec1decf6f5fe9a295
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>
70 #include <fcntl.h>
72 #include "global.h"
73 #include "tty.h"
74 #include "eregex.h"
75 #include "dialog.h"
76 #include "setup.h"
77 /* Needed by query_replace */
78 #include "color.h"
79 #include "win.h"
80 #include "dlg.h"
81 #include "widget.h"
82 #define WANT_WIDGETS
83 #include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
84 #include "layout.h"
85 #include "widget.h"
86 #include "wtools.h"
88 /* Needed for current_panel, other_panel and WTree */
89 #include "dir.h"
90 #include "panel.h"
91 #include "file.h"
92 #include "filegui.h"
93 #include "tree.h"
94 #include "key.h"
95 #include "../vfs/vfs.h"
97 /* }}} */
99 /* rcsid [] = "$Id$" */
101 int verbose = 1;
104 * Whether the Midnight Commander tries to provide more
105 * information about copy/move sizes and bytes transfered
106 * at the expense of some speed
108 int file_op_compute_totals = 1;
110 /* If on, it gets a little scrict with dangerous operations */
111 int know_not_what_am_i_doing = 0;
113 /* This is a hard link cache */
114 struct link {
115 struct link *next;
116 vfs *vfs;
117 dev_t dev;
118 ino_t ino;
119 short linkcount;
120 umode_t st_mode;
121 char name[1];
124 /* the hard link cache */
125 struct link *linklist = NULL;
127 /* the files-to-be-erased list */
128 struct link *erase_list;
131 * In copy_dir_dir we use two additional single linked lists: The first -
132 * variable name `parent_dirs' - holds information about already copied
133 * directories and is used to detect cyclic symbolic links.
134 * The second (`dest_dirs' below) holds information about just created
135 * target directories and is used to detect when an directory is copied
136 * into itself (we don't want to copy infinitly).
137 * Both lists don't use the linkcount and name structure members of struct
138 * link.
140 struct link *dest_dirs = 0;
142 char *op_names [3] = {
143 N_(" Copy "),
144 N_(" Move "),
145 N_(" Delete ")
148 /* }}} */
150 static int query_replace (FileOpContext *ctx, char *destname,
151 struct stat *_s_stat, struct stat *_d_stat);
152 static int query_recursive (FileOpContext *ctx, char *s);
153 static int do_file_error (char *str);
156 enum CaseConvs { NO_CONV=0, UP_CHAR=1, LOW_CHAR=2, UP_SECT=4, LOW_SECT=8 };
158 static int
159 convert_case (int c, enum CaseConvs *conversion)
161 if (*conversion & UP_CHAR){
162 *conversion &= ~UP_CHAR;
163 return toupper (c);
164 } else if (*conversion & LOW_CHAR){
165 *conversion &= ~LOW_CHAR;
166 return tolower (c);
167 } else if (*conversion & UP_SECT){
168 return toupper (c);
169 } else if (*conversion & LOW_SECT){
170 return tolower (c);
171 } else
172 return c;
175 static int transform_error = 0;
177 static unsigned char *
178 do_transform_source (FileOpContext *ctx, unsigned char *source)
180 int j, k, l, len;
181 unsigned char *fnsource = x_basename (source);
182 int next_reg;
183 enum CaseConvs case_conv = NO_CONV;
184 static unsigned char fntarget [MC_MAXPATHLEN];
186 len = strlen (fnsource);
187 j = re_match (&ctx->rx, fnsource, len, 0, &ctx->regs);
188 if (j != len){
189 transform_error = FILE_SKIP;
190 return NULL;
192 for (next_reg = 1, j = 0, k = 0; j < strlen (ctx->dest_mask); j++){
193 switch (ctx->dest_mask [j]){
194 case '\\':
195 j++;
196 if (! isdigit ((unsigned char) ctx->dest_mask [j])){
197 /* Backslash followed by non-digit */
198 switch (ctx->dest_mask [j]){
199 case 'U':
200 case_conv |= UP_SECT;
201 case_conv &= ~LOW_SECT;
202 break;
203 case 'u':
204 case_conv |= UP_CHAR;
205 break;
206 case 'L':
207 case_conv |= LOW_SECT;
208 case_conv &= ~UP_SECT;
209 break;
210 case 'l':
211 case_conv |= LOW_CHAR;
212 break;
213 case 'E':
214 case_conv = NO_CONV;
215 break;
216 default:
217 /* Backslash as quote mark */
218 fntarget [k++] = convert_case (ctx->dest_mask [j], &case_conv);
220 break;
221 } else {
222 /* Backslash followed by digit */
223 next_reg = ctx->dest_mask [j] - '0';
224 /* Fall through */
227 case '*':
228 if (next_reg < 0 || next_reg >= RE_NREGS
229 || ctx->regs.start [next_reg] < 0){
230 message_1s (1, MSG_ERROR, _(" Invalid target mask "));
231 transform_error = FILE_ABORT;
232 return NULL;
234 for (l = ctx->regs.start [next_reg]; 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 && vfs_type (lp->name) == my_vfs){
319 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
320 was copied to */
321 if (vfs_type (dst_name) == vfs_type (p)){
322 if (!mc_stat (p, &link_stat)){
323 if (!mc_link (p, dst_name))
324 return 1;
328 message_1s (1, MSG_ERROR, _(" Could not make the hardlink "));
329 return 0;
331 lp = (struct link *) g_malloc (sizeof (struct link) + strlen (src_name)
332 + strlen (dst_name) + 1);
333 if (lp){
334 lp->vfs = my_vfs;
335 lp->ino = ino;
336 lp->dev = dev;
337 strcpy (lp->name, src_name);
338 p = strchr (lp->name, 0) + 1;
339 strcpy (p, dst_name);
340 lp->next = linklist;
341 linklist = lp;
343 return 0;
347 * Duplicate the contents of the symbolic link src_path in dst_path.
348 * Try to make a stable symlink if the option "stable symlink" was
349 * set in the file mask dialog.
350 * If dst_path is an existing symlink it will be deleted silently
351 * (upper levels take already care of existing files at dst_path).
353 static int
354 make_symlink (FileOpContext *ctx, char *src_path, char *dst_path)
356 char link_target[MC_MAXPATHLEN];
357 int len;
358 int return_status;
359 struct stat sb;
360 int dst_is_symlink;
362 if (mc_lstat (dst_path, &sb) == 0 && S_ISLNK (sb.st_mode))
363 dst_is_symlink = 1;
364 else
365 dst_is_symlink = 0;
367 retry_src_readlink:
368 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN);
369 if (len < 0){
370 return_status = file_error (_(" Cannot read source link \"%s\" \n %s "), src_path);
371 if (return_status == FILE_RETRY)
372 goto retry_src_readlink;
373 return return_status;
375 link_target[len] = 0;
377 if (ctx->stable_symlinks)
378 if (!vfs_file_is_local (src_path) || !vfs_file_is_local (dst_path)) {
379 message_1s (1, MSG_ERROR,
380 _(" Cannot make stable symlinks across "
381 "non-local filesystems: \n\n"
382 " Option Stable Symlinks will be disabled "));
383 ctx->stable_symlinks = 0;
386 if (ctx->stable_symlinks && *link_target != PATH_SEP) {
387 char *p, *q, *r, *s;
389 p = g_strdup (src_path);
390 r = strrchr (p, PATH_SEP);
392 if (r){
393 r[1] = 0;
394 if (*dst_path == PATH_SEP)
395 q = g_strdup (dst_path);
396 else
397 q = g_strconcat (p, dst_path, NULL);
398 r = strrchr (q, PATH_SEP);
399 if (r){
400 r[1] = 0;
401 s = g_strconcat (p, link_target, NULL);
402 strcpy (link_target, s);
403 g_free (s);
404 s = diff_two_paths (q, link_target);
405 if (s){
406 strcpy (link_target, s);
407 g_free (s);
410 g_free (q);
412 g_free (p);
414 retry_dst_symlink:
415 if (mc_symlink (link_target, dst_path) == 0)
416 /* Success */
417 return FILE_CONT;
419 * if dst_exists, it is obvious that this had failed.
420 * We can delete the old symlink and try again...
422 if (dst_is_symlink){
423 if (!mc_unlink (dst_path))
424 if (mc_symlink (link_target, dst_path) == 0)
425 /* Success */
426 return FILE_CONT;
428 return_status = file_error (_(" Cannot create target symlink \"%s\" \n %s "), dst_path);
429 if (return_status == FILE_RETRY)
430 goto retry_dst_symlink;
431 return return_status;
434 static int
435 progress_update_one (FileOpContext *ctx,
436 off_t *progress_count,
437 double *progress_bytes,
438 int add,
439 int is_toplevel_file)
441 int ret;
443 if (is_toplevel_file || ctx->progress_totals_computed) {
444 (*progress_count)++;
445 (*progress_bytes) += add;
448 /* Apply some heuristic here to not call the update stuff very often */
449 ret = file_progress_show_count (ctx, *progress_count, ctx->progress_count);
450 if (ret != FILE_CONT)
451 return ret;
452 ret = file_progress_show_bytes (ctx, *progress_bytes, ctx->progress_bytes);
454 return ret;
458 copy_file_file (FileOpContext *ctx, char *src_path, char *dst_path, int ask_overwrite,
459 off_t *progress_count, double *progress_bytes,
460 int is_toplevel_file)
462 #ifndef OS2_NT
463 uid_t src_uid = (uid_t) -1;
464 gid_t src_gid = (gid_t) -1;
465 #endif /* !OS2_NT */
466 char *buf = NULL;
467 int buf_size = BUF_8K;
468 int src_desc, dest_desc = 0;
469 int n_read, n_written;
470 int src_mode = 0; /* The mode of the source file */
471 struct stat sb, sb2;
472 struct utimbuf utb;
473 int dst_exists = 0, appending = 0;
474 off_t n_read_total = 0, file_size = -1;
475 int return_status, temp_status;
476 struct timeval tv_transfer_start;
478 /* bitmask used to remember which resourses we should release on return
479 A single goto label is much easier to handle than a bunch of gotos ;-). */
480 unsigned resources = 0;
482 /* FIXME: We should not be using global variables! */
483 ctx->do_reget = 0;
484 return_status = FILE_RETRY;
486 if (file_progress_show_source (ctx, src_path) == FILE_ABORT ||
487 file_progress_show_target (ctx, dst_path) == FILE_ABORT)
488 return FILE_ABORT;
490 mc_refresh ();
492 retry_dst_stat:
493 if (mc_stat (dst_path, &sb2) == 0){
494 if (S_ISDIR (sb2.st_mode)){
495 return_status = file_error (_(" Cannot overwrite directory \"%s\" \n %s "), dst_path);
496 if (return_status == FILE_RETRY)
497 goto retry_dst_stat;
498 return return_status;
500 dst_exists = 1;
503 while ((* ctx->stat_func) (src_path, &sb)) {
504 return_status = file_error (_(" Cannot stat source file \"%s\" \n %s "), src_path);
505 if (return_status != FILE_RETRY)
506 return return_status;
509 if (dst_exists){
510 /* .ado: For OS/2 or NT: no st_ino exists, it is better to just try to
511 * overwrite the target file
513 #ifndef OS2_NT
514 /* Destination already exists */
515 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino){
516 message_3s (1, MSG_ERROR, _(" `%s' and `%s' are the same file. "), src_path, dst_path);
517 do_refresh ();
518 return FILE_SKIP;
520 #endif /* !OS2_NT */
522 /* Should we replace destination? */
523 if (ask_overwrite){
524 ctx->do_reget = 0;
525 return_status = query_replace (ctx, dst_path, &sb, &sb2);
526 if (return_status != FILE_CONT)
527 return return_status;
531 if (!ctx->do_append) {
532 /* .ado: OS2 and NT don't have hardlinks */
533 #ifndef OS2_NT
534 /* Check the hardlinks */
535 if (!ctx->follow_links && sb.st_nlink > 1 &&
536 check_hardlinks (src_path, dst_path, &sb) == 1){
537 /* We have made a hardlink - no more processing is necessary */
538 return return_status;
541 if (S_ISLNK (sb.st_mode)) {
542 int retval;
544 retval = make_symlink (ctx, src_path, dst_path);
545 return retval;
548 #endif /* !OS2_NT */
550 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) || S_ISFIFO (sb.st_mode)
551 || S_ISSOCK (sb.st_mode)){
552 while (mc_mknod (dst_path, sb.st_mode & ctx->umask_kill, sb.st_rdev) < 0){
553 return_status = file_error (_(" Cannot create special file \"%s\" \n %s "), dst_path);
554 if (return_status == FILE_RETRY)
555 continue;
556 return return_status;
558 /* Success */
560 #ifndef OS2_NT
561 while (ctx->preserve_uidgid && mc_chown (dst_path, sb.st_uid, sb.st_gid)){
562 temp_status = file_error (_(" Cannot chown target file \"%s\" \n %s "), dst_path);
563 if (temp_status == FILE_RETRY)
564 continue;
565 return temp_status;
567 #endif /* !OS2_NT */
568 while (ctx->preserve &&
569 (mc_chmod (dst_path, sb.st_mode & ctx->umask_kill) < 0)){
570 temp_status = file_error (_(" Cannot chmod target file \"%s\" \n %s "), dst_path);
571 if (temp_status == FILE_RETRY)
572 continue;
573 return temp_status;
575 return FILE_CONT;
579 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
581 while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0){
582 return_status = file_error (_(" Cannot open source file \"%s\" \n %s "), src_path);
583 if (return_status == FILE_RETRY)
584 continue;
585 ctx->do_append = 0;
586 return return_status;
589 resources |= 1;
590 if (ctx->do_reget){
591 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget){
592 message_1s (1, _(" Warning "), _(" Reget failed, about to overwrite file "));
593 ctx->do_reget = ctx->do_append = 0;
597 while (mc_fstat (src_desc, &sb)){
598 return_status = file_error (_(" Cannot fstat source file \"%s\" \n %s "), src_path);
599 if (return_status == FILE_RETRY)
600 continue;
601 ctx->do_append = 0;
602 goto ret;
604 src_mode = sb.st_mode;
605 #ifndef OS2_NT
606 src_uid = sb.st_uid;
607 src_gid = sb.st_gid;
608 #endif /* !OS2_NT */
609 utb.actime = sb.st_atime;
610 utb.modtime = sb.st_mtime;
611 file_size = sb.st_size;
613 /* Create the new regular file with small permissions initially,
614 do not create a security hole. FIXME: You have security hole
615 here, btw. Imagine copying to /tmp and symlink attack :-( */
617 while ((dest_desc = mc_open (dst_path, O_WRONLY |
618 (ctx->do_append ? O_APPEND : (O_CREAT | O_TRUNC)), 0600)) < 0){
619 return_status = file_error (_(" Cannot create target file \"%s\" \n %s "), dst_path);
620 if (return_status == FILE_RETRY)
621 continue;
622 ctx->do_append = 0;
623 goto ret;
625 resources |= 2; /* dst_path exists/dst_path opened */
626 resources |= 4; /* remove short file */
628 appending = ctx->do_append;
629 ctx->do_append = 0;
631 /* Find out the optimal buffer size. */
632 while (mc_fstat (dest_desc, &sb)){
633 return_status = file_error (_(" Cannot fstat target file \"%s\" \n %s "), dst_path);
634 if (return_status == FILE_RETRY)
635 continue;
636 goto ret;
638 buf = (char *) g_malloc (buf_size);
640 ctx->eta_secs = 0.0;
641 ctx->bps = 0;
643 return_status = file_progress_show (ctx, 0, file_size);
645 mc_refresh ();
647 if (return_status != FILE_CONT)
648 goto ret;
651 struct timeval tv_current, tv_last_update, tv_last_input;
652 int secs, update_secs;
653 long dt;
654 char *stalled_msg;
656 tv_last_update = tv_transfer_start;
658 for (;;){
659 /* src_read */
660 if (mc_ctl (src_desc, MCCTL_IS_NOTREADY, 0))
661 n_read = -1;
662 else
663 while ((n_read = mc_read (src_desc, buf, buf_size))<0){
664 return_status = file_error(_(" Cannot read source file \"%s\" \n %s "), src_path);
665 if (return_status == FILE_RETRY)
666 continue;
667 goto ret;
669 if (n_read == 0)
670 break;
672 gettimeofday (&tv_current, NULL);
674 if (n_read>0){
675 n_read_total += n_read;
677 /* Windows NT ftp servers report that files have no
678 * permissions: -------, so if we happen to have actually
679 * read something, we should fix the permissions.
681 if (!(src_mode &
682 ((S_IRUSR|S_IWUSR|S_IXUSR) /* user */
683 |(S_IXOTH|S_IWOTH|S_IROTH) /* other */
684 |(S_IXGRP|S_IWGRP|S_IRGRP)))) /* group */
685 src_mode = S_IRUSR|S_IWUSR|S_IROTH|S_IRGRP;
686 gettimeofday (&tv_last_input, NULL);
688 /* dst_write */
689 while ((n_written = mc_write (dest_desc, buf, n_read)) < n_read){
690 if (n_written>0){
691 n_read -= n_written;
692 continue;
694 return_status = file_error(_(" Cannot write target file \"%s\" \n %s "),
695 dst_path);
696 if (return_status == FILE_RETRY)
697 continue;
698 goto ret;
702 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
703 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
704 if (secs > 2){
705 rotate_dash ();
706 tv_last_update = tv_current;
709 /* 2. Check for a stalled condition */
710 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
711 stalled_msg = "";
712 if (update_secs > 4){
713 stalled_msg = _("(stalled)");
716 /* 3. Compute ETA */
717 if (secs > 2){
718 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
720 if (n_read_total){
721 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
722 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
723 } else
724 ctx->eta_secs = 0.0;
727 /* 4. Compute BPS rate */
728 if (secs > 2){
729 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
730 if (ctx->bps_time < 1)
731 ctx->bps_time = 1;
732 ctx->bps = n_read_total / ctx->bps_time;
735 file_progress_set_stalled_label (ctx, stalled_msg);
736 file_progress_show_bytes (ctx, *progress_bytes + n_read_total, ctx->progress_bytes);
737 return_status = file_progress_show (ctx, n_read_total, file_size);
738 mc_refresh ();
739 if (return_status != FILE_CONT)
740 goto ret;
744 resources &= ~4; /* copy successful, don't remove target file */
746 ret:
747 if (buf)
748 g_free (buf);
750 while ((resources & 1) && mc_close (src_desc) < 0){
751 temp_status = file_error (_(" Cannot close source file \"%s\" \n %s "), src_path);
752 if (temp_status == FILE_RETRY)
753 continue;
754 if (temp_status == FILE_ABORT)
755 return_status = temp_status;
756 break;
759 while ((resources & 2) && mc_close (dest_desc) < 0){
760 temp_status = file_error (_(" Cannot close target file \"%s\" \n %s "), dst_path);
761 if (temp_status == FILE_RETRY)
762 continue;
763 return_status = temp_status;
764 break;
767 if (resources & 4){
768 /* Remove short file */
769 int result;
770 result = query_dialog (_("Copy"), _("Incomplete file was retrieved. Keep it?"),
771 D_ERROR, 2, _("&Delete"), _("&Keep"));
772 if (!result)
773 mc_unlink (dst_path);
774 } else if (resources & (2|8)){
775 /* no short file and destination file exists */
776 #ifndef OS2_NT
777 if (!appending && ctx->preserve_uidgid){
778 while (mc_chown (dst_path, src_uid, src_gid)){
779 temp_status = file_error
780 (_(" Cannot chown target file \"%s\" \n %s "), dst_path);
781 if (temp_status == FILE_RETRY)
782 continue;
783 return_status = temp_status;
784 break;
787 #endif /* !OS2_NT */
790 * .ado: according to the XPG4 standard, the file must be closed before
791 * chmod can be invoked
793 if (!appending && ctx->preserve){
794 while (mc_chmod (dst_path, src_mode & ctx->umask_kill)){
795 temp_status = file_error (
796 _(" Cannot chmod target file \"%s\" \n %s "), dst_path);
797 if (temp_status != FILE_RETRY){
798 return_status = temp_status;
799 break;
802 mc_utime (dst_path, &utb);
806 if (return_status == FILE_CONT)
807 return_status = progress_update_one (ctx, progress_count, progress_bytes,
808 file_size, is_toplevel_file);
810 return return_status;
814 * I think these copy_*_* functions should have a return type.
815 * anyway, this function *must* have two directories as arguments.
817 /* FIXME: This function needs to check the return values of the
818 function calls */
820 copy_dir_dir (FileOpContext *ctx, char *s, char *d, int toplevel,
821 int move_over, int delete,
822 struct link *parent_dirs,
823 off_t *progress_count,
824 double *progress_bytes)
826 struct dirent *next;
827 struct stat buf, cbuf;
828 DIR *reading;
829 char *path, *mdpath, *dest_file, *dest_dir;
830 int return_status = FILE_CONT;
831 struct utimbuf utb;
832 struct link *lp;
834 /* First get the mode of the source dir */
835 retry_src_stat:
836 if ((* ctx->stat_func) (s, &cbuf)){
837 return_status = file_error (_(" Cannot stat source directory \"%s\" \n %s "), s);
838 if (return_status == FILE_RETRY)
839 goto retry_src_stat;
840 return return_status;
843 if (is_in_linklist (dest_dirs, s, &cbuf)){
844 /* Don't copy a directory we created before (we don't want to copy
845 infinitely if a directory is copied into itself) */
846 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
847 return FILE_CONT;
850 /* Hmm, hardlink to directory??? - Norbert */
851 /* FIXME: In this step we should do something
852 in case the destination already exist */
853 /* Check the hardlinks */
854 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf) == 1){
855 /* We have made a hardlink - no more processing is necessary */
856 return return_status;
859 if (!S_ISDIR (cbuf.st_mode)){
860 return_status = file_error (_(" Source directory \"%s\" is not a directory \n %s "), s);
861 if (return_status == FILE_RETRY)
862 goto retry_src_stat;
863 return return_status;
866 if (is_in_linklist (parent_dirs, s, &cbuf)){
867 /* we found a cyclic symbolic link */
868 message_2s (1, MSG_ERROR, _(" Cannot copy cyclic symbolic link \n `%s' "), s);
869 return FILE_SKIP;
872 lp = g_new (struct link, 1);
873 lp->vfs = vfs_type (s);
874 lp->ino = cbuf.st_ino;
875 lp->dev = cbuf.st_dev;
876 lp->next = parent_dirs;
877 parent_dirs = lp;
879 retry_dst_stat:
880 /* Now, check if the dest dir exists, if not, create it. */
881 if (mc_stat (d, &buf)){
882 /* Here the dir doesn't exist : make it !*/
884 if (move_over){
885 if (mc_rename (s, d) == 0){
886 g_free (parent_dirs);
887 return FILE_CONT;
890 dest_dir = g_strdup (d);
891 } else {
893 * If the destination directory exists, we want to copy the whole
894 * directory, but we only want this to happen once.
896 * Escape sequences added to the * to compiler warnings.
897 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
898 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
900 if (!S_ISDIR (buf.st_mode)){
901 return_status = file_error (_(" Destination \"%s\" must be a directory \n %s "), d);
902 if (return_status == FILE_RETRY)
903 goto retry_dst_stat;
904 g_free (parent_dirs);
905 return return_status;
907 #if 1
908 /* Again, I'm getting curious. Is not d already what we wanted, incl.
909 * masked source basename? Is not this just a relict of the past versions?
910 * I'm afraid this will lead into a two level deep dive :(
912 * I think this is indeed the problem. I can not remember any case where
913 * we actually would like that behaviour -miguel
915 * It's a documented feature (option `Dive into subdir if exists' in the
916 * copy/move dialog). -Norbert
918 if (toplevel && ctx->dive_into_subdirs){
919 dest_dir = concat_dir_and_file (d, x_basename (s));
920 } else
921 #endif
923 dest_dir = g_strdup (d);
924 goto dont_mkdir;
927 retry_dst_mkdir:
928 if (my_mkdir (dest_dir, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU)){
929 return_status = file_error (_(" Cannot create target directory \"%s\" \n %s "), dest_dir);
930 if (return_status == FILE_RETRY)
931 goto retry_dst_mkdir;
932 goto ret;
935 lp = g_new (struct link, 1);
936 mc_stat (dest_dir, &buf);
937 lp->vfs = vfs_type (dest_dir);
938 lp->ino = buf.st_ino;
939 lp->dev = buf.st_dev;
940 lp->next = dest_dirs;
941 dest_dirs = lp;
943 #ifndef OS2_NT
944 if (ctx->preserve_uidgid){
945 retry_dst_chown:
946 if (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid)){
947 return_status = file_error (_(" Cannot chown target directory \"%s\" \n %s "), dest_dir);
948 if (return_status == FILE_RETRY)
949 goto retry_dst_chown;
950 goto ret;
953 #endif /* !OS2_NT */
955 dont_mkdir:
956 /* open the source dir for reading */
957 if ((reading = mc_opendir (s)) == 0){
958 goto ret;
961 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT){
963 * Now, we don't want '.' and '..' to be created / copied at any time
965 if (!strcmp (next->d_name, "."))
966 continue;
967 if (!strcmp (next->d_name, ".."))
968 continue;
970 /* get the filename and add it to the src directory */
971 path = concat_dir_and_file (s, next->d_name);
973 (* ctx->stat_func) (path, &buf);
974 if (S_ISDIR (buf.st_mode)){
975 mdpath = concat_dir_and_file (dest_dir, next->d_name);
977 * From here, we just intend to recursively copy subdirs, not
978 * the double functionality of copying different when the target
979 * dir already exists. So, we give the recursive call the flag 0
980 * meaning no toplevel.
982 return_status = copy_dir_dir (ctx, path, mdpath, 0, 0,
983 delete, parent_dirs, progress_count, progress_bytes);
984 g_free (mdpath);
985 } else {
986 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
987 return_status = copy_file_file (ctx, path, dest_file, 1,
988 progress_count, progress_bytes, 0);
989 g_free (dest_file);
991 if (delete && return_status == FILE_CONT){
992 if (ctx->erase_at_end){
993 static struct link *tail;
994 lp = g_malloc (sizeof (struct link) + strlen (path));
995 strcpy (lp->name, path);
996 lp->st_mode = buf.st_mode;
997 lp->next = 0;
998 if (erase_list){
999 tail->next = lp;
1000 tail = lp;
1001 } else
1002 erase_list = tail = lp;
1003 } else {
1004 if (S_ISDIR (buf.st_mode)){
1005 return_status = erase_dir_iff_empty (ctx, path);
1006 } else
1007 return_status = erase_file (ctx, path, 0, 0, 0);
1011 g_free (path);
1013 mc_closedir (reading);
1015 if (ctx->preserve){
1016 mc_chmod (dest_dir, cbuf.st_mode & ctx->umask_kill);
1017 utb.actime = cbuf.st_atime;
1018 utb.modtime = cbuf.st_mtime;
1019 mc_utime(dest_dir, &utb);
1022 ret:
1023 g_free (dest_dir);
1024 g_free (parent_dirs);
1025 return return_status;
1028 /* }}} */
1030 /* {{{ Move routines */
1033 move_file_file (FileOpContext *ctx, char *s, char *d,
1034 off_t *progress_count, double *progress_bytes)
1036 struct stat src_stats, dst_stats;
1037 int return_status = FILE_CONT;
1039 if (file_progress_show_source (ctx, s) == FILE_ABORT
1040 || file_progress_show_target (ctx, d) == FILE_ABORT)
1041 return FILE_ABORT;
1043 mc_refresh ();
1045 retry_src_lstat:
1046 if (mc_lstat (s, &src_stats) != 0){
1047 /* Source doesn't exist */
1048 return_status = file_error (_(" Cannot stat file \"%s\" \n %s "), s);
1049 if (return_status == FILE_RETRY)
1050 goto retry_src_lstat;
1051 return return_status;
1054 if (mc_lstat (d, &dst_stats) == 0){
1055 /* Destination already exists */
1056 /* .ado: for OS/2 and NT, no st_ino exists */
1057 #ifndef OS2_NT
1058 if (src_stats.st_dev == dst_stats.st_dev
1059 && src_stats.st_ino == dst_stats.st_ino){
1060 int msize = COLS - 36;
1061 char st[MC_MAXPATHLEN];
1062 char dt[MC_MAXPATHLEN];
1064 if (msize < 0)
1065 msize = 40;
1066 msize /= 2;
1068 strcpy (st, name_trunc (s, msize));
1069 strcpy (dt, name_trunc (d, msize));
1070 message_3s (1, MSG_ERROR, _(" `%s' and `%s' are the same file "),
1071 st, dt );
1072 do_refresh ();
1073 return FILE_SKIP;
1075 #endif /* !OS2_NT */
1076 if (S_ISDIR (dst_stats.st_mode)){
1077 message_2s (1, MSG_ERROR, _(" Cannot overwrite directory `%s' "), d);
1078 do_refresh ();
1079 return FILE_SKIP;
1082 if (confirm_overwrite){
1083 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
1084 if (return_status != FILE_CONT)
1085 return return_status;
1087 /* Ok to overwrite */
1090 if (!ctx->do_append) {
1091 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks) {
1092 if ((return_status = make_symlink (ctx, s, d)) == FILE_CONT) {
1093 goto retry_src_remove;
1094 } else
1095 return return_status;
1098 if (mc_rename (s, d) == 0){
1099 return FILE_CONT;
1102 #if 0
1103 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1104 one nfs to the same, but on the server it is on two different
1105 filesystems. Then nfs returns EIO instead of EXDEV.
1106 Hope it will not hurt if we always in case of error try to copy/delete. */
1107 else
1108 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1110 if (errno != EXDEV){
1111 return_status = files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s, d);
1112 if (return_status == FILE_RETRY)
1113 goto retry_rename;
1114 return return_status;
1116 #endif
1118 /* Failed because filesystem boundary -> copy the file instead */
1119 return_status = copy_file_file (ctx, s, d, 0, progress_count, progress_bytes, 1);
1120 if (return_status != FILE_CONT)
1121 return return_status;
1123 if ((return_status = file_progress_show_source (ctx, NULL)) != FILE_CONT
1124 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1125 return return_status;
1127 mc_refresh ();
1129 retry_src_remove:
1130 if (mc_unlink (s)){
1131 return_status = file_error (_(" Cannot remove file \"%s\" \n %s "), s);
1132 if (return_status == FILE_RETRY)
1133 goto retry_src_remove;
1134 return return_status;
1137 if (return_status == FILE_CONT)
1138 return_status = progress_update_one (ctx,
1139 progress_count,
1140 progress_bytes, src_stats.st_size, 1);
1142 return return_status;
1146 move_dir_dir (FileOpContext *ctx, char *s, char *d,
1147 off_t *progress_count, double *progress_bytes)
1149 struct stat sbuf, dbuf, destbuf;
1150 struct link *lp;
1151 char *destdir;
1152 int return_status;
1153 int move_over = 0;
1155 if (file_progress_show_source (ctx, s) == FILE_ABORT ||
1156 file_progress_show_target (ctx, d) == FILE_ABORT)
1157 return FILE_ABORT;
1159 mc_refresh ();
1161 mc_stat (s, &sbuf);
1162 if (mc_stat (d, &dbuf))
1163 destdir = g_strdup (d); /* destination doesn't exist */
1164 else if (!ctx->dive_into_subdirs){
1165 destdir = g_strdup (d);
1166 move_over = 1;
1167 } else
1168 destdir = concat_dir_and_file (d, x_basename (s));
1169 #ifndef OS2_NT
1170 if (sbuf.st_dev == dbuf.st_dev
1171 && sbuf.st_ino == dbuf.st_ino){
1172 int msize = COLS - 36;
1173 char st[MC_MAXPATHLEN];
1174 char dt[MC_MAXPATHLEN];
1176 if (msize < 0)
1177 msize = 40;
1178 msize /= 2;
1180 strcpy (st, name_trunc (s, msize));
1181 strcpy (dt, name_trunc (d, msize));
1182 message_3s (1, MSG_ERROR, _(" `%s' and `%s' are the same directory "),
1183 st, dt );
1184 do_refresh ();
1185 return FILE_SKIP;
1187 #endif /* !OS2_NT */
1189 /* Check if the user inputted an existing dir */
1190 retry_dst_stat:
1191 if (!mc_stat (destdir, &destbuf)){
1192 if (move_over){
1193 return_status = copy_dir_dir (ctx, s, destdir, 0, 1, 1, 0,
1194 progress_count, progress_bytes);
1196 if (return_status != FILE_CONT)
1197 goto ret;
1198 goto oktoret;
1199 } else {
1200 if (S_ISDIR (destbuf.st_mode))
1201 return_status = file_error (_(" Cannot overwrite directory \"%s\" %s "), destdir);
1202 else
1203 return_status = file_error (_(" Cannot overwrite file \"%s\" %s "), destdir);
1204 if (return_status == FILE_RETRY)
1205 goto retry_dst_stat;
1207 g_free (destdir);
1208 return return_status;
1211 retry_rename:
1212 if (mc_rename (s, destdir) == 0){
1213 return_status = FILE_CONT;
1214 goto ret;
1216 /* .ado: Drive, Do we need this anymore? */
1217 #ifdef WIN32
1218 else {
1219 /* EXDEV: cross device; does not work everywhere */
1220 if (toupper(s[0]) != toupper(destdir[0]))
1221 goto w32try;
1223 #endif /* WIN32 */
1225 if (errno != EXDEV){
1226 return_status = files_error (_(" Cannot move directory \"%s\" to \"%s\" \n %s "), s, d);
1227 if (return_status == FILE_RETRY)
1228 goto retry_rename;
1229 goto ret;
1231 #ifdef WIN32
1232 w32try:
1233 #endif /* WIN32 */
1234 /* Failed because of filesystem boundary -> copy dir instead */
1235 return_status = copy_dir_dir (ctx, s, destdir, 0, 0, 1, 0, progress_count, progress_bytes);
1237 if (return_status != FILE_CONT)
1238 goto ret;
1239 oktoret:
1240 if ((return_status = file_progress_show_source (ctx, NULL)) != FILE_CONT
1241 || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT)
1242 goto ret;
1244 mc_refresh ();
1245 if (ctx->erase_at_end){
1246 for (; erase_list && return_status != FILE_ABORT;){
1247 if (S_ISDIR (erase_list->st_mode)){
1248 return_status = erase_dir_iff_empty (ctx, erase_list->name);
1249 } else
1250 return_status = erase_file (ctx, erase_list->name, 0, 0, 0);
1251 lp = erase_list;
1252 erase_list = erase_list->next;
1253 g_free (lp);
1256 erase_dir_iff_empty (ctx, s);
1258 ret:
1259 g_free (destdir);
1260 while (erase_list){
1261 lp = erase_list;
1262 erase_list = erase_list->next;
1263 g_free (lp);
1265 return return_status;
1268 /* }}} */
1270 /* {{{ Erase routines */
1271 /* Don't update progress status if progress_count==NULL */
1273 erase_file (FileOpContext *ctx, char *s, off_t *progress_count, double *progress_bytes,
1274 int is_toplevel_file)
1276 int return_status;
1277 struct stat buf;
1279 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1280 return FILE_ABORT;
1281 mc_refresh ();
1283 if (progress_count && mc_lstat (s, &buf)) {
1284 /* ignore, most likely the mc_unlink fails, too */
1285 buf.st_size = 0;
1288 while (mc_unlink (s)){
1289 return_status = file_error (_(" Cannot delete file \"%s\" \n %s "), s);
1290 if (return_status != FILE_RETRY)
1291 return return_status;
1294 if (progress_count)
1295 return progress_update_one (ctx, progress_count, progress_bytes, buf.st_size,
1296 is_toplevel_file);
1297 else
1298 return FILE_CONT;
1301 static int
1302 recursive_erase (FileOpContext *ctx, char *s, off_t *progress_count, double *progress_bytes)
1304 struct dirent *next;
1305 struct stat buf;
1306 DIR *reading;
1307 char *path;
1308 int return_status = FILE_CONT;
1310 if (!strcmp (s, ".."))
1311 return 1;
1313 reading = mc_opendir (s);
1315 if (!reading)
1316 return 1;
1318 while ((next = mc_readdir (reading)) && return_status == FILE_CONT){
1319 if (!strcmp (next->d_name, "."))
1320 continue;
1321 if (!strcmp (next->d_name, ".."))
1322 continue;
1323 path = concat_dir_and_file (s, next->d_name);
1324 if (mc_lstat (path, &buf)){
1325 g_free (path);
1326 mc_closedir (reading);
1327 return 1;
1329 if (S_ISDIR (buf.st_mode))
1330 return_status = (recursive_erase (ctx, path, progress_count, progress_bytes)
1331 != FILE_CONT);
1332 else
1333 return_status = erase_file (ctx, path, progress_count, progress_bytes, 0);
1334 g_free (path);
1336 mc_closedir (reading);
1337 if (return_status != FILE_CONT)
1338 return return_status;
1339 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1340 return FILE_ABORT;
1341 mc_refresh ();
1343 while (my_rmdir (s)){
1344 return_status = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1345 if (return_status != FILE_RETRY)
1346 return return_status;
1349 return FILE_CONT;
1352 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1353 in the directory path points to, 0 else. */
1354 static int
1355 check_dir_is_empty(char *path)
1357 DIR *dir;
1358 struct dirent *d;
1359 int i;
1361 dir = mc_opendir (path);
1362 if (!dir)
1363 return -1;
1365 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir)){
1366 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1367 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1368 continue; /* "." or ".." */
1369 i = 0;
1370 break;
1373 mc_closedir (dir);
1374 return i;
1378 erase_dir (FileOpContext *ctx, char *s, off_t *progress_count, double *progress_bytes)
1380 int error;
1382 if (strcmp (s, "..") == 0)
1383 return FILE_SKIP;
1385 if (strcmp (s, ".") == 0)
1386 return FILE_SKIP;
1388 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1389 return FILE_ABORT;
1390 mc_refresh ();
1392 /* The old way to detect a non empty directory was:
1393 error = my_rmdir (s);
1394 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1395 For the linux user space nfs server (nfs-server-2.2beta29-2)
1396 we would have to check also for EIO. I hope the new way is
1397 fool proof. (Norbert)
1399 error = check_dir_is_empty (s);
1400 if (error == 0){ /* not empty */
1401 error = query_recursive (ctx, s);
1402 if (error == FILE_CONT)
1403 return recursive_erase (ctx, s, progress_count, progress_bytes);
1404 else
1405 return error;
1408 while (my_rmdir (s) == -1){
1409 error = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1410 if (error != FILE_RETRY)
1411 return error;
1414 return FILE_CONT;
1418 erase_dir_iff_empty (FileOpContext *ctx, char *s)
1420 int error;
1422 if (strcmp (s, "..") == 0)
1423 return FILE_SKIP;
1425 if (strcmp (s, ".") == 0)
1426 return FILE_SKIP;
1428 if (file_progress_show_deleting (ctx, s) == FILE_ABORT)
1429 return FILE_ABORT;
1430 mc_refresh ();
1432 if (1 != check_dir_is_empty (s)) /* not empty or error */
1433 return FILE_CONT;
1435 while (my_rmdir (s)){
1436 error = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1437 if (error != FILE_RETRY)
1438 return error;
1441 return FILE_CONT;
1444 /* }}} */
1446 /* {{{ Panel operate routines */
1448 /* Returns currently selected file or the first marked file if there is one */
1449 char *
1450 panel_get_file (WPanel *panel, struct stat *stat_buf)
1452 int i;
1454 /* No problem with Gnome, as get_current_type never returns view_tree there */
1455 if (get_current_type () == view_tree){
1456 WTree *tree = (WTree *)get_panel_widget (get_current_index ());
1458 mc_stat (tree->selected_ptr->name, stat_buf);
1459 return tree->selected_ptr->name;
1462 if (panel->marked){
1463 for (i = 0; i < panel->count; i++)
1464 if (panel->dir.list [i].f.marked){
1465 *stat_buf = panel->dir.list [i].buf;
1466 return panel->dir.list [i].fname;
1468 } else {
1469 *stat_buf = panel->dir.list [panel->selected].buf;
1470 return panel->dir.list [panel->selected].fname;
1472 g_assert_not_reached ();
1473 return NULL;
1477 is_wildcarded (char *p)
1479 for (; *p; p++){
1480 if (*p == '*')
1481 return 1;
1482 else if (*p == '\\' && p [1] >= '1' && p [1] <= '9')
1483 return 1;
1485 return 0;
1489 * compute_dir_size:
1491 * Computes the number of bytes used by the files in a directory
1493 void
1494 compute_dir_size (char *dirname, off_t *ret_marked, double *ret_total)
1496 DIR *dir;
1497 struct dirent *dirent;
1499 dir = mc_opendir (dirname);
1501 if (!dir)
1502 return;
1504 while ((dirent = mc_readdir (dir)) != NULL){
1505 struct stat s;
1506 char *fullname;
1507 int res;
1509 if (strcmp (dirent->d_name, ".") == 0)
1510 continue;
1511 if (strcmp (dirent->d_name, "..") == 0)
1512 continue;
1514 fullname = concat_dir_and_file (dirname, dirent->d_name);
1516 res = mc_lstat (fullname, &s);
1518 if (res != 0){
1519 g_free (fullname);
1520 continue;
1523 if (S_ISDIR (s.st_mode)){
1524 off_t subdir_count = 0;
1525 double subdir_bytes = 0;
1527 compute_dir_size (fullname, &subdir_count, &subdir_bytes);
1529 *ret_marked += subdir_count;
1530 *ret_total += subdir_bytes;
1531 } else {
1532 (*ret_marked)++;
1533 *ret_total += s.st_size;
1535 g_free (fullname);
1538 mc_closedir (dir);
1542 * panel_compute_totals:
1544 * compute the number of files and the number of bytes
1545 * used up by the whole selection, recursing directories
1546 * as required. In addition, it checks to see if it will
1547 * overwrite any files by doing the copy.
1549 static void
1550 panel_compute_totals (WPanel *panel, off_t *ret_marked, double *ret_total)
1552 int i;
1554 *ret_marked = 0;
1555 *ret_total = 0.0;
1557 for (i = 0; i < panel->count; i++){
1558 struct stat *s;
1560 if (!panel->dir.list [i].f.marked)
1561 continue;
1563 s = &panel->dir.list [i].buf;
1565 if (S_ISDIR (s->st_mode)){
1566 char *dir_name;
1567 off_t subdir_count = 0;
1568 double subdir_bytes = 0;
1570 dir_name = concat_dir_and_file (panel->cwd, panel->dir.list [i].fname);
1571 compute_dir_size (dir_name, &subdir_count, &subdir_bytes);
1573 *ret_marked += subdir_count;
1574 *ret_total += subdir_bytes;
1575 g_free (dir_name);
1576 } else {
1577 (*ret_marked)++;
1578 *ret_total += s->st_size;
1584 * This array introduced to avoid translation problems. The former (op_names)
1585 * is assumed to be nouns, suitable in dialog box titles; this one should
1586 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1587 * Notice first symbol - it is to fool gettext and force these strings to
1588 * be different for it. First symbol is skipped while building a prompt.
1589 * (I don't use spaces around the words, because someday they could be
1590 * dropped, when widgets get smarter)
1592 static char *op_names1 [] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
1593 #define FMD_XLEN 64
1595 int fmd_xlen = FMD_XLEN;
1598 * These are formats for building a prompt. Parts encoded as follows:
1599 * %o - operation from op_names1
1600 * %f - file/files or files/directories, as appropriate
1601 * %m - "with source mask" or question mark for delete
1602 * %s - source name (truncated)
1603 * %d - number of marked files
1604 * %e - "to:" or question mark for delete
1606 * xgettext:no-c-format */
1607 static char* one_format = N_("%o %f \"%s\"%m");
1608 /* xgettext:no-c-format */
1609 static char* many_format = N_("%o %d %f%m");
1610 static char* prompt_parts [] =
1612 N_("file"), N_("files"), N_("directory"), N_("directories"),
1613 N_("files/directories"), N_(" with source mask:"), N_(" to:")
1616 static char *
1617 panel_operate_generate_prompt (WPanel* panel, int operation, int only_one,
1618 struct stat* src_stat)
1620 register char *sp, *cp;
1621 register int i;
1622 char format_string [BUF_MEDIUM];
1623 char *dp = format_string;
1624 char* source = NULL;
1626 #ifdef ENABLE_NLS
1627 static int i18n_flag = 0;
1628 if (!i18n_flag)
1630 fmd_init_i18n (FALSE); /* to get proper fmd_xlen */
1632 for (i = sizeof (op_names1) / sizeof (op_names1 [0]); i--;)
1633 op_names1 [i] = _(op_names1 [i]);
1635 for (i = sizeof (prompt_parts) / sizeof (prompt_parts [0]); i--;)
1636 prompt_parts [i] = _(prompt_parts [i]);
1638 one_format = _(one_format);
1639 many_format = _(many_format);
1640 i18n_flag = 1;
1642 #endif /* ENABLE_NLS */
1644 sp = only_one ? one_format : many_format;
1646 if (only_one)
1647 source = panel_get_file (panel, src_stat);
1649 while (*sp)
1651 switch (*sp)
1653 case '%':
1654 cp = NULL;
1655 switch (sp[1])
1657 case 'o':
1658 cp = op_names1 [operation] + 1;
1659 break;
1660 case 'm':
1661 cp = operation == OP_DELETE ? "?" : prompt_parts [5];
1662 break;
1663 case 'e':
1664 cp = operation == OP_DELETE ? "?" : prompt_parts [6];
1665 break;
1666 case 'f':
1667 if (only_one)
1669 cp = S_ISDIR (src_stat->st_mode) ?
1670 prompt_parts [2] : prompt_parts [0];
1672 else
1674 cp = (panel->marked == panel->dirs_marked)
1675 ? prompt_parts [3]
1676 : (panel->dirs_marked ? prompt_parts [4]
1677 : prompt_parts [1]);
1679 break;
1680 default:
1681 *dp++ = *sp++;
1683 if (cp)
1685 sp += 2;
1686 while (*cp)
1687 *dp++ = *cp++;
1689 break;
1690 default:
1691 *dp++ = *sp++;
1694 *dp = '\0';
1696 if (only_one)
1698 i = fmd_xlen - strlen(format_string) - 4;
1699 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string, name_trunc (source, i));
1701 else
1703 g_snprintf (cmd_buf, sizeof (cmd_buf), format_string, panel->marked);
1704 i = strlen (cmd_buf) + 6 - fmd_xlen;
1705 if (i > 0)
1707 fmd_xlen += i;
1708 fmd_init_i18n (TRUE); /* to recalculate positions of child widgets */
1712 return source;
1716 * panel_operate:
1718 * Performs one of the operations on the selection on the source_panel
1719 * (copy, delete, move).
1721 * Returns 1 if did change the directory
1722 * structure, Returns 0 if user aborted
1725 panel_operate (void *source_panel, FileOperation operation, char *thedefault, int ask_user)
1727 WPanel *panel = source_panel;
1728 #ifdef WITH_FULL_PATHS
1729 char *source_with_path = NULL;
1730 #else
1731 # define source_with_path source
1732 #endif /* !WITH_FULL_PATHS */
1733 char *source = NULL;
1734 char *dest = NULL;
1735 char *temp = NULL;
1736 char *save_cwd = NULL, *save_dest = NULL;
1737 int only_one = (get_current_type () == view_tree) || (panel->marked <= 1);
1738 struct stat src_stat, dst_stat;
1739 int i, value;
1740 FileOpContext *ctx;
1742 off_t count = 0;
1743 double bytes = 0;
1745 int dst_result;
1746 int do_bg; /* do background operation? */
1748 ctx = file_op_context_new ();
1750 do_bg = 0;
1751 ctx->rx.buffer = NULL;
1752 free_linklist (&linklist);
1753 free_linklist (&dest_dirs);
1754 if (get_current_type () == view_listing)
1755 if (!panel->marked && !strcmp (selection (panel)->fname, "..")){
1756 message (1, MSG_ERROR, _(" Cannot operate on \"..\"! "));
1757 file_op_context_destroy (ctx);
1758 return 0;
1761 if (operation < OP_COPY || operation > OP_DELETE) {
1762 file_op_context_destroy (ctx);
1763 return 0;
1766 /* Generate confirmation prompt */
1767 source = panel_operate_generate_prompt (panel, operation, only_one, &src_stat);
1769 /* Show confirmation dialog */
1770 if (operation == OP_DELETE && confirm_delete){
1771 if (know_not_what_am_i_doing)
1772 query_set_sel (1);
1774 i = query_dialog (_(op_names [operation]), cmd_buf,
1775 D_ERROR, 2, _("&Yes"), _("&No"));
1777 if (i != 0) {
1778 file_op_context_destroy (ctx);
1779 return 0;
1781 } else if (operation != OP_DELETE){
1782 ctx->rx.buffer = (char *) g_malloc (MC_MAXPATHLEN);
1783 ctx->rx.allocated = MC_MAXPATHLEN;
1784 ctx->rx.translate = 0;
1786 if (ask_user){
1787 char *dest_dir;
1789 if (thedefault != NULL)
1790 dest_dir = thedefault;
1791 else if (get_other_type () == view_listing)
1792 dest_dir = opanel->cwd;
1793 else
1794 dest_dir = panel->cwd;
1796 dest = file_mask_dialog (ctx, operation, cmd_buf, dest_dir, only_one, &do_bg);
1797 if (!dest){
1798 g_free (ctx->rx.buffer);
1799 file_op_context_destroy (ctx);
1800 return 0;
1802 if (!*dest){
1803 g_free (ctx->rx.buffer);
1804 file_op_context_destroy (ctx);
1805 g_free (dest);
1806 return 0;
1808 } else {
1809 char *all = "^\\(.*\\)$";
1811 re_compile_pattern (all, strlen (all), &ctx->rx);
1812 ctx->dest_mask = g_strdup ("*");
1813 do_bg = FALSE;
1814 dest = g_strdup (thedefault);
1818 #ifdef WITH_BACKGROUND
1819 /* Did the user select to do a background operation? */
1820 if (do_bg){
1821 int v;
1823 v = do_background (ctx, g_strconcat (op_names [operation], ": ", panel->cwd, NULL));
1824 if (v == -1){
1825 message (1, MSG_ERROR, _(" Sorry, I could not put the job in background "));
1828 /* If we are the parent */
1829 if (v == 1){
1830 mc_setctl (panel->cwd, MCCTL_FORGET_ABOUT, NULL);
1831 mc_setctl (dest, MCCTL_FORGET_ABOUT, NULL);
1832 /* file_op_context_destroy (ctx); */
1833 return 0;
1836 #endif /* WITH_BACKGROUND */
1838 /* Initialize things */
1839 /* We do not want to trash cache every time file is
1840 created/touched. However, this will make our cache contain
1841 invalid data. */
1842 if (dest) {
1843 if (mc_setctl (dest, MCCTL_WANT_STALE_DATA, NULL))
1844 save_dest = g_strdup (dest);
1846 if (panel->cwd) {
1847 if (mc_setctl (panel->cwd, MCCTL_WANT_STALE_DATA, NULL))
1848 save_cwd = g_strdup (panel->cwd);
1851 /* Now, let's do the job */
1853 if (do_bg)
1854 ctx->ui = NULL;
1855 else
1856 file_op_context_create_ui (ctx, operation, 1);
1858 /* This code is only called by the tree and panel code */
1859 if (only_one){
1860 /* We now have ETA in all cases */
1862 /* One file: FIXME mc_chdir will take user out of any vfs */
1863 if (operation != OP_COPY && get_current_type () == view_tree)
1864 mc_chdir (PATH_SEP_STR);
1866 /* The source and src_stat variables have been initialized before */
1867 #ifdef WITH_FULL_PATHS
1868 source_with_path = concat_dir_and_file (panel->cwd, source);
1869 #endif /* WITH_FULL_PATHS */
1871 if (operation == OP_DELETE) {
1872 if (S_ISDIR (src_stat.st_mode))
1873 value = erase_dir (ctx, source_with_path, &count, &bytes);
1874 else
1875 value = erase_file (ctx, source_with_path, &count, &bytes, 1);
1876 } else {
1877 temp = transform_source (ctx, source_with_path);
1879 if (temp == NULL){
1880 value = transform_error;
1881 } else {
1882 temp = concat_dir_and_file (dest, temp);
1883 g_free (dest);
1884 dest = temp;
1885 temp = 0;
1887 switch (operation) {
1888 case OP_COPY:
1890 * we use file_mask_op_follow_links only with OP_COPY,
1892 (* ctx->stat_func) (source_with_path, &src_stat);
1894 if (S_ISDIR (src_stat.st_mode))
1895 value = copy_dir_dir (ctx, source_with_path, dest, 1, 0, 0, 0,
1896 &count, &bytes);
1897 else
1898 value = copy_file_file (ctx, source_with_path, dest, 1, &count, &bytes, 1);
1899 break;
1901 case OP_MOVE:
1902 if (S_ISDIR (src_stat.st_mode))
1903 value = move_dir_dir (ctx, source_with_path, dest, &count, &bytes);
1904 else
1905 value = move_file_file (ctx, source_with_path, dest, &count, &bytes);
1906 break;
1908 default:
1909 value = FILE_CONT;
1910 message_1s (1, _(" Internal failure "), _(" Unknown file operation "));
1913 } /* Copy or move operation */
1915 if (value == FILE_CONT)
1916 unmark_files (panel);
1917 } else {
1918 /* Many files */
1919 /* Check destination for copy or move operation */
1920 if (operation != OP_DELETE){
1921 retry_many_dst_stat:
1922 dst_result = mc_stat (dest, &dst_stat);
1923 if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)){
1924 if (file_error (_(" Destination \"%s\" must be a directory \n %s "),
1925 dest) == FILE_RETRY)
1926 goto retry_many_dst_stat;
1927 goto clean_up;
1931 /* Initialize variables for progress bars */
1932 if (operation != OP_MOVE && verbose && file_op_compute_totals) {
1933 panel_compute_totals (panel, &ctx->progress_count, &ctx->progress_bytes);
1934 ctx->progress_totals_computed = 1;
1935 } else {
1936 ctx->progress_totals_computed = 0;
1937 ctx->progress_count = panel->marked;
1938 ctx->progress_bytes = panel->total;
1941 /* Loop for every file, perform the actual copy operation */
1942 for (i = 0; i < panel->count; i++) {
1943 if (!panel->dir.list [i].f.marked)
1944 continue; /* Skip the unmarked ones */
1946 source = panel->dir.list [i].fname;
1947 src_stat = panel->dir.list [i].buf;
1949 #ifdef WITH_FULL_PATHS
1950 if (source_with_path)
1951 g_free (source_with_path);
1952 source_with_path = concat_dir_and_file (panel->cwd, source);
1953 #endif /* WITH_FULL_PATHS */
1955 if (operation == OP_DELETE){
1956 if (S_ISDIR (src_stat.st_mode))
1957 value = erase_dir (ctx, source_with_path, &count, &bytes);
1958 else
1959 value = erase_file (ctx, source_with_path, &count, &bytes, 1);
1960 } else {
1961 if (temp)
1962 g_free (temp);
1964 temp = transform_source (ctx, source_with_path);
1965 if (temp == NULL)
1966 value = transform_error;
1967 else {
1968 temp = concat_dir_and_file (dest, temp);
1970 switch (operation){
1971 case OP_COPY:
1973 * we use file_mask_op_follow_links only with OP_COPY,
1975 (* ctx->stat_func) (source_with_path, &src_stat);
1976 if (S_ISDIR (src_stat.st_mode))
1977 value = copy_dir_dir (ctx, source_with_path, temp, 1, 0, 0, 0,
1978 &count, &bytes);
1979 else
1980 value = copy_file_file (ctx, source_with_path, temp, 1,
1981 &count, &bytes, 1);
1982 free_linklist (&dest_dirs);
1983 break;
1985 case OP_MOVE:
1986 if (S_ISDIR (src_stat.st_mode))
1987 value = move_dir_dir (ctx, source_with_path, temp, &count, &bytes);
1988 else
1989 value = move_file_file (ctx, source_with_path, temp, &count, &bytes);
1990 break;
1992 default:
1993 message_1s (1, _(" Internal failure "), _(" Unknown file operation "));
1994 goto clean_up;
1997 } /* Copy or move operation */
1999 if (value == FILE_ABORT)
2000 goto clean_up;
2002 if (value == FILE_CONT)
2003 do_file_mark (panel, i, 0);
2005 if (file_progress_show_count (ctx, count, ctx->progress_count) == FILE_ABORT)
2006 goto clean_up;
2008 if (verbose && file_progress_show_bytes (ctx, bytes, ctx->progress_bytes) == FILE_ABORT)
2009 goto clean_up;
2011 if (operation != OP_DELETE && verbose && file_progress_show (ctx, 0, 0) == FILE_ABORT)
2012 goto clean_up;
2014 mc_refresh ();
2015 } /* Loop for every file */
2016 } /* Many files */
2017 clean_up:
2018 /* Clean up */
2020 if (save_cwd) {
2021 mc_setctl (save_cwd, MCCTL_NO_STALE_DATA, NULL);
2022 g_free (save_cwd);
2024 if (save_dest) {
2025 mc_setctl (save_dest, MCCTL_NO_STALE_DATA, NULL);
2026 g_free (save_dest);
2029 free_linklist (&linklist);
2030 free_linklist (&dest_dirs);
2031 #ifdef WITH_FULL_PATHS
2032 if (source_with_path)
2033 g_free (source_with_path);
2034 #endif /* WITH_FULL_PATHS */
2036 if (dest)
2037 g_free (dest);
2039 if (temp)
2040 g_free (temp);
2042 if (ctx->rx.buffer) {
2043 g_free (ctx->rx.buffer);
2044 ctx->rx.buffer = NULL;
2047 if (ctx->dest_mask) {
2048 g_free (ctx->dest_mask);
2049 ctx->dest_mask = NULL;
2052 #ifdef WITH_BACKGROUND
2053 /* Let our parent know we are saying bye bye */
2054 if (we_are_background) {
2055 vfs_shut ();
2056 tell_parent (MSG_CHILD_EXITING);
2057 _exit (1);
2059 #endif /* WITH_BACKGROUND */
2061 file_op_context_destroy (ctx);
2062 return 1;
2065 /* }}} */
2067 /* {{{ Query/status report routines */
2069 static int
2070 real_do_file_error (enum OperationMode mode, char *error)
2072 int result;
2073 char *msg;
2075 msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
2076 result = query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"), _("&Abort"));
2078 switch (result){
2079 case 0:
2080 do_refresh ();
2081 return FILE_SKIP;
2083 case 1:
2084 do_refresh ();
2085 return FILE_RETRY;
2087 case 2:
2088 default:
2089 return FILE_ABORT;
2093 /* Report error with one file */
2095 file_error (char *format, char *file)
2097 g_snprintf (cmd_buf, sizeof (cmd_buf), format,
2098 name_trunc (file, 30), unix_error_string (errno));
2100 return do_file_error (cmd_buf);
2103 /* Report error with two files */
2105 files_error (char *format, char *file1, char *file2)
2107 char nfile1 [16];
2108 char nfile2 [16];
2110 strcpy (nfile1, name_trunc (file1, 15));
2111 strcpy (nfile2, name_trunc (file2, 15));
2113 g_snprintf (cmd_buf, sizeof (cmd_buf), format, nfile1, nfile2,
2114 unix_error_string (errno));
2116 return do_file_error (cmd_buf);
2119 static int
2120 real_query_recursive (FileOpContext *ctx, enum OperationMode mode, char *s)
2122 char *confirm;
2123 gchar *text;
2125 if (ctx->recursive_result < RECURSIVE_ALWAYS){
2126 char *msg =
2127 mode == Foreground ? _("\n Directory not empty. \n Delete it recursively? ")
2128 : _("\n Background process: Directory not empty \n Delete it recursively? ");
2129 text = g_strconcat (_(" Delete: "), name_trunc (s, 30), " ", NULL);
2131 if (know_not_what_am_i_doing)
2132 query_set_sel (1);
2133 ctx->recursive_result = query_dialog (text, msg, D_ERROR, 5,
2134 _("&Yes"), _("&No"),
2135 _("a&ll"), _("non&E"),
2136 _("&Abort"));
2138 if (ctx->recursive_result != RECURSIVE_ABORT)
2139 do_refresh ();
2140 g_free (text);
2141 if (know_not_what_am_i_doing){
2142 if (ctx->recursive_result == RECURSIVE_YES ||
2143 ctx->recursive_result == RECURSIVE_ALWAYS){
2144 text = g_strconcat (
2145 _(" Type 'yes' if you REALLY want to delete "),
2146 ctx->recursive_result == RECURSIVE_YES
2147 ? name_trunc (s, 19) : _("all the directories "), " ", NULL);
2148 confirm = input_dialog (
2149 mode == Foreground ? _(" Recursive Delete ")
2150 : _(" Background process: Recursive Delete "),
2151 text, _("no"));
2152 do_refresh ();
2153 if (!confirm || strcmp (confirm, _("yes")))
2154 ctx->recursive_result = RECURSIVE_NEVER;
2155 g_free (confirm);
2156 g_free (text);
2161 switch (ctx->recursive_result){
2162 case RECURSIVE_YES:
2163 case RECURSIVE_ALWAYS:
2164 return FILE_CONT;
2166 case RECURSIVE_NO:
2167 case RECURSIVE_NEVER:
2168 return FILE_SKIP;
2170 case RECURSIVE_ABORT:
2172 default:
2173 return FILE_ABORT;
2177 #ifdef WITH_BACKGROUND
2178 static int
2179 do_file_error (char *str)
2181 if (we_are_background)
2182 return parent_call (real_do_file_error, NULL, 1, strlen (str), str);
2183 else
2184 return real_do_file_error (Foreground, str);
2187 static int
2188 query_recursive (FileOpContext *ctx, char *s)
2190 if (we_are_background)
2191 return parent_call (real_query_recursive, ctx, 1, strlen (s), s);
2192 else
2193 return real_query_recursive (ctx, Foreground, s);
2196 static int
2197 query_replace (FileOpContext *ctx, char *destname, struct stat *_s_stat, struct stat *_d_stat)
2199 if (we_are_background)
2200 return parent_call ((void *)file_progress_real_query_replace,
2201 ctx,
2203 strlen (destname), destname,
2204 sizeof (struct stat), _s_stat,
2205 sizeof(struct stat), _d_stat);
2206 else
2207 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
2210 #else
2211 static int
2212 do_file_error (char *str)
2214 return real_do_file_error (Foreground, str);
2217 static int
2218 query_recursive (FileOpContext *ctx, char *s)
2220 return real_query_recursive (ctx, Foreground, s);
2223 static int
2224 query_replace (FileOpContext *ctx, char *destname, struct stat *_s_stat, struct stat *_d_stat)
2226 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
2229 #endif /* !WITH_BACKGROUND */
2232 Cause emacs to enter folding mode for this file:
2233 Local variables:
2234 end: