cppcheck: Cleanup Passing NULL after the last typed argument warning
[midnight-commander.git] / src / filemanager / file.c
blob5ca363c99da46fc054329a7f6eb94e3612cef627
1 /*
2 File management.
4 Copyright (C) 1994-2016
5 Free Software Foundation, Inc.
7 Written by:
8 Janne Kukonlehto, 1994, 1995
9 Fred Leeflang, 1994, 1995
10 Miguel de Icaza, 1994, 1995, 1996
11 Jakub Jelinek, 1995, 1996
12 Norbert Warmuth, 1997
13 Pavel Machek, 1998
14 Andrew Borodin <aborodin@vmail.ru>, 2011-2014
16 The copy code was based in GNU's cp, and was written by:
17 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
19 The move code was based in GNU's mv, and was written by:
20 Mike Parker and David MacKenzie.
22 Janne Kukonlehto added much error recovery to them for being used
23 in an interactive program.
25 This file is part of the Midnight Commander.
27 The Midnight Commander is free software: you can redistribute it
28 and/or modify it under the terms of the GNU General Public License as
29 published by the Free Software Foundation, either version 3 of the License,
30 or (at your option) any later version.
32 The Midnight Commander is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program. If not, see <http://www.gnu.org/licenses/>.
42 * Please note that all dialogs used here must be safe for background
43 * operations.
46 /** \file src/filemanager/file.c
47 * \brief Source: file management
50 /* {{{ Include files */
52 #include <config.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
63 #include "lib/global.h"
64 #include "lib/tty/tty.h"
65 #include "lib/tty/key.h"
66 #include "lib/search.h"
67 #include "lib/strescape.h"
68 #include "lib/strutil.h"
69 #include "lib/util.h"
70 #include "lib/vfs/vfs.h"
71 #include "lib/widget.h"
73 #include "src/setup.h"
74 #ifdef ENABLE_BACKGROUND
75 #include "src/background.h" /* do_background() */
76 #endif
78 /* Needed for current_panel, other_panel and WTree */
79 #include "dir.h"
80 #include "filegui.h"
81 #include "filenot.h"
82 #include "tree.h"
83 #include "midnight.h" /* current_panel */
84 #include "layout.h" /* rotate_dash() */
85 #include "ioblksize.h" /* io_blksize() */
87 #include "file.h"
89 /* }}} */
91 /*** global variables ****************************************************************************/
93 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
94 const char *op_names[3] = {
95 N_("DialogTitle|Copy"),
96 N_("DialogTitle|Move"),
97 N_("DialogTitle|Delete")
100 /*** file scope macro definitions ****************************************************************/
102 /* Hack: the vfs code should not rely on this */
103 #define WITH_FULL_PATHS 1
105 #define FILEOP_UPDATE_INTERVAL 2
106 #define FILEOP_STALLING_INTERVAL 4
108 /*** file scope type declarations ****************************************************************/
110 /* This is a hard link cache */
111 struct link
113 const struct vfs_class *vfs;
114 dev_t dev;
115 ino_t ino;
116 short linkcount;
117 mode_t st_mode;
118 vfs_path_t *src_vpath;
119 vfs_path_t *dst_vpath;
122 /* Status of the destination file */
123 typedef enum
125 DEST_NONE = 0, /* Not created */
126 DEST_SHORT = 1, /* Created, not fully copied */
127 DEST_FULL = 2 /* Created, fully copied */
128 } dest_status_t;
131 * This array introduced to avoid translation problems. The former (op_names)
132 * is assumed to be nouns, suitable in dialog box titles; this one should
133 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
134 * (I don't use spaces around the words, because someday they could be
135 * dropped, when widgets get smarter)
138 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
139 static const char *op_names1[] = {
140 N_("FileOperation|Copy"),
141 N_("FileOperation|Move"),
142 N_("FileOperation|Delete")
146 * These are formats for building a prompt. Parts encoded as follows:
147 * %o - operation from op_names1
148 * %f - file/files or files/directories, as appropriate
149 * %m - "with source mask" or question mark for delete
150 * %s - source name (truncated)
151 * %d - number of marked files
152 * %n - the '\n' symbol to form two-line prompt for delete or space for other operations
154 /* xgettext:no-c-format */
155 static const char *one_format = N_("%o %f%n\"%s\"%m");
156 /* xgettext:no-c-format */
157 static const char *many_format = N_("%o %d %f%m");
159 static const char *prompt_parts[] = {
160 N_("file"),
161 N_("files"),
162 N_("directory"),
163 N_("directories"),
164 N_("files/directories"),
165 /* TRANSLATORS: keep leading space here to split words in Copy/Move dialog */
166 N_(" with source mask:")
169 /*** file scope variables ************************************************************************/
171 /* the hard link cache */
172 static GSList *linklist = NULL;
174 /* the files-to-be-erased list */
175 static GSList *erase_list = NULL;
178 * In copy_dir_dir we use two additional single linked lists: The first -
179 * variable name 'parent_dirs' - holds information about already copied
180 * directories and is used to detect cyclic symbolic links.
181 * The second ('dest_dirs' below) holds information about just created
182 * target directories and is used to detect when an directory is copied
183 * into itself (we don't want to copy infinitly).
184 * Both lists don't use the linkcount and name structure members of struct
185 * link.
187 static GSList *dest_dirs = NULL;
189 static FileProgressStatus transform_error = FILE_CONT;
191 /* --------------------------------------------------------------------------------------------- */
192 /*** file scope functions ************************************************************************/
193 /* --------------------------------------------------------------------------------------------- */
195 static void
196 dirsize_status_locate_buttons (dirsize_status_msg_t * dsm)
198 status_msg_t *sm = STATUS_MSG (dsm);
199 Widget *wd = WIDGET (sm->dlg);
200 int y, x;
202 y = wd->y + 5;
203 x = wd->x;
205 if (!dsm->allow_skip)
207 /* single button: "Abort" */
208 x += (wd->cols - dsm->abort_button->cols) / 2;
209 widget_set_size (dsm->abort_button, y, x,
210 dsm->abort_button->lines, dsm->abort_button->cols);
212 else
214 /* two buttons: "Abort" and "Skip" */
215 int cols;
217 cols = dsm->abort_button->cols + dsm->skip_button->cols + 1;
218 x += (wd->cols - cols) / 2;
219 widget_set_size (dsm->abort_button, y, x, dsm->abort_button->lines,
220 dsm->abort_button->cols);
221 x += dsm->abort_button->cols + 1;
222 widget_set_size (dsm->skip_button, y, x, dsm->skip_button->lines, dsm->skip_button->cols);
226 /* --------------------------------------------------------------------------------------------- */
228 static char *
229 transform_source (file_op_context_t * ctx, const vfs_path_t * source_vpath)
231 char *s, *q;
232 const char *fnsource;
234 s = g_strdup (vfs_path_as_str (source_vpath));
236 /* We remove \n from the filename since regex routines would use \n as an anchor */
237 /* this is just to be allowed to maniupulate file names with \n on it */
238 for (q = s; *q != '\0'; q++)
239 if (*q == '\n')
240 *q = ' ';
242 fnsource = x_basename (s);
244 if (mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
246 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
247 if (ctx->search_handle->error != MC_SEARCH_E_OK)
249 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
250 q = NULL;
251 transform_error = FILE_ABORT;
254 else
256 q = NULL;
257 transform_error = FILE_SKIP;
260 g_free (s);
261 return q;
264 /* --------------------------------------------------------------------------------------------- */
266 static void
267 free_link (void *data)
269 struct link *lp = (struct link *) data;
271 vfs_path_free (lp->src_vpath);
272 vfs_path_free (lp->dst_vpath);
273 g_free (lp);
276 /* --------------------------------------------------------------------------------------------- */
278 static inline void *
279 free_linklist (GSList * lp)
281 g_slist_free_full (lp, free_link);
283 return NULL;
286 /* --------------------------------------------------------------------------------------------- */
288 static gboolean
289 is_in_linklist (const GSList * lp, const vfs_path_t * vpath, const struct stat *sb)
291 const struct vfs_class *class;
292 ino_t ino = sb->st_ino;
293 dev_t dev = sb->st_dev;
295 class = vfs_path_get_last_path_vfs (vpath);
297 for (; lp != NULL; lp = (const GSList *) g_slist_next (lp))
299 const struct link *lnk = (const struct link *) lp->data;
301 if (lnk->vfs == class && lnk->ino == ino && lnk->dev == dev)
302 return TRUE;
304 return FALSE;
307 /* --------------------------------------------------------------------------------------------- */
309 * Check and made hardlink
311 * @return FALSE if the inode wasn't found in the cache and TRUE if it was found
312 * and a hardlink was successfully made
315 static gboolean
316 check_hardlinks (const vfs_path_t * src_vpath, const vfs_path_t * dst_vpath, struct stat *pstat)
318 GSList *lp;
319 struct link *lnk;
321 const struct vfs_class *my_vfs;
322 ino_t ino = pstat->st_ino;
323 dev_t dev = pstat->st_dev;
324 struct stat link_stat;
326 if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
327 return FALSE;
329 my_vfs = vfs_path_get_by_index (src_vpath, -1)->class;
331 for (lp = linklist; lp != NULL; lp = g_slist_next (lp))
333 lnk = (struct link *) lp->data;
335 if (lnk->vfs == my_vfs && lnk->ino == ino && lnk->dev == dev)
337 const struct vfs_class *lp_name_class;
338 int stat_result;
340 lp_name_class = vfs_path_get_last_path_vfs (lnk->src_vpath);
341 stat_result = mc_stat (lnk->src_vpath, &link_stat);
343 if (stat_result == 0 && link_stat.st_ino == ino
344 && link_stat.st_dev == dev && lp_name_class == my_vfs)
346 const struct vfs_class *p_class, *dst_name_class;
348 dst_name_class = vfs_path_get_last_path_vfs (dst_vpath);
349 p_class = vfs_path_get_last_path_vfs (lnk->dst_vpath);
351 if (dst_name_class == p_class &&
352 mc_stat (lnk->dst_vpath, &link_stat) == 0 &&
353 mc_link (lnk->dst_vpath, dst_vpath) == 0)
354 return TRUE;
357 message (D_ERROR, MSG_ERROR, _("Cannot make the hardlink"));
358 return FALSE;
362 lnk = g_new0 (struct link, 1);
363 if (lnk != NULL)
365 lnk->vfs = my_vfs;
366 lnk->ino = ino;
367 lnk->dev = dev;
368 lnk->src_vpath = vfs_path_clone (src_vpath);
369 lnk->dst_vpath = vfs_path_clone (dst_vpath);
370 linklist = g_slist_prepend (linklist, lnk);
373 return FALSE;
376 /* --------------------------------------------------------------------------------------------- */
378 * Duplicate the contents of the symbolic link src_path in dst_path.
379 * Try to make a stable symlink if the option "stable symlink" was
380 * set in the file mask dialog.
381 * If dst_path is an existing symlink it will be deleted silently
382 * (upper levels take already care of existing files at dst_path).
385 static FileProgressStatus
386 make_symlink (file_op_context_t * ctx, const char *src_path, const char *dst_path)
388 char link_target[MC_MAXPATHLEN];
389 int len;
390 FileProgressStatus return_status;
391 struct stat sb;
392 vfs_path_t *src_vpath;
393 vfs_path_t *dst_vpath;
394 gboolean dst_is_symlink;
395 vfs_path_t *link_target_vpath = NULL;
397 src_vpath = vfs_path_from_str (src_path);
398 dst_vpath = vfs_path_from_str (dst_path);
399 dst_is_symlink = (mc_lstat (dst_vpath, &sb) == 0) && S_ISLNK (sb.st_mode);
401 retry_src_readlink:
402 len = mc_readlink (src_vpath, link_target, MC_MAXPATHLEN - 1);
403 if (len < 0)
405 if (ctx->skip_all)
406 return_status = FILE_SKIPALL;
407 else
409 return_status = file_error (_("Cannot read source link \"%s\"\n%s"), src_path);
410 if (return_status == FILE_SKIPALL)
411 ctx->skip_all = TRUE;
412 if (return_status == FILE_RETRY)
413 goto retry_src_readlink;
415 goto ret;
417 link_target[len] = 0;
419 if (ctx->stable_symlinks)
422 if (!vfs_file_is_local (src_vpath) || !vfs_file_is_local (dst_vpath))
424 message (D_ERROR, MSG_ERROR,
425 _("Cannot make stable symlinks across"
426 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
427 ctx->stable_symlinks = FALSE;
431 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
433 const char *r = strrchr (src_path, PATH_SEP);
435 if (r)
437 char *p;
438 vfs_path_t *q;
440 p = g_strndup (src_path, r - src_path + 1);
441 if (g_path_is_absolute (dst_path))
442 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
443 else
444 q = vfs_path_build_filename (p, dst_path, (char *) NULL);
446 if (vfs_path_tokens_count (q) > 1)
448 char *s;
449 vfs_path_t *tmp_vpath1, *tmp_vpath2;
451 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
452 s = g_strconcat (p, link_target, (char *) NULL);
453 g_free (p);
454 g_strlcpy (link_target, s, sizeof (link_target));
455 g_free (s);
456 tmp_vpath2 = vfs_path_from_str (link_target);
457 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
458 vfs_path_free (tmp_vpath1);
459 vfs_path_free (tmp_vpath2);
460 if (s)
462 g_strlcpy (link_target, s, sizeof (link_target));
463 g_free (s);
466 else
467 g_free (p);
468 vfs_path_free (q);
471 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
473 retry_dst_symlink:
474 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
476 /* Success */
477 return_status = FILE_CONT;
478 goto ret;
481 * if dst_exists, it is obvious that this had failed.
482 * We can delete the old symlink and try again...
484 if (dst_is_symlink)
486 if (mc_unlink (dst_vpath) == 0)
487 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
489 /* Success */
490 return_status = FILE_CONT;
491 goto ret;
494 if (ctx->skip_all)
495 return_status = FILE_SKIPALL;
496 else
498 return_status = file_error (_("Cannot create target symlink \"%s\"\n%s"), dst_path);
499 if (return_status == FILE_SKIPALL)
500 ctx->skip_all = TRUE;
501 if (return_status == FILE_RETRY)
502 goto retry_dst_symlink;
505 ret:
506 vfs_path_free (src_vpath);
507 vfs_path_free (dst_vpath);
508 vfs_path_free (link_target_vpath);
509 return return_status;
512 /* --------------------------------------------------------------------------------------------- */
514 * do_compute_dir_size:
516 * Computes the number of bytes used by the files in a directory
519 static FileProgressStatus
520 do_compute_dir_size (const vfs_path_t * dirname_vpath, dirsize_status_msg_t * dsm,
521 size_t * dir_count, size_t * ret_marked, uintmax_t * ret_total,
522 gboolean compute_symlinks)
524 static guint64 timestamp = 0;
525 /* update with 25 FPS rate */
526 static const guint64 delay = G_USEC_PER_SEC / 25;
528 status_msg_t *sm = STATUS_MSG (dsm);
529 int res;
530 struct stat s;
531 DIR *dir;
532 struct dirent *dirent;
533 FileProgressStatus ret = FILE_CONT;
535 if (!compute_symlinks)
537 res = mc_lstat (dirname_vpath, &s);
538 if (res != 0)
539 return ret;
541 /* don't scan symlink to directory */
542 if (S_ISLNK (s.st_mode))
544 (*ret_marked)++;
545 *ret_total += (uintmax_t) s.st_size;
546 return ret;
550 (*dir_count)++;
552 dir = mc_opendir (dirname_vpath);
553 if (dir == NULL)
554 return ret;
556 while (ret == FILE_CONT && (dirent = mc_readdir (dir)) != NULL)
558 vfs_path_t *tmp_vpath;
560 if (DIR_IS_DOT (dirent->d_name) || DIR_IS_DOTDOT (dirent->d_name))
561 continue;
563 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, (char *) NULL);
565 res = mc_lstat (tmp_vpath, &s);
566 if (res == 0)
568 if (S_ISDIR (s.st_mode))
569 ret =
570 do_compute_dir_size (tmp_vpath, dsm, dir_count, ret_marked, ret_total,
571 compute_symlinks);
572 else
574 ret = FILE_CONT;
576 (*ret_marked)++;
577 *ret_total += (uintmax_t) s.st_size;
580 if (ret == FILE_CONT && sm->update != NULL && mc_time_elapsed (&timestamp, delay))
582 dsm->dirname_vpath = tmp_vpath;
583 dsm->dir_count = *dir_count;
584 dsm->total_size = *ret_total;
585 ret = sm->update (sm);
589 vfs_path_free (tmp_vpath);
592 mc_closedir (dir);
593 return ret;
596 /* --------------------------------------------------------------------------------------------- */
598 static FileProgressStatus
599 progress_update_one (file_op_total_context_t * tctx, file_op_context_t * ctx, off_t add)
601 struct timeval tv_current;
602 static struct timeval tv_start = { 0, 0 };
604 tctx->progress_count++;
605 tctx->progress_bytes += (uintmax_t) add;
607 if (tv_start.tv_sec == 0)
609 gettimeofday (&tv_start, (struct timezone *) NULL);
611 gettimeofday (&tv_current, (struct timezone *) NULL);
612 if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL)
614 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
616 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
617 file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE);
619 tv_start.tv_sec = tv_current.tv_sec;
622 return check_progress_buttons (ctx);
625 /* --------------------------------------------------------------------------------------------- */
627 static FileProgressStatus
628 real_warn_same_file (enum OperationMode mode, const char *fmt, const char *a, const char *b)
630 char *msg;
631 int result = 0;
632 const char *head_msg;
634 head_msg = mode == Foreground ? MSG_ERROR : _("Background process error");
636 msg = g_strdup_printf (fmt, a, b);
637 result = query_dialog (head_msg, msg, D_ERROR, 2, _("&Skip"), _("&Abort"));
638 g_free (msg);
639 do_refresh ();
641 return (result == 1) ? FILE_ABORT : FILE_SKIP;
644 /* --------------------------------------------------------------------------------------------- */
646 static FileProgressStatus
647 warn_same_file (const char *fmt, const char *a, const char *b)
649 #ifdef ENABLE_BACKGROUND
650 /* *INDENT-OFF* */
651 union
653 void *p;
654 FileProgressStatus (*f) (enum OperationMode, const char *fmt, const char *a, const char *b);
655 } pntr;
656 /* *INDENT-ON* */
658 pntr.f = real_warn_same_file;
660 if (mc_global.we_are_background)
661 return parent_call (pntr.p, NULL, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
662 #endif
663 return real_warn_same_file (Foreground, fmt, a, b);
666 /* --------------------------------------------------------------------------------------------- */
667 /* {{{ Query/status report routines */
669 static FileProgressStatus
670 real_do_file_error (enum OperationMode mode, const char *error)
672 int result;
673 const char *msg;
675 msg = mode == Foreground ? MSG_ERROR : _("Background process error");
676 result =
677 query_dialog (msg, error, D_ERROR, 4, _("&Skip"), _("Ski&p all"), _("&Retry"), _("&Abort"));
679 switch (result)
681 case 0:
682 do_refresh ();
683 return FILE_SKIP;
685 case 1:
686 do_refresh ();
687 return FILE_SKIPALL;
689 case 2:
690 do_refresh ();
691 return FILE_RETRY;
693 case 3:
694 default:
695 return FILE_ABORT;
699 /* --------------------------------------------------------------------------------------------- */
701 static FileProgressStatus
702 real_query_recursive (file_op_context_t * ctx, enum OperationMode mode, const char *s)
704 if (ctx->recursive_result < RECURSIVE_ALWAYS)
706 const char *msg;
707 char *text;
709 msg = mode == Foreground
710 ? _("Directory \"%s\" not empty.\nDelete it recursively?")
711 : _("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
712 text = g_strdup_printf (msg, path_trunc (s, 30));
714 if (safe_delete)
715 query_set_sel (1);
717 ctx->recursive_result =
718 query_dialog (op_names[OP_DELETE], text, D_ERROR, 5, _("&Yes"), _("&No"), _("A&ll"),
719 _("Non&e"), _("&Abort"));
720 g_free (text);
722 if (ctx->recursive_result != RECURSIVE_ABORT)
723 do_refresh ();
726 switch (ctx->recursive_result)
728 case RECURSIVE_YES:
729 case RECURSIVE_ALWAYS:
730 return FILE_CONT;
732 case RECURSIVE_NO:
733 case RECURSIVE_NEVER:
734 return FILE_SKIP;
736 case RECURSIVE_ABORT:
737 default:
738 return FILE_ABORT;
742 /* --------------------------------------------------------------------------------------------- */
744 #ifdef ENABLE_BACKGROUND
745 static FileProgressStatus
746 do_file_error (const char *str)
748 /* *INDENT-OFF* */
749 union
751 void *p;
752 FileProgressStatus (*f) (enum OperationMode, const char *);
753 } pntr;
754 /* *INDENT-ON* */
756 pntr.f = real_do_file_error;
758 if (mc_global.we_are_background)
759 return parent_call (pntr.p, NULL, 1, strlen (str), str);
760 else
761 return real_do_file_error (Foreground, str);
764 /* --------------------------------------------------------------------------------------------- */
766 static FileProgressStatus
767 query_recursive (file_op_context_t * ctx, const char *s)
769 /* *INDENT-OFF* */
770 union
772 void *p;
773 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *);
774 } pntr;
775 /* *INDENT-ON* */
777 pntr.f = real_query_recursive;
779 if (mc_global.we_are_background)
780 return parent_call (pntr.p, ctx, 1, strlen (s), s);
781 else
782 return real_query_recursive (ctx, Foreground, s);
785 /* --------------------------------------------------------------------------------------------- */
787 static FileProgressStatus
788 query_replace (file_op_context_t * ctx, const char *destname, struct stat *_s_stat,
789 struct stat *_d_stat)
791 /* *INDENT-OFF* */
792 union
794 void *p;
795 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *,
796 struct stat *, struct stat *);
797 } pntr;
798 /* *INDENT-ON* */
800 pntr.f = file_progress_real_query_replace;
802 if (mc_global.we_are_background)
803 return parent_call (pntr.p, ctx, 3, strlen (destname), destname,
804 sizeof (struct stat), _s_stat, sizeof (struct stat), _d_stat);
805 else
806 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
809 #else
810 /* --------------------------------------------------------------------------------------------- */
812 static FileProgressStatus
813 do_file_error (const char *str)
815 return real_do_file_error (Foreground, str);
818 /* --------------------------------------------------------------------------------------------- */
820 static FileProgressStatus
821 query_recursive (file_op_context_t * ctx, const char *s)
823 return real_query_recursive (ctx, Foreground, s);
826 /* --------------------------------------------------------------------------------------------- */
828 static FileProgressStatus
829 query_replace (file_op_context_t * ctx, const char *destname, struct stat *_s_stat,
830 struct stat *_d_stat)
832 return file_progress_real_query_replace (ctx, Foreground, destname, _s_stat, _d_stat);
835 #endif /* !ENABLE_BACKGROUND */
837 /* --------------------------------------------------------------------------------------------- */
838 /** Report error with two files */
840 static FileProgressStatus
841 files_error (const char *format, const char *file1, const char *file2)
843 char buf[BUF_MEDIUM];
844 char *nfile1 = g_strdup (path_trunc (file1, 15));
845 char *nfile2 = g_strdup (path_trunc (file2, 15));
847 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
849 g_free (nfile1);
850 g_free (nfile2);
852 return do_file_error (buf);
855 /* }}} */
857 /* --------------------------------------------------------------------------------------------- */
859 static void
860 copy_file_file_display_progress (file_op_total_context_t * tctx, file_op_context_t * ctx,
861 struct timeval tv_current, struct timeval tv_transfer_start,
862 off_t file_size, off_t n_read_total)
864 long dt;
866 /* 1. Update rotating dash after some time */
867 rotate_dash (TRUE);
869 /* 3. Compute ETA */
870 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
872 if (n_read_total == 0)
873 ctx->eta_secs = 0.0;
874 else
876 ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
877 ctx->bps = n_read_total / ((dt < 1) ? 1 : dt);
880 /* 4. Compute BPS rate */
881 ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
882 if (ctx->bps_time < 1)
883 ctx->bps_time = 1;
884 ctx->bps = n_read_total / ctx->bps_time;
886 /* 5. Compute total ETA and BPS */
887 if (ctx->progress_bytes != 0)
889 uintmax_t remain_bytes;
891 remain_bytes = ctx->progress_bytes - tctx->copied_bytes;
892 #if 1
894 int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec;
896 if (total_secs < 1)
897 total_secs = 1;
899 tctx->bps = tctx->copied_bytes / total_secs;
900 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
902 #else
903 /* broken on lot of little files */
904 tctx->bps_count++;
905 tctx->bps = (tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count;
906 tctx->eta_secs = (tctx->bps != 0) ? remain_bytes / tctx->bps : 0;
907 #endif
911 /* --------------------------------------------------------------------------------------------- */
913 /* {{{ Move routines */
914 static FileProgressStatus
915 move_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s,
916 const char *d)
918 struct stat src_stats, dst_stats;
919 FileProgressStatus return_status = FILE_CONT;
920 gboolean copy_done = FALSE;
921 gboolean old_ask_overwrite;
922 vfs_path_t *src_vpath, *dst_vpath;
924 src_vpath = vfs_path_from_str (s);
925 dst_vpath = vfs_path_from_str (d);
927 file_progress_show_source (ctx, src_vpath);
928 file_progress_show_target (ctx, dst_vpath);
930 if (check_progress_buttons (ctx) == FILE_ABORT)
932 return_status = FILE_ABORT;
933 goto ret;
936 mc_refresh ();
938 while (mc_lstat (src_vpath, &src_stats) != 0)
940 /* Source doesn't exist */
941 if (ctx->skip_all)
942 return_status = FILE_SKIPALL;
943 else
945 return_status = file_error (_("Cannot stat file \"%s\"\n%s"), s);
946 if (return_status == FILE_SKIPALL)
947 ctx->skip_all = TRUE;
950 if (return_status != FILE_RETRY)
951 goto ret;
954 if (mc_lstat (dst_vpath, &dst_stats) == 0)
956 if (src_stats.st_dev == dst_stats.st_dev && src_stats.st_ino == dst_stats.st_ino)
958 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"), s, d);
959 goto ret;
962 if (S_ISDIR (dst_stats.st_mode))
964 message (D_ERROR, MSG_ERROR, _("Cannot overwrite directory \"%s\""), d);
965 do_refresh ();
966 return_status = FILE_SKIP;
967 goto ret;
970 if (confirm_overwrite)
972 return_status = query_replace (ctx, d, &src_stats, &dst_stats);
973 if (return_status != FILE_CONT)
974 goto ret;
976 /* Ok to overwrite */
979 if (!ctx->do_append)
981 if (S_ISLNK (src_stats.st_mode) && ctx->stable_symlinks)
983 return_status = make_symlink (ctx, s, d);
984 if (return_status == FILE_CONT)
985 goto retry_src_remove;
986 goto ret;
989 if (mc_rename (src_vpath, dst_vpath) == 0)
991 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
992 goto ret;
995 #if 0
996 /* Comparison to EXDEV seems not to work in nfs if you're moving from
997 one nfs to the same, but on the server it is on two different
998 filesystems. Then nfs returns EIO instead of EXDEV.
999 Hope it will not hurt if we always in case of error try to copy/delete. */
1000 else
1001 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1003 if (errno != EXDEV)
1005 if (ctx->skip_all)
1006 return_status = FILE_SKIPALL;
1007 else
1009 return_status = files_error (_("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
1010 if (return_status == FILE_SKIPALL)
1011 ctx->skip_all = TRUE;
1012 if (return_status == FILE_RETRY)
1013 goto retry_rename;
1016 goto ret;
1018 #endif
1020 /* Failed because filesystem boundary -> copy the file instead */
1021 old_ask_overwrite = tctx->ask_overwrite;
1022 tctx->ask_overwrite = FALSE;
1023 return_status = copy_file_file (tctx, ctx, s, d);
1024 tctx->ask_overwrite = old_ask_overwrite;
1025 if (return_status != FILE_CONT)
1026 goto ret;
1028 copy_done = TRUE;
1030 file_progress_show_source (ctx, NULL);
1031 file_progress_show (ctx, 0, 0, "", FALSE);
1033 return_status = check_progress_buttons (ctx);
1034 if (return_status != FILE_CONT)
1035 goto ret;
1036 mc_refresh ();
1038 retry_src_remove:
1039 if (mc_unlink (src_vpath) != 0 && !ctx->skip_all)
1041 return_status = file_error (_("Cannot remove file \"%s\"\n%s"), s);
1042 if (return_status == FILE_RETRY)
1043 goto retry_src_remove;
1044 if (return_status == FILE_SKIPALL)
1045 ctx->skip_all = TRUE;
1046 goto ret;
1049 if (!copy_done)
1050 return_status = progress_update_one (tctx, ctx, src_stats.st_size);
1052 ret:
1053 vfs_path_free (src_vpath);
1054 vfs_path_free (dst_vpath);
1056 return return_status;
1059 /* }}} */
1061 /* --------------------------------------------------------------------------------------------- */
1062 /* {{{ Erase routines */
1063 /** Don't update progress status if progress_count==NULL */
1065 static FileProgressStatus
1066 erase_file (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * vpath)
1068 struct stat buf;
1070 file_progress_show_deleting (ctx, vfs_path_as_str (vpath), &tctx->progress_count);
1071 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1072 if (check_progress_buttons (ctx) == FILE_ABORT)
1073 return FILE_ABORT;
1075 mc_refresh ();
1077 if (tctx->progress_count != 0 && mc_lstat (vpath, &buf) != 0)
1079 /* ignore, most likely the mc_unlink fails, too */
1080 buf.st_size = 0;
1083 while (mc_unlink (vpath) != 0 && !ctx->skip_all)
1085 int return_status;
1087 return_status = file_error (_("Cannot delete file \"%s\"\n%s"), vfs_path_as_str (vpath));
1088 if (return_status == FILE_ABORT)
1089 return return_status;
1090 if (return_status == FILE_RETRY)
1091 continue;
1092 if (return_status == FILE_SKIPALL)
1093 ctx->skip_all = TRUE;
1094 break;
1097 if (tctx->progress_count == 0)
1098 return FILE_CONT;
1100 return check_progress_buttons (ctx);
1103 /* --------------------------------------------------------------------------------------------- */
1106 Recursive remove of files
1107 abort->cancel stack
1108 skip ->warn every level, gets default
1109 skipall->remove as much as possible
1111 static FileProgressStatus
1112 recursive_erase (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * vpath)
1114 struct dirent *next;
1115 DIR *reading;
1116 const char *s;
1117 FileProgressStatus return_status = FILE_CONT;
1119 reading = mc_opendir (vpath);
1120 if (reading == NULL)
1121 return FILE_RETRY;
1123 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1125 vfs_path_t *tmp_vpath;
1126 struct stat buf;
1128 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
1129 continue;
1131 tmp_vpath = vfs_path_append_new (vpath, next->d_name, (char *) NULL);
1132 if (mc_lstat (tmp_vpath, &buf) != 0)
1134 mc_closedir (reading);
1135 vfs_path_free (tmp_vpath);
1136 return FILE_RETRY;
1138 if (S_ISDIR (buf.st_mode))
1139 return_status = recursive_erase (tctx, ctx, tmp_vpath);
1140 else
1141 return_status = erase_file (tctx, ctx, tmp_vpath);
1142 vfs_path_free (tmp_vpath);
1144 mc_closedir (reading);
1146 if (return_status == FILE_ABORT)
1147 return FILE_ABORT;
1149 s = vfs_path_as_str (vpath);
1151 file_progress_show_deleting (ctx, s, NULL);
1152 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1153 if (check_progress_buttons (ctx) == FILE_ABORT)
1154 return FILE_ABORT;
1156 mc_refresh ();
1158 while (my_rmdir (s) != 0 && !ctx->skip_all)
1160 return_status = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1161 if (return_status == FILE_RETRY)
1162 continue;
1163 if (return_status == FILE_ABORT)
1164 break;
1165 if (return_status == FILE_SKIPALL)
1166 ctx->skip_all = TRUE;
1167 break;
1170 return return_status;
1173 /* --------------------------------------------------------------------------------------------- */
1174 /** Return -1 on error, 1 if there are no entries besides "." and ".."
1175 in the directory path points to, 0 else. */
1177 static int
1178 check_dir_is_empty (const vfs_path_t * vpath)
1180 DIR *dir;
1181 struct dirent *d;
1182 int i = 1;
1184 dir = mc_opendir (vpath);
1185 if (dir == NULL)
1186 return -1;
1188 for (d = mc_readdir (dir); d != NULL; d = mc_readdir (dir))
1189 if (!DIR_IS_DOT (d->d_name) && !DIR_IS_DOTDOT (d->d_name))
1191 i = 0;
1192 break;
1195 mc_closedir (dir);
1196 return i;
1199 /* --------------------------------------------------------------------------------------------- */
1201 static FileProgressStatus
1202 erase_dir_iff_empty (file_op_context_t * ctx, const vfs_path_t * vpath, size_t count)
1204 FileProgressStatus error = FILE_CONT;
1205 const char *s;
1207 s = vfs_path_as_str (vpath);
1209 file_progress_show_deleting (ctx, s, NULL);
1210 file_progress_show_count (ctx, count, ctx->progress_count);
1211 if (check_progress_buttons (ctx) == FILE_ABORT)
1212 return FILE_ABORT;
1214 mc_refresh ();
1216 if (check_dir_is_empty (vpath) == 1) /* not empty or error */
1218 while (my_rmdir (s) != 0 && !ctx->skip_all)
1220 error = file_error (_("Cannot remove directory \"%s\"\n%s"), s);
1221 if (error == FILE_SKIPALL)
1222 ctx->skip_all = TRUE;
1223 if (error != FILE_RETRY)
1224 break;
1228 return error;
1231 /* }}} */
1233 /* --------------------------------------------------------------------------------------------- */
1234 /* {{{ Panel operate routines */
1237 * Return currently selected entry name or the name of the first marked
1238 * entry if there is one.
1241 static const char *
1242 panel_get_file (WPanel * panel)
1244 if (get_current_type () == view_tree)
1246 WTree *tree;
1247 const vfs_path_t *selected_name;
1249 tree = (WTree *) get_panel_widget (get_current_index ());
1250 selected_name = tree_selected_name (tree);
1251 return vfs_path_as_str (selected_name);
1254 if (panel->marked != 0)
1256 int i;
1258 for (i = 0; i < panel->dir.len; i++)
1259 if (panel->dir.list[i].f.marked)
1260 return panel->dir.list[i].fname;
1263 return panel->dir.list[panel->selected].fname;
1266 /* --------------------------------------------------------------------------------------------- */
1268 * panel_compute_totals:
1270 * compute the number of files and the number of bytes
1271 * used up by the whole selection, recursing directories
1272 * as required. In addition, it checks to see if it will
1273 * overwrite any files by doing the copy.
1276 static FileProgressStatus
1277 panel_compute_totals (const WPanel * panel, dirsize_status_msg_t * sm, size_t * ret_count,
1278 uintmax_t * ret_total, gboolean compute_symlinks)
1280 int i;
1281 size_t dir_count = 0;
1283 for (i = 0; i < panel->dir.len; i++)
1285 struct stat *s;
1287 if (!panel->dir.list[i].f.marked)
1288 continue;
1290 s = &panel->dir.list[i].st;
1292 if (S_ISDIR (s->st_mode))
1294 vfs_path_t *p;
1295 FileProgressStatus status;
1297 p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, (char *) NULL);
1298 status = compute_dir_size (p, sm, &dir_count, ret_count, ret_total, compute_symlinks);
1299 vfs_path_free (p);
1301 if (status != FILE_CONT)
1302 return status;
1304 else
1306 (*ret_count)++;
1307 *ret_total += (uintmax_t) s->st_size;
1311 return FILE_CONT;
1314 /* --------------------------------------------------------------------------------------------- */
1316 /** Initialize variables for progress bars */
1317 static FileProgressStatus
1318 panel_operate_init_totals (const WPanel * panel, const char *source, file_op_context_t * ctx,
1319 filegui_dialog_type_t dialog_type)
1321 FileProgressStatus status;
1323 #ifdef ENABLE_BACKGROUND
1324 if (mc_global.we_are_background)
1325 return FILE_CONT;
1326 #endif
1328 if (verbose && file_op_compute_totals)
1330 dirsize_status_msg_t dsm;
1332 memset (&dsm, 0, sizeof (dsm));
1333 dsm.allow_skip = TRUE;
1334 status_msg_init (STATUS_MSG (&dsm), _("Directory scanning"), 0, dirsize_status_init_cb,
1335 dirsize_status_update_cb, dirsize_status_deinit_cb);
1337 ctx->progress_count = 0;
1338 ctx->progress_bytes = 0;
1340 if (source == NULL)
1341 status = panel_compute_totals (panel, &dsm, &ctx->progress_count, &ctx->progress_bytes,
1342 ctx->follow_links);
1343 else
1345 vfs_path_t *p;
1346 size_t dir_count = 0;
1348 p = vfs_path_from_str (source);
1349 status = compute_dir_size (p, &dsm, &dir_count, &ctx->progress_count,
1350 &ctx->progress_bytes, ctx->follow_links);
1351 vfs_path_free (p);
1354 status_msg_deinit (STATUS_MSG (&dsm));
1356 ctx->progress_totals_computed = (status == FILE_CONT);
1358 if (status == FILE_SKIP)
1359 status = FILE_CONT;
1361 else
1363 status = FILE_CONT;
1364 ctx->progress_count = panel->marked;
1365 ctx->progress_bytes = panel->total;
1366 ctx->progress_totals_computed = FALSE;
1369 file_op_context_create_ui (ctx, TRUE, dialog_type);
1371 return status;
1374 /* --------------------------------------------------------------------------------------------- */
1376 * Generate user prompt for panel operation.
1377 * src_stat must be not NULL for single source, and NULL for multiple sources
1380 static char *
1381 panel_operate_generate_prompt (const WPanel * panel, FileOperation operation,
1382 const struct stat *src_stat)
1384 char *sp;
1385 char *format_string;
1386 const char *cp;
1388 static gboolean i18n_flag = FALSE;
1389 if (!i18n_flag)
1391 size_t i;
1393 for (i = G_N_ELEMENTS (op_names1); i-- != 0;)
1394 op_names1[i] = Q_ (op_names1[i]);
1396 #ifdef ENABLE_NLS
1397 for (i = G_N_ELEMENTS (prompt_parts); i-- != 0;)
1398 prompt_parts[i] = _(prompt_parts[i]);
1400 one_format = _(one_format);
1401 many_format = _(many_format);
1402 #endif /* ENABLE_NLS */
1403 i18n_flag = TRUE;
1406 /* Possible prompts:
1407 * OP_COPY:
1408 * "Copy file \"%s\" with source mask:"
1409 * "Copy %d files with source mask:"
1410 * "Copy directory \"%s\" with source mask:"
1411 * "Copy %d directories with source mask:"
1412 * "Copy %d files/directories with source mask:"
1413 * OP_MOVE:
1414 * "Move file \"%s\" with source mask:"
1415 * "Move %d files with source mask:"
1416 * "Move directory \"%s\" with source mask:"
1417 * "Move %d directories with source mask:"
1418 * "Move %d files/directories with source mask:"
1419 * OP_DELETE:
1420 * "Delete file \"%s\"?"
1421 * "Delete %d files?"
1422 * "Delete directory \"%s\"?"
1423 * "Delete %d directories?"
1424 * "Delete %d files/directories?"
1427 cp = (src_stat != NULL ? one_format : many_format);
1429 /* 1. Substitute %o */
1430 format_string = str_replace_all (cp, "%o", op_names1[(int) operation]);
1432 /* 2. Substitute %n */
1433 cp = operation == OP_DELETE ? "\n" : " ";
1434 sp = format_string;
1435 format_string = str_replace_all (sp, "%n", cp);
1436 g_free (sp);
1438 /* 3. Substitute %f */
1439 if (src_stat != NULL)
1440 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1441 else if (panel->marked == panel->dirs_marked)
1442 cp = prompt_parts[3];
1443 else
1444 cp = panel->dirs_marked != 0 ? prompt_parts[4] : prompt_parts[1];
1446 sp = format_string;
1447 format_string = str_replace_all (sp, "%f", cp);
1448 g_free (sp);
1450 /* 4. Substitute %m */
1451 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1452 sp = format_string;
1453 format_string = str_replace_all (sp, "%m", cp);
1454 g_free (sp);
1456 return format_string;
1459 /* --------------------------------------------------------------------------------------------- */
1461 #ifdef ENABLE_BACKGROUND
1462 static int
1463 end_bg_process (file_op_context_t * ctx, enum OperationMode mode)
1465 int pid = ctx->pid;
1467 (void) mode;
1468 ctx->pid = 0;
1470 unregister_task_with_pid (pid);
1471 /* file_op_context_destroy(ctx); */
1472 return 1;
1474 #endif
1475 /* }}} */
1477 /* --------------------------------------------------------------------------------------------- */
1478 /*** public functions ****************************************************************************/
1479 /* --------------------------------------------------------------------------------------------- */
1481 FileProgressStatus
1482 copy_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx,
1483 const char *src_path, const char *dst_path)
1485 uid_t src_uid = (uid_t) (-1);
1486 gid_t src_gid = (gid_t) (-1);
1488 int src_desc, dest_desc = -1;
1489 mode_t src_mode = 0; /* The mode of the source file */
1490 struct stat src_stat, dst_stat;
1491 struct utimbuf utb;
1492 gboolean dst_exists = FALSE, appending = FALSE;
1493 off_t file_size = -1;
1494 FileProgressStatus return_status, temp_status;
1495 struct timeval tv_transfer_start;
1496 dest_status_t dst_status = DEST_NONE;
1497 int open_flags;
1498 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
1499 char *buf = NULL;
1501 /* FIXME: We should not be using global variables! */
1502 ctx->do_reget = 0;
1503 return_status = FILE_RETRY;
1505 dst_vpath = vfs_path_from_str (dst_path);
1506 src_vpath = vfs_path_from_str (src_path);
1508 file_progress_show_source (ctx, src_vpath);
1509 file_progress_show_target (ctx, dst_vpath);
1511 if (check_progress_buttons (ctx) == FILE_ABORT)
1513 return_status = FILE_ABORT;
1514 goto ret_fast;
1517 mc_refresh ();
1519 while (mc_stat (dst_vpath, &dst_stat) == 0)
1521 if (S_ISDIR (dst_stat.st_mode))
1523 if (ctx->skip_all)
1524 return_status = FILE_SKIPALL;
1525 else
1527 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), dst_path);
1528 if (return_status == FILE_SKIPALL)
1529 ctx->skip_all = TRUE;
1530 if (return_status == FILE_RETRY)
1531 continue;
1533 goto ret_fast;
1536 dst_exists = TRUE;
1537 break;
1540 while ((*ctx->stat_func) (src_vpath, &src_stat) != 0)
1542 if (ctx->skip_all)
1543 return_status = FILE_SKIPALL;
1544 else
1546 return_status = file_error (_("Cannot stat source file \"%s\"\n%s"), src_path);
1547 if (return_status == FILE_SKIPALL)
1548 ctx->skip_all = TRUE;
1551 if (return_status != FILE_RETRY)
1552 goto ret_fast;
1555 if (dst_exists)
1557 /* Destination already exists */
1558 if (src_stat.st_dev == dst_stat.st_dev && src_stat.st_ino == dst_stat.st_ino)
1560 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same file"),
1561 src_path, dst_path);
1562 goto ret_fast;
1565 /* Should we replace destination? */
1566 if (tctx->ask_overwrite)
1568 ctx->do_reget = 0;
1569 return_status = query_replace (ctx, dst_path, &src_stat, &dst_stat);
1570 if (return_status != FILE_CONT)
1571 goto ret_fast;
1575 if (!ctx->do_append)
1577 /* Check the hardlinks */
1578 if (!ctx->follow_links && src_stat.st_nlink > 1
1579 && check_hardlinks (src_vpath, dst_vpath, &src_stat))
1581 /* We have made a hardlink - no more processing is necessary */
1582 return_status = FILE_CONT;
1583 goto ret_fast;
1586 if (S_ISLNK (src_stat.st_mode))
1588 return_status = make_symlink (ctx, src_path, dst_path);
1589 goto ret_fast;
1592 if (S_ISCHR (src_stat.st_mode) || S_ISBLK (src_stat.st_mode) || S_ISFIFO (src_stat.st_mode)
1593 || S_ISNAM (src_stat.st_mode) || S_ISSOCK (src_stat.st_mode))
1595 while (mc_mknod (dst_vpath, src_stat.st_mode & ctx->umask_kill, src_stat.st_rdev) < 0
1596 && !ctx->skip_all)
1598 return_status = file_error (_("Cannot create special file \"%s\"\n%s"), dst_path);
1599 if (return_status == FILE_RETRY)
1600 continue;
1601 if (return_status == FILE_SKIPALL)
1602 ctx->skip_all = TRUE;
1603 goto ret_fast;
1605 /* Success */
1607 while (ctx->preserve_uidgid
1608 && mc_chown (dst_vpath, src_stat.st_uid, src_stat.st_gid) != 0 && !ctx->skip_all)
1610 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1611 if (temp_status == FILE_SKIP)
1612 break;
1613 if (temp_status == FILE_SKIPALL)
1614 ctx->skip_all = TRUE;
1615 if (temp_status != FILE_RETRY)
1617 return_status = temp_status;
1618 goto ret_fast;
1622 while (ctx->preserve && mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill) != 0
1623 && !ctx->skip_all)
1625 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1626 if (temp_status == FILE_SKIP)
1627 break;
1628 if (temp_status == FILE_SKIPALL)
1629 ctx->skip_all = TRUE;
1630 if (temp_status != FILE_RETRY)
1632 return_status = temp_status;
1633 goto ret_fast;
1637 return_status = FILE_CONT;
1638 goto ret_fast;
1642 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1644 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
1646 return_status = file_error (_("Cannot open source file \"%s\"\n%s"), src_path);
1647 if (return_status == FILE_RETRY)
1648 continue;
1649 if (return_status == FILE_SKIPALL)
1650 ctx->skip_all = TRUE;
1651 if (return_status == FILE_SKIP)
1652 break;
1653 ctx->do_append = FALSE;
1654 goto ret_fast;
1657 if (ctx->do_reget != 0)
1659 if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
1661 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
1662 ctx->do_reget = 0;
1663 ctx->do_append = FALSE;
1667 while (mc_fstat (src_desc, &src_stat) != 0)
1669 if (ctx->skip_all)
1670 return_status = FILE_SKIPALL;
1671 else
1673 return_status = file_error (_("Cannot fstat source file \"%s\"\n%s"), src_path);
1674 if (return_status == FILE_RETRY)
1675 continue;
1676 if (return_status == FILE_SKIPALL)
1677 ctx->skip_all = TRUE;
1678 ctx->do_append = FALSE;
1680 goto ret;
1683 src_mode = src_stat.st_mode;
1684 src_uid = src_stat.st_uid;
1685 src_gid = src_stat.st_gid;
1686 utb.actime = src_stat.st_atime;
1687 utb.modtime = src_stat.st_mtime;
1688 file_size = src_stat.st_size;
1690 open_flags = O_WRONLY;
1691 if (dst_exists)
1693 if (ctx->do_append)
1694 open_flags |= O_APPEND;
1695 else
1696 open_flags |= O_CREAT | O_TRUNC;
1698 else
1700 open_flags |= O_CREAT | O_EXCL;
1703 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
1705 if (errno != EEXIST)
1707 if (ctx->skip_all)
1708 return_status = FILE_SKIPALL;
1709 else
1711 return_status = file_error (_("Cannot create target file \"%s\"\n%s"), dst_path);
1712 if (return_status == FILE_RETRY)
1713 continue;
1714 if (return_status == FILE_SKIPALL)
1715 ctx->skip_all = TRUE;
1716 ctx->do_append = FALSE;
1719 goto ret;
1721 dst_status = DEST_SHORT; /* file opened, but not fully copied */
1723 appending = ctx->do_append;
1724 ctx->do_append = FALSE;
1726 /* Find out the optimal buffer size. */
1727 while (mc_fstat (dest_desc, &dst_stat) != 0)
1729 if (ctx->skip_all)
1730 return_status = FILE_SKIPALL;
1731 else
1733 return_status = file_error (_("Cannot fstat target file \"%s\"\n%s"), dst_path);
1734 if (return_status == FILE_RETRY)
1735 continue;
1736 if (return_status == FILE_SKIPALL)
1737 ctx->skip_all = TRUE;
1739 goto ret;
1742 /* try preallocate space; if fail, try copy anyway */
1743 while (vfs_preallocate (dest_desc, file_size, appending ? dst_stat.st_size : 0) != 0)
1745 if (ctx->skip_all)
1747 /* cannot allocate, start the file copying anyway */
1748 return_status = FILE_CONT;
1749 break;
1752 return_status =
1753 file_error (_("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
1755 if (return_status == FILE_SKIPALL)
1756 ctx->skip_all = TRUE;
1758 if (ctx->skip_all || return_status == FILE_SKIP)
1760 /* skip the space allocation error, start file copying */
1761 return_status = FILE_CONT;
1762 break;
1765 if (return_status == FILE_ABORT)
1767 mc_close (dest_desc);
1768 dest_desc = -1;
1769 mc_unlink (dst_vpath);
1770 dst_status = DEST_NONE;
1771 goto ret;
1774 /* return_status == FILE_RETRY -- try allocate space again */
1777 ctx->eta_secs = 0.0;
1778 ctx->bps = 0;
1780 if (tctx->bps == 0 || (file_size / (tctx->bps)) > FILEOP_UPDATE_INTERVAL)
1781 file_progress_show (ctx, 0, file_size, "", TRUE);
1782 else
1783 file_progress_show (ctx, 1, 1, "", TRUE);
1784 return_status = check_progress_buttons (ctx);
1785 mc_refresh ();
1787 if (return_status == FILE_CONT)
1789 size_t bufsize;
1790 off_t n_read_total = 0;
1791 struct timeval tv_current, tv_last_update, tv_last_input;
1792 int secs, update_secs;
1793 const char *stalled_msg = "";
1794 gboolean is_first_time = TRUE;
1796 tv_last_update = tv_transfer_start;
1798 bufsize = io_blksize (dst_stat);
1799 buf = g_malloc (bufsize);
1801 while (TRUE)
1803 ssize_t n_read = -1, n_written;
1805 /* src_read */
1806 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0) == 0)
1807 while ((n_read = mc_read (src_desc, buf, bufsize)) < 0 && !ctx->skip_all)
1809 return_status = file_error (_("Cannot read source file \"%s\"\n%s"), src_path);
1810 if (return_status == FILE_RETRY)
1811 continue;
1812 if (return_status == FILE_SKIPALL)
1813 ctx->skip_all = TRUE;
1814 goto ret;
1817 if (n_read == 0)
1818 break;
1820 gettimeofday (&tv_current, NULL);
1822 if (n_read > 0)
1824 char *t = buf;
1826 n_read_total += n_read;
1828 /* Windows NT ftp servers report that files have no
1829 * permissions: -------, so if we happen to have actually
1830 * read something, we should fix the permissions.
1832 if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
1833 src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1834 gettimeofday (&tv_last_input, NULL);
1836 /* dst_write */
1837 while ((n_written = mc_write (dest_desc, t, (size_t) n_read)) < n_read)
1839 gboolean write_errno_nospace;
1841 if (n_written > 0)
1843 n_read -= n_written;
1844 t += n_written;
1845 continue;
1848 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
1850 if (ctx->skip_all)
1851 return_status = FILE_SKIPALL;
1852 else
1853 return_status =
1854 file_error (_("Cannot write target file \"%s\"\n%s"), dst_path);
1856 if (return_status == FILE_SKIP)
1858 if (write_errno_nospace)
1859 goto ret;
1860 break;
1862 if (return_status == FILE_SKIPALL)
1864 ctx->skip_all = TRUE;
1865 if (write_errno_nospace)
1866 goto ret;
1868 if (return_status != FILE_RETRY)
1869 goto ret;
1873 tctx->copied_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget;
1875 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1876 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1878 if (is_first_time || secs > FILEOP_UPDATE_INTERVAL)
1880 copy_file_file_display_progress (tctx, ctx,
1881 tv_current,
1882 tv_transfer_start, file_size, n_read_total);
1883 tv_last_update = tv_current;
1885 is_first_time = FALSE;
1887 if (update_secs > FILEOP_STALLING_INTERVAL)
1889 stalled_msg = _("(stalled)");
1893 gboolean force_update;
1895 force_update =
1896 (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL;
1898 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
1900 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
1901 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
1904 file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg,
1905 force_update);
1907 mc_refresh ();
1909 return_status = check_progress_buttons (ctx);
1911 if (return_status != FILE_CONT)
1913 mc_refresh ();
1914 goto ret;
1918 dst_status = DEST_FULL; /* copy successful, don't remove target file */
1921 ret:
1922 g_free (buf);
1924 rotate_dash (FALSE);
1925 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
1927 temp_status = file_error (_("Cannot close source file \"%s\"\n%s"), src_path);
1928 if (temp_status == FILE_RETRY)
1929 continue;
1930 if (temp_status == FILE_ABORT)
1931 return_status = temp_status;
1932 if (temp_status == FILE_SKIPALL)
1933 ctx->skip_all = TRUE;
1934 break;
1937 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
1939 temp_status = file_error (_("Cannot close target file \"%s\"\n%s"), dst_path);
1940 if (temp_status == FILE_RETRY)
1941 continue;
1942 if (temp_status == FILE_SKIPALL)
1943 ctx->skip_all = TRUE;
1944 return_status = temp_status;
1945 break;
1948 if (dst_status == DEST_SHORT)
1950 /* Query to remove short file */
1951 if (query_dialog (Q_ ("DialogTitle|Copy"), _("Incomplete file was retrieved. Keep it?"),
1952 D_ERROR, 2, _("&Delete"), _("&Keep")) == 0)
1953 mc_unlink (dst_vpath);
1955 else if (dst_status == DEST_FULL)
1957 /* Copy has succeeded */
1958 if (!appending && ctx->preserve_uidgid)
1960 while (mc_chown (dst_vpath, src_uid, src_gid) != 0 && !ctx->skip_all)
1962 temp_status = file_error (_("Cannot chown target file \"%s\"\n%s"), dst_path);
1963 if (temp_status == FILE_RETRY)
1964 continue;
1965 if (temp_status == FILE_SKIPALL)
1967 ctx->skip_all = TRUE;
1968 return_status = FILE_CONT;
1970 if (temp_status == FILE_SKIP)
1971 return_status = FILE_CONT;
1972 break;
1976 if (!appending)
1978 if (ctx->preserve)
1980 while (mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0 && !ctx->skip_all)
1982 temp_status = file_error (_("Cannot chmod target file \"%s\"\n%s"), dst_path);
1983 if (temp_status == FILE_RETRY)
1984 continue;
1985 if (temp_status == FILE_SKIPALL)
1987 ctx->skip_all = TRUE;
1988 return_status = FILE_CONT;
1990 if (temp_status == FILE_SKIP)
1991 return_status = FILE_CONT;
1992 break;
1995 else if (!dst_exists)
1997 src_mode = umask (-1);
1998 umask (src_mode);
1999 src_mode = 0100666 & ~src_mode;
2000 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
2002 mc_utime (dst_vpath, &utb);
2006 if (return_status == FILE_CONT)
2007 return_status = progress_update_one (tctx, ctx, file_size);
2009 ret_fast:
2010 vfs_path_free (src_vpath);
2011 vfs_path_free (dst_vpath);
2012 return return_status;
2015 /* --------------------------------------------------------------------------------------------- */
2017 * I think these copy_*_* functions should have a return type.
2018 * anyway, this function *must* have two directories as arguments.
2020 /* FIXME: This function needs to check the return values of the
2021 function calls */
2023 FileProgressStatus
2024 copy_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d,
2025 gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs)
2027 struct dirent *next;
2028 struct stat buf, cbuf;
2029 DIR *reading;
2030 FileProgressStatus return_status = FILE_CONT;
2031 struct link *lp;
2032 vfs_path_t *src_vpath, *dst_vpath;
2033 gboolean do_mkdir = TRUE;
2035 src_vpath = vfs_path_from_str (s);
2036 dst_vpath = vfs_path_from_str (d);
2038 /* First get the mode of the source dir */
2040 retry_src_stat:
2041 if ((*ctx->stat_func) (src_vpath, &cbuf) != 0)
2043 if (ctx->skip_all)
2044 return_status = FILE_SKIPALL;
2045 else
2047 return_status = file_error (_("Cannot stat source directory \"%s\"\n%s"), s);
2048 if (return_status == FILE_RETRY)
2049 goto retry_src_stat;
2050 if (return_status == FILE_SKIPALL)
2051 ctx->skip_all = TRUE;
2053 goto ret_fast;
2056 if (is_in_linklist (dest_dirs, src_vpath, &cbuf))
2058 /* Don't copy a directory we created before (we don't want to copy
2059 infinitely if a directory is copied into itself) */
2060 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
2061 return_status = FILE_CONT;
2062 goto ret_fast;
2065 /* Hmm, hardlink to directory??? - Norbert */
2066 /* FIXME: In this step we should do something
2067 in case the destination already exist */
2068 /* Check the hardlinks */
2069 if (ctx->preserve && cbuf.st_nlink > 1 && check_hardlinks (src_vpath, dst_vpath, &cbuf))
2071 /* We have made a hardlink - no more processing is necessary */
2072 goto ret_fast;
2075 if (!S_ISDIR (cbuf.st_mode))
2077 if (ctx->skip_all)
2078 return_status = FILE_SKIPALL;
2079 else
2081 return_status = file_error (_("Source \"%s\" is not a directory\n%s"), s);
2082 if (return_status == FILE_RETRY)
2083 goto retry_src_stat;
2084 if (return_status == FILE_SKIPALL)
2085 ctx->skip_all = TRUE;
2087 goto ret_fast;
2090 if (is_in_linklist (parent_dirs, src_vpath, &cbuf))
2092 /* we found a cyclic symbolic link */
2093 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
2094 return_status = FILE_SKIP;
2095 goto ret_fast;
2098 lp = g_new0 (struct link, 1);
2099 lp->vfs = vfs_path_get_by_index (src_vpath, -1)->class;
2100 lp->ino = cbuf.st_ino;
2101 lp->dev = cbuf.st_dev;
2102 parent_dirs = g_slist_prepend (parent_dirs, lp);
2104 retry_dst_stat:
2105 /* Now, check if the dest dir exists, if not, create it. */
2106 if (mc_stat (dst_vpath, &buf) != 0)
2108 /* Here the dir doesn't exist : make it ! */
2109 if (move_over && mc_rename (src_vpath, dst_vpath) == 0)
2111 return_status = FILE_CONT;
2112 goto ret;
2115 else
2118 * If the destination directory exists, we want to copy the whole
2119 * directory, but we only want this to happen once.
2121 * Escape sequences added to the * to compiler warnings.
2122 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
2123 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
2125 if (!S_ISDIR (buf.st_mode))
2127 if (ctx->skip_all)
2128 return_status = FILE_SKIPALL;
2129 else
2131 return_status = file_error (_("Destination \"%s\" must be a directory\n%s"), d);
2132 if (return_status == FILE_SKIPALL)
2133 ctx->skip_all = TRUE;
2134 if (return_status == FILE_RETRY)
2135 goto retry_dst_stat;
2137 goto ret;
2139 /* Dive into subdir if exists */
2140 if (toplevel && ctx->dive_into_subdirs)
2142 vfs_path_t *tmp;
2144 tmp = dst_vpath;
2145 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
2146 vfs_path_free (tmp);
2149 else
2150 do_mkdir = FALSE;
2153 d = vfs_path_as_str (dst_vpath);
2155 if (do_mkdir)
2157 while (my_mkdir (dst_vpath, (cbuf.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
2159 if (ctx->skip_all)
2160 return_status = FILE_SKIPALL;
2161 else
2163 return_status = file_error (_("Cannot create target directory \"%s\"\n%s"), d);
2164 if (return_status == FILE_SKIPALL)
2165 ctx->skip_all = TRUE;
2167 if (return_status != FILE_RETRY)
2168 goto ret;
2171 lp = g_new0 (struct link, 1);
2172 mc_stat (dst_vpath, &buf);
2173 lp->vfs = vfs_path_get_by_index (dst_vpath, -1)->class;
2174 lp->ino = buf.st_ino;
2175 lp->dev = buf.st_dev;
2176 dest_dirs = g_slist_prepend (dest_dirs, lp);
2179 if (ctx->preserve_uidgid)
2181 while (mc_chown (dst_vpath, cbuf.st_uid, cbuf.st_gid) != 0)
2183 if (ctx->skip_all)
2184 return_status = FILE_SKIPALL;
2185 else
2187 return_status = file_error (_("Cannot chown target directory \"%s\"\n%s"), d);
2188 if (return_status == FILE_SKIPALL)
2189 ctx->skip_all = TRUE;
2191 if (return_status != FILE_RETRY)
2192 goto ret;
2196 /* open the source dir for reading */
2197 reading = mc_opendir (src_vpath);
2198 if (reading == NULL)
2199 goto ret;
2201 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
2203 char *path;
2204 vfs_path_t *tmp_vpath;
2207 * Now, we don't want '.' and '..' to be created / copied at any time
2209 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
2210 continue;
2212 /* get the filename and add it to the src directory */
2213 path = mc_build_filename (s, next->d_name, (char *) NULL);
2214 tmp_vpath = vfs_path_from_str (path);
2216 (*ctx->stat_func) (tmp_vpath, &buf);
2217 if (S_ISDIR (buf.st_mode))
2219 char *mdpath;
2221 mdpath = mc_build_filename (d, next->d_name, (char *) NULL);
2223 * From here, we just intend to recursively copy subdirs, not
2224 * the double functionality of copying different when the target
2225 * dir already exists. So, we give the recursive call the flag 0
2226 * meaning no toplevel.
2228 return_status =
2229 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
2230 g_free (mdpath);
2232 else
2234 char *dest_file;
2236 dest_file = mc_build_filename (d, x_basename (path), (char *) NULL);
2237 return_status = copy_file_file (tctx, ctx, path, dest_file);
2238 g_free (dest_file);
2241 g_free (path);
2243 if (do_delete && return_status == FILE_CONT)
2245 if (ctx->erase_at_end)
2247 lp = g_new0 (struct link, 1);
2248 lp->src_vpath = tmp_vpath;
2249 lp->st_mode = buf.st_mode;
2250 erase_list = g_slist_append (erase_list, lp);
2251 tmp_vpath = NULL;
2253 else if (S_ISDIR (buf.st_mode))
2254 return_status = erase_dir_iff_empty (ctx, tmp_vpath, tctx->progress_count);
2255 else
2256 return_status = erase_file (tctx, ctx, tmp_vpath);
2258 vfs_path_free (tmp_vpath);
2260 mc_closedir (reading);
2262 if (ctx->preserve)
2264 struct utimbuf utb;
2266 mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill);
2267 utb.actime = cbuf.st_atime;
2268 utb.modtime = cbuf.st_mtime;
2269 mc_utime (dst_vpath, &utb);
2271 else
2273 cbuf.st_mode = umask (-1);
2274 umask (cbuf.st_mode);
2275 cbuf.st_mode = 0100777 & ~cbuf.st_mode;
2276 mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill);
2279 ret:
2280 free_link (parent_dirs->data);
2281 g_slist_free_1 (parent_dirs);
2282 ret_fast:
2283 vfs_path_free (src_vpath);
2284 vfs_path_free (dst_vpath);
2285 return return_status;
2288 /* }}} */
2290 /* --------------------------------------------------------------------------------------------- */
2291 /* {{{ Move routines */
2293 FileProgressStatus
2294 move_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d)
2296 struct stat sbuf, dbuf;
2297 FileProgressStatus return_status = FILE_CONT;
2298 gboolean move_over = FALSE;
2299 gboolean dstat_ok;
2300 vfs_path_t *src_vpath, *dst_vpath;
2302 src_vpath = vfs_path_from_str (s);
2303 dst_vpath = vfs_path_from_str (d);
2305 file_progress_show_source (ctx, src_vpath);
2306 file_progress_show_target (ctx, dst_vpath);
2308 if (check_progress_buttons (ctx) == FILE_ABORT)
2310 return_status = FILE_ABORT;
2311 goto ret_fast;
2314 mc_refresh ();
2316 mc_stat (src_vpath, &sbuf);
2318 dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
2319 if (dstat_ok && sbuf.st_dev == dbuf.st_dev && sbuf.st_ino == dbuf.st_ino)
2321 return_status = warn_same_file (_("\"%s\"\nand\n\"%s\"\nare the same directory"), s, d);
2322 goto ret_fast;
2325 if (!dstat_ok)
2326 ; /* destination doesn't exist */
2327 else if (!ctx->dive_into_subdirs)
2328 move_over = TRUE;
2329 else
2331 vfs_path_t *tmp;
2333 tmp = dst_vpath;
2334 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
2335 vfs_path_free (tmp);
2338 d = vfs_path_as_str (dst_vpath);
2340 /* Check if the user inputted an existing dir */
2341 retry_dst_stat:
2342 if (mc_stat (dst_vpath, &dbuf) == 0)
2344 if (move_over)
2346 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, TRUE, TRUE, NULL);
2348 if (return_status != FILE_CONT)
2349 goto ret;
2350 goto oktoret;
2352 else if (ctx->skip_all)
2353 return_status = FILE_SKIPALL;
2354 else
2356 if (S_ISDIR (dbuf.st_mode))
2357 return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), d);
2358 else
2359 return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), d);
2360 if (return_status == FILE_SKIPALL)
2361 ctx->skip_all = TRUE;
2362 if (return_status == FILE_RETRY)
2363 goto retry_dst_stat;
2366 goto ret_fast;
2369 retry_rename:
2370 if (mc_rename (src_vpath, dst_vpath) == 0)
2372 return_status = FILE_CONT;
2373 goto ret;
2376 if (errno != EXDEV)
2378 if (!ctx->skip_all)
2380 return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
2381 if (return_status == FILE_SKIPALL)
2382 ctx->skip_all = TRUE;
2383 if (return_status == FILE_RETRY)
2384 goto retry_rename;
2386 goto ret;
2388 /* Failed because of filesystem boundary -> copy dir instead */
2389 return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, FALSE, TRUE, NULL);
2391 if (return_status != FILE_CONT)
2392 goto ret;
2393 oktoret:
2394 file_progress_show_source (ctx, NULL);
2395 file_progress_show_target (ctx, NULL);
2396 file_progress_show (ctx, 0, 0, "", FALSE);
2398 return_status = check_progress_buttons (ctx);
2399 if (return_status != FILE_CONT)
2400 goto ret;
2402 mc_refresh ();
2403 if (ctx->erase_at_end)
2405 /* Reset progress count before delete to avoid counting files twice */
2406 tctx->progress_count = tctx->prev_progress_count;
2408 while (erase_list != NULL && return_status != FILE_ABORT)
2410 struct link *lp = (struct link *) erase_list->data;
2412 if (S_ISDIR (lp->st_mode))
2413 return_status = erase_dir_iff_empty (ctx, lp->src_vpath, tctx->progress_count);
2414 else
2415 return_status = erase_file (tctx, ctx, lp->src_vpath);
2417 erase_list = g_slist_remove (erase_list, lp);
2418 free_link (lp);
2421 /* Save progress counter before move next directory */
2422 tctx->prev_progress_count = tctx->progress_count;
2424 erase_dir_iff_empty (ctx, src_vpath, tctx->progress_count);
2426 ret:
2427 erase_list = free_linklist (erase_list);
2428 ret_fast:
2429 vfs_path_free (src_vpath);
2430 vfs_path_free (dst_vpath);
2431 return return_status;
2434 /* }}} */
2436 /* --------------------------------------------------------------------------------------------- */
2437 /* {{{ Erase routines */
2439 FileProgressStatus
2440 erase_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * s_vpath)
2442 FileProgressStatus error;
2444 file_progress_show_deleting (ctx, vfs_path_as_str (s_vpath), NULL);
2445 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2446 if (check_progress_buttons (ctx) == FILE_ABORT)
2447 return FILE_ABORT;
2449 mc_refresh ();
2451 /* The old way to detect a non empty directory was:
2452 error = my_rmdir (s);
2453 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
2454 For the linux user space nfs server (nfs-server-2.2beta29-2)
2455 we would have to check also for EIO. I hope the new way is
2456 fool proof. (Norbert)
2458 error = check_dir_is_empty (s_vpath);
2459 if (error == 0)
2460 { /* not empty */
2461 error = query_recursive (ctx, vfs_path_as_str (s_vpath));
2462 if (error == FILE_CONT)
2463 error = recursive_erase (tctx, ctx, s_vpath);
2464 return error;
2467 while (my_rmdir (vfs_path_as_str (s_vpath)) == -1 && !ctx->skip_all)
2469 error = file_error (_("Cannot remove directory \"%s\"\n%s"), vfs_path_as_str (s_vpath));
2470 if (error != FILE_RETRY)
2471 return error;
2474 return FILE_CONT;
2477 /* }}} */
2479 /* --------------------------------------------------------------------------------------------- */
2480 /* {{{ Panel operate routines */
2482 void
2483 dirsize_status_init_cb (status_msg_t * sm)
2485 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
2486 Widget *wd = WIDGET (sm->dlg);
2488 const char *b1_name = N_("&Abort");
2489 const char *b2_name = N_("&Skip");
2490 int b_width, ui_width;
2492 #ifdef ENABLE_NLS
2493 b1_name = _(b1_name);
2494 b2_name = _(b2_name);
2495 #endif
2497 b_width = str_term_width1 (b1_name) + 4;
2498 if (dsm->allow_skip)
2499 b_width += str_term_width1 (b2_name) + 4 + 1;
2501 ui_width = max (COLS / 2, b_width + 6);
2502 dsm->dirname = label_new (2, 3, "");
2503 add_widget (sm->dlg, dsm->dirname);
2504 dsm->count_size = label_new (3, 3, "");
2505 add_widget (sm->dlg, dsm->count_size);
2506 add_widget (sm->dlg, hline_new (4, -1, -1));
2508 dsm->abort_button = WIDGET (button_new (5, 3, FILE_ABORT, NORMAL_BUTTON, b1_name, NULL));
2509 add_widget (sm->dlg, dsm->abort_button);
2510 if (dsm->allow_skip)
2512 dsm->skip_button = WIDGET (button_new (5, 3, FILE_SKIP, NORMAL_BUTTON, b2_name, NULL));
2513 add_widget (sm->dlg, dsm->skip_button);
2514 dlg_select_widget (dsm->skip_button);
2517 widget_set_size (wd, wd->y, wd->x, 8, ui_width);
2518 dirsize_status_locate_buttons (dsm);
2521 /* --------------------------------------------------------------------------------------------- */
2524 dirsize_status_update_cb (status_msg_t * sm)
2526 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
2527 Widget *wd = WIDGET (sm->dlg);
2529 /* update second (longer label) */
2530 label_set_textv (dsm->count_size, _("Directories: %zd, total size: %s"),
2531 dsm->dir_count, size_trunc_sep (dsm->total_size, panels_options.kilobyte_si));
2533 /* enlarge dialog if required */
2534 if (WIDGET (dsm->count_size)->cols + 6 > wd->cols)
2536 dlg_set_size (sm->dlg, wd->lines, WIDGET (dsm->count_size)->cols + 6);
2537 dirsize_status_locate_buttons (dsm);
2538 dlg_redraw (sm->dlg);
2541 /* adjust first label */
2542 label_set_text (dsm->dirname, str_trunc (vfs_path_as_str (dsm->dirname_vpath), wd->cols - 6));
2544 switch (status_msg_common_update (sm))
2546 case B_CANCEL:
2547 case FILE_ABORT:
2548 return FILE_ABORT;
2549 case FILE_SKIP:
2550 return FILE_SKIP;
2551 default:
2552 return FILE_CONT;
2556 /* --------------------------------------------------------------------------------------------- */
2558 void
2559 dirsize_status_deinit_cb (status_msg_t * sm)
2561 (void) sm;
2563 /* schedule to update passive panel */
2564 if (get_other_type () == view_listing)
2565 other_panel->dirty = 1;
2568 /* --------------------------------------------------------------------------------------------- */
2570 * compute_dir_size:
2572 * Computes the number of bytes used by the files in a directory
2575 FileProgressStatus
2576 compute_dir_size (const vfs_path_t * dirname_vpath, dirsize_status_msg_t * sm,
2577 size_t * ret_dir_count, size_t * ret_marked_count, uintmax_t * ret_total,
2578 gboolean compute_symlinks)
2580 return do_compute_dir_size (dirname_vpath, sm, ret_dir_count, ret_marked_count, ret_total,
2581 compute_symlinks);
2584 /* --------------------------------------------------------------------------------------------- */
2586 * panel_operate:
2588 * Performs one of the operations on the selection on the source_panel
2589 * (copy, delete, move).
2591 * Returns TRUE if did change the directory
2592 * structure, Returns FALSE if user aborted
2594 * force_single forces operation on the current entry and affects
2595 * default destination. Current filename is used as default.
2598 gboolean
2599 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
2601 WPanel *panel = PANEL (source_panel);
2602 const gboolean single_entry = force_single || (panel->marked <= 1)
2603 || (get_current_type () == view_tree);
2605 const char *source = NULL;
2606 #ifdef WITH_FULL_PATHS
2607 vfs_path_t *source_with_vpath = NULL;
2608 #endif /* WITH_FULL_PATHS */
2609 char *dest = NULL;
2610 vfs_path_t *dest_vpath = NULL;
2611 char *temp = NULL;
2612 char *save_cwd = NULL, *save_dest = NULL;
2613 struct stat src_stat;
2614 gboolean ret_val = TRUE;
2615 int i;
2616 FileProgressStatus value;
2617 file_op_context_t *ctx;
2618 file_op_total_context_t *tctx;
2619 vfs_path_t *tmp_vpath;
2620 filegui_dialog_type_t dialog_type = FILEGUI_DIALOG_ONE_ITEM;
2622 gboolean do_bg = FALSE; /* do background operation? */
2624 static gboolean i18n_flag = FALSE;
2625 if (!i18n_flag)
2627 for (i = G_N_ELEMENTS (op_names); i-- != 0;)
2628 op_names[i] = Q_ (op_names[i]);
2629 i18n_flag = TRUE;
2632 linklist = free_linklist (linklist);
2633 dest_dirs = free_linklist (dest_dirs);
2635 if (single_entry)
2637 gboolean ok;
2639 if (force_single)
2640 source = selection (panel)->fname;
2641 else
2642 source = panel_get_file (panel);
2644 ok = !DIR_IS_DOTDOT (source);
2646 if (!ok)
2647 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
2648 else
2650 vfs_path_t *source_vpath;
2652 source_vpath = vfs_path_from_str (source);
2654 /* Update stat to get actual info */
2655 ok = mc_lstat (source_vpath, &src_stat) == 0;
2656 if (!ok)
2658 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
2659 path_trunc (source, 30), unix_error_string (errno));
2661 /* Directory was changed outside MC. Reload it forced */
2662 if (!panel->is_panelized)
2664 panel_update_flags_t flags = UP_RELOAD;
2666 /* don't update panelized panel */
2667 if (get_other_type () == view_listing && other_panel->is_panelized)
2668 flags |= UP_ONLY_CURRENT;
2670 update_panels (flags, UP_KEEPSEL);
2674 vfs_path_free (source_vpath);
2677 if (!ok)
2678 return FALSE;
2681 ctx = file_op_context_new (operation);
2683 /* Show confirmation dialog */
2684 if (operation != OP_DELETE)
2686 const char *tmp_dest_dir;
2687 char *dest_dir;
2688 char *format;
2690 /* Forced single operations default to the original name */
2691 if (force_single)
2692 tmp_dest_dir = source;
2693 else if (get_other_type () == view_listing)
2694 tmp_dest_dir = vfs_path_as_str (other_panel->cwd_vpath);
2695 else
2696 tmp_dest_dir = vfs_path_as_str (panel->cwd_vpath);
2698 * Add trailing backslash only when do non-local ops.
2699 * It saves user from occasional file renames (when destination
2700 * dir is deleted)
2702 if (!force_single && tmp_dest_dir != NULL && tmp_dest_dir[0] != '\0'
2703 && !IS_PATH_SEP (tmp_dest_dir[strlen (tmp_dest_dir) - 1]))
2705 /* add trailing separator */
2706 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
2708 else
2710 /* just copy */
2711 dest_dir = g_strdup (tmp_dest_dir);
2713 if (dest_dir == NULL)
2715 ret_val = FALSE;
2716 goto ret_fast;
2719 /* Generate confirmation prompt */
2720 format =
2721 panel_operate_generate_prompt (panel, operation, source != NULL ? &src_stat : NULL);
2723 dest =
2724 file_mask_dialog (ctx, operation, source != NULL, format,
2725 source != NULL ? source : (const void *) &panel->marked, dest_dir,
2726 &do_bg);
2728 g_free (format);
2729 g_free (dest_dir);
2731 if (dest == NULL || dest[0] == '\0')
2733 g_free (dest);
2734 ret_val = FALSE;
2735 goto ret_fast;
2737 dest_vpath = vfs_path_from_str (dest);
2739 else if (confirm_delete)
2741 char *format;
2742 char fmd_buf[BUF_MEDIUM];
2744 /* Generate confirmation prompt */
2745 format =
2746 panel_operate_generate_prompt (panel, OP_DELETE, source != NULL ? &src_stat : NULL);
2748 if (source == NULL)
2749 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2750 else
2752 const int fmd_xlen = 64;
2753 i = fmd_xlen - str_term_width1 (format) - 4;
2754 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2757 g_free (format);
2759 if (safe_delete)
2760 query_set_sel (1);
2762 i = query_dialog (op_names[operation], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2764 if (i != 0)
2766 ret_val = FALSE;
2767 goto ret_fast;
2771 tctx = file_op_total_context_new ();
2772 gettimeofday (&tctx->transfer_start, (struct timezone *) NULL);
2774 #ifdef ENABLE_BACKGROUND
2775 /* Did the user select to do a background operation? */
2776 if (do_bg)
2778 int v;
2780 v = do_background (ctx,
2781 g_strconcat (op_names[operation], ": ",
2782 vfs_path_as_str (panel->cwd_vpath), (char *) NULL));
2783 if (v == -1)
2784 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
2786 /* If we are the parent */
2787 if (v == 1)
2789 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
2791 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
2792 vfs_path_free (dest_vpath);
2793 g_free (dest);
2794 /* file_op_context_destroy (ctx); */
2795 return FALSE;
2798 else
2799 #endif /* ENABLE_BACKGROUND */
2801 if (operation == OP_DELETE)
2802 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
2803 else if (single_entry && S_ISDIR (selection (panel)->st.st_mode))
2804 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2805 else if (single_entry || force_single)
2806 dialog_type = FILEGUI_DIALOG_ONE_ITEM;
2807 else
2808 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
2811 /* Initialize things */
2812 /* We do not want to trash cache every time file is
2813 created/touched. However, this will make our cache contain
2814 invalid data. */
2815 if ((dest != NULL)
2816 && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
2817 save_dest = g_strdup (dest);
2819 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
2820 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
2821 save_cwd = g_strdup (vfs_path_as_str (panel->cwd_vpath));
2823 /* Now, let's do the job */
2825 /* This code is only called by the tree and panel code */
2826 if (single_entry)
2828 /* We now have ETA in all cases */
2830 /* One file: FIXME mc_chdir will take user out of any vfs */
2831 if ((operation != OP_COPY) && (get_current_type () == view_tree))
2833 vfs_path_t *vpath;
2834 int chdir_retcode;
2836 vpath = vfs_path_from_str (PATH_SEP_STR);
2837 chdir_retcode = mc_chdir (vpath);
2838 vfs_path_free (vpath);
2839 if (chdir_retcode < 0)
2841 ret_val = FALSE;
2842 goto clean_up;
2846 /* The source and src_stat variables have been initialized before */
2847 #ifdef WITH_FULL_PATHS
2848 if (g_path_is_absolute (source))
2849 source_with_vpath = vfs_path_from_str (source);
2850 else
2851 source_with_vpath = vfs_path_append_new (panel->cwd_vpath, source, (char *) NULL);
2852 #endif /* WITH_FULL_PATHS */
2853 if (panel_operate_init_totals (panel, vfs_path_as_str (source_with_vpath), ctx, dialog_type)
2854 == FILE_CONT)
2856 if (operation == OP_DELETE)
2858 if (S_ISDIR (src_stat.st_mode))
2859 value = erase_dir (tctx, ctx, source_with_vpath);
2860 else
2861 value = erase_file (tctx, ctx, source_with_vpath);
2863 else
2865 temp = transform_source (ctx, source_with_vpath);
2866 if (temp == NULL)
2867 value = transform_error;
2868 else
2870 char *repl_dest, *temp2;
2872 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2873 if (ctx->search_handle->error != MC_SEARCH_E_OK)
2875 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
2876 g_free (repl_dest);
2877 goto clean_up;
2880 temp2 = mc_build_filename (repl_dest, temp, (char *) NULL);
2881 g_free (temp);
2882 g_free (repl_dest);
2883 g_free (dest);
2884 vfs_path_free (dest_vpath);
2885 dest = temp2;
2886 dest_vpath = vfs_path_from_str (dest);
2888 switch (operation)
2890 case OP_COPY:
2891 /* we use file_mask_op_follow_links only with OP_COPY */
2892 ctx->stat_func (source_with_vpath, &src_stat);
2894 if (S_ISDIR (src_stat.st_mode))
2895 value =
2896 copy_dir_dir (tctx, ctx, vfs_path_as_str (source_with_vpath),
2897 dest, TRUE, FALSE, FALSE, NULL);
2898 else
2899 value =
2900 copy_file_file (tctx, ctx, vfs_path_as_str (source_with_vpath),
2901 dest);
2902 break;
2904 case OP_MOVE:
2905 if (S_ISDIR (src_stat.st_mode))
2906 value =
2907 move_dir_dir (tctx, ctx, vfs_path_as_str (source_with_vpath), dest);
2908 else
2909 value =
2910 move_file_file (tctx, ctx, vfs_path_as_str (source_with_vpath),
2911 dest);
2912 break;
2914 default:
2915 /* Unknown file operation */
2916 abort ();
2919 } /* Copy or move operation */
2921 if ((value == FILE_CONT) && !force_single)
2922 unmark_files (panel);
2925 else
2927 /* Many files */
2929 /* Check destination for copy or move operation */
2930 while (operation != OP_DELETE)
2932 int dst_result;
2933 struct stat dst_stat;
2935 dst_result = mc_stat (dest_vpath, &dst_stat);
2937 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
2938 break;
2940 if (ctx->skip_all
2941 || file_error (_("Destination \"%s\" must be a directory\n%s"), dest) != FILE_RETRY)
2942 goto clean_up;
2945 if (panel_operate_init_totals (panel, NULL, ctx, dialog_type) == FILE_CONT)
2947 /* Loop for every file, perform the actual copy operation */
2948 for (i = 0; i < panel->dir.len; i++)
2950 const char *source2;
2952 if (!panel->dir.list[i].f.marked)
2953 continue; /* Skip the unmarked ones */
2955 source2 = panel->dir.list[i].fname;
2956 src_stat = panel->dir.list[i].st;
2958 #ifdef WITH_FULL_PATHS
2959 vfs_path_free (source_with_vpath);
2960 if (g_path_is_absolute (source2))
2961 source_with_vpath = vfs_path_from_str (source2);
2962 else
2963 source_with_vpath =
2964 vfs_path_append_new (panel->cwd_vpath, source2, (char *) NULL);
2965 #endif /* WITH_FULL_PATHS */
2967 if (operation == OP_DELETE)
2969 if (S_ISDIR (src_stat.st_mode))
2970 value = erase_dir (tctx, ctx, source_with_vpath);
2971 else
2972 value = erase_file (tctx, ctx, source_with_vpath);
2974 else
2976 temp = transform_source (ctx, source_with_vpath);
2977 if (temp == NULL)
2978 value = transform_error;
2979 else
2981 char *temp2, *repl_dest, *source_with_path_str;
2983 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
2984 if (ctx->search_handle->error != MC_SEARCH_E_OK)
2986 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
2987 g_free (repl_dest);
2988 goto clean_up;
2991 temp2 = mc_build_filename (repl_dest, temp, (char *) NULL);
2992 g_free (temp);
2993 g_free (repl_dest);
2994 source_with_path_str =
2995 strutils_shell_unescape (vfs_path_as_str (source_with_vpath));
2996 temp = strutils_shell_unescape (temp2);
2997 g_free (temp2);
2999 switch (operation)
3001 case OP_COPY:
3002 /* we use file_mask_op_follow_links only with OP_COPY */
3004 vfs_path_t *vpath;
3006 vpath = vfs_path_from_str (source_with_path_str);
3007 ctx->stat_func (vpath, &src_stat);
3008 vfs_path_free (vpath);
3010 if (S_ISDIR (src_stat.st_mode))
3011 value = copy_dir_dir (tctx, ctx, source_with_path_str, temp,
3012 TRUE, FALSE, FALSE, NULL);
3013 else
3014 value = copy_file_file (tctx, ctx, source_with_path_str, temp);
3015 dest_dirs = free_linklist (dest_dirs);
3016 break;
3018 case OP_MOVE:
3019 if (S_ISDIR (src_stat.st_mode))
3020 value = move_dir_dir (tctx, ctx, source_with_path_str, temp);
3021 else
3022 value = move_file_file (tctx, ctx, source_with_path_str, temp);
3023 break;
3025 default:
3026 /* Unknown file operation */
3027 abort ();
3030 g_free (source_with_path_str);
3031 g_free (temp);
3033 } /* Copy or move operation */
3035 if (value == FILE_ABORT)
3036 break;
3038 if (value == FILE_CONT)
3039 do_file_mark (panel, i, 0);
3041 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3043 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3044 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3047 if (operation != OP_DELETE)
3048 file_progress_show (ctx, 0, 0, "", FALSE);
3050 if (check_progress_buttons (ctx) == FILE_ABORT)
3051 break;
3053 mc_refresh ();
3054 } /* Loop for every file */
3056 } /* Many entries */
3058 clean_up:
3059 /* Clean up */
3060 if (save_cwd != NULL)
3062 tmp_vpath = vfs_path_from_str (save_cwd);
3063 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3064 vfs_path_free (tmp_vpath);
3065 g_free (save_cwd);
3068 if (save_dest != NULL)
3070 tmp_vpath = vfs_path_from_str (save_dest);
3071 mc_setctl (tmp_vpath, VFS_SETCTL_STALE_DATA, NULL);
3072 vfs_path_free (tmp_vpath);
3073 g_free (save_dest);
3076 linklist = free_linklist (linklist);
3077 dest_dirs = free_linklist (dest_dirs);
3078 #ifdef WITH_FULL_PATHS
3079 vfs_path_free (source_with_vpath);
3080 #endif /* WITH_FULL_PATHS */
3081 g_free (dest);
3082 vfs_path_free (dest_vpath);
3083 MC_PTR_FREE (ctx->dest_mask);
3085 #ifdef ENABLE_BACKGROUND
3086 /* Let our parent know we are saying bye bye */
3087 if (mc_global.we_are_background)
3089 int cur_pid = getpid ();
3090 /* Send pid to parent with child context, it is fork and
3091 don't modify real parent ctx */
3092 ctx->pid = cur_pid;
3093 parent_call ((void *) end_bg_process, ctx, 0);
3095 vfs_shut ();
3096 my_exit (EXIT_SUCCESS);
3098 #endif /* ENABLE_BACKGROUND */
3100 file_op_total_context_destroy (tctx);
3101 ret_fast:
3102 file_op_context_destroy (ctx);
3104 return ret_val;
3107 /* }}} */
3109 /* --------------------------------------------------------------------------------------------- */
3110 /* {{{ Query/status report routines */
3111 /** Report error with one file */
3112 FileProgressStatus
3113 file_error (const char *format, const char *file)
3115 char buf[BUF_MEDIUM];
3117 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3119 return do_file_error (buf);
3122 /* --------------------------------------------------------------------------------------------- */
3125 Cause emacs to enter folding mode for this file:
3126 Local variables:
3127 end: