Don't calculate totals for move operation on single file.

Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
This commit is contained in:
Andrew Borodin 2017-08-08 15:36:48 +03:00
parent 7dac8f7bd6
commit 3ba4417c18

View File

@ -609,6 +609,118 @@ do_compute_dir_size (const vfs_path_t * dirname_vpath, dirsize_status_msg_t * ds
return ret;
}
/* --------------------------------------------------------------------------------------------- */
/**
* panel_compute_totals:
*
* compute the number of files and the number of bytes
* used up by the whole selection, recursing directories
* as required. In addition, it checks to see if it will
* overwrite any files by doing the copy.
*/
static FileProgressStatus
panel_compute_totals (const WPanel * panel, dirsize_status_msg_t * sm, size_t * ret_count,
uintmax_t * ret_total, gboolean compute_symlinks)
{
int i;
size_t dir_count = 0;
for (i = 0; i < panel->dir.len; i++)
{
struct stat *s;
if (!panel->dir.list[i].f.marked)
continue;
s = &panel->dir.list[i].st;
if (S_ISDIR (s->st_mode))
{
vfs_path_t *p;
FileProgressStatus status;
p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, (char *) NULL);
status = compute_dir_size (p, sm, &dir_count, ret_count, ret_total, compute_symlinks);
vfs_path_free (p);
if (status != FILE_CONT)
return status;
}
else
{
(*ret_count)++;
*ret_total += (uintmax_t) s->st_size;
}
}
return FILE_CONT;
}
/* --------------------------------------------------------------------------------------------- */
/** Initialize variables for progress bars */
static FileProgressStatus
panel_operate_init_totals (const WPanel * panel, const vfs_path_t * source,
const struct stat *source_stat, file_op_context_t * ctx,
filegui_dialog_type_t dialog_type)
{
FileProgressStatus status;
#ifdef ENABLE_BACKGROUND
if (mc_global.we_are_background)
return FILE_CONT;
#endif
if (verbose && file_op_compute_totals)
{
dirsize_status_msg_t dsm;
memset (&dsm, 0, sizeof (dsm));
dsm.allow_skip = TRUE;
status_msg_init (STATUS_MSG (&dsm), _("Directory scanning"), 0, dirsize_status_init_cb,
dirsize_status_update_cb, dirsize_status_deinit_cb);
ctx->progress_count = 0;
ctx->progress_bytes = 0;
if (source == NULL)
status = panel_compute_totals (panel, &dsm, &ctx->progress_count, &ctx->progress_bytes,
ctx->follow_links);
else if (S_ISDIR (source_stat->st_mode))
{
size_t dir_count = 0;
status = compute_dir_size (source, &dsm, &dir_count, &ctx->progress_count,
&ctx->progress_bytes, ctx->follow_links);
}
else
{
ctx->progress_count++;
ctx->progress_bytes += (uintmax_t) source_stat->st_size;
status = FILE_CONT;
}
status_msg_deinit (STATUS_MSG (&dsm));
ctx->progress_totals_computed = (status == FILE_CONT);
if (status == FILE_SKIP)
status = FILE_CONT;
}
else
{
status = FILE_CONT;
ctx->progress_count = panel->marked;
ctx->progress_bytes = panel->total;
ctx->progress_totals_computed = FALSE;
}
file_op_context_create_ui (ctx, TRUE, dialog_type);
return status;
}
/* --------------------------------------------------------------------------------------------- */
static FileProgressStatus
@ -976,9 +1088,21 @@ try_remove_file (file_op_context_t * ctx, const vfs_path_t * vpath, FileProgress
/* --------------------------------------------------------------------------------------------- */
/* {{{ Move routines */
/**
* Move single file or one of many files from one location to another.
*
* @panel pointer to panel in case of single file, NULL otherwise
* @tctx file operation total context object
* @ctx file operation context object
* @s source file name
* @d destination file name
*
* @return operation result
*/
static FileProgressStatus
move_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s,
const char *d)
move_file_file (const WPanel * panel, file_op_total_context_t * tctx, file_op_context_t * ctx,
const char *s, const char *d)
{
struct stat src_stats, dst_stats;
FileProgressStatus return_status = FILE_CONT;
@ -992,6 +1116,7 @@ move_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx, const c
file_progress_show_source (ctx, src_vpath);
file_progress_show_target (ctx, dst_vpath);
/* FIXME: do we really need to check buttons in case of single file? */
if (check_progress_buttons (ctx) == FILE_ABORT)
{
return_status = FILE_ABORT;
@ -1050,6 +1175,7 @@ move_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx, const c
if (mc_rename (src_vpath, dst_vpath) == 0)
{
/* FIXME: do we really need to update progress in case of single file? */
return_status = progress_update_one (tctx, ctx, src_stats.st_size);
goto ret;
}
@ -1079,7 +1205,17 @@ move_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx, const c
}
#endif
/* Failed because filesystem boundary -> copy the file instead */
/* Failed rename -> copy the file instead */
if (panel != NULL)
{
/* In case of single file, calculate totals. In case of many files,
totals are calcuated already. */
return_status =
panel_operate_init_totals (panel, src_vpath, &src_stats, ctx, FILEGUI_DIALOG_ONE_ITEM);
if (return_status != FILE_CONT)
goto ret;
}
old_ask_overwrite = tctx->ask_overwrite;
tctx->ask_overwrite = FALSE;
return_status = copy_file_file (tctx, ctx, s, d);
@ -1089,16 +1225,22 @@ move_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx, const c
copy_done = TRUE;
file_progress_show_source (ctx, NULL);
file_progress_show (ctx, 0, 0, "", FALSE);
/* FIXME: there is no need to update progress and check buttons
at the finish of single file operation. */
if (panel == NULL)
{
file_progress_show_source (ctx, NULL);
file_progress_show (ctx, 0, 0, "", FALSE);
return_status = check_progress_buttons (ctx);
if (return_status != FILE_CONT)
goto ret;
}
return_status = check_progress_buttons (ctx);
if (return_status != FILE_CONT)
goto ret;
mc_refresh ();
retry_src_remove:
if (!try_remove_file (ctx, src_vpath, &return_status))
if (!try_remove_file (ctx, src_vpath, &return_status) && panel == NULL)
goto ret;
if (!copy_done)
@ -1307,6 +1449,157 @@ erase_dir_after_copy (file_op_total_context_t * tctx, file_op_context_t * ctx,
/* --------------------------------------------------------------------------------------------- */
/**
* Move single directory or one of many directories from one location to another.
*
* @panel pointer to panel in case of single directory, NULL otherwise
* @tctx file operation total context object
* @ctx file operation context object
* @s source directory name
* @d destination directory name
*
* @return operation result
*/
static FileProgressStatus
do_move_dir_dir (const WPanel * panel, file_op_total_context_t * tctx, file_op_context_t * ctx,
const char *s, const char *d)
{
struct stat sbuf, dbuf;
FileProgressStatus return_status = FILE_CONT;
gboolean move_over = FALSE;
gboolean dstat_ok;
vfs_path_t *src_vpath, *dst_vpath;
src_vpath = vfs_path_from_str (s);
dst_vpath = vfs_path_from_str (d);
file_progress_show_source (ctx, src_vpath);
file_progress_show_target (ctx, dst_vpath);
/* FIXME: do we really need to check buttons in case of single directory? */
if (panel != NULL && check_progress_buttons (ctx) == FILE_ABORT)
{
return_status = FILE_ABORT;
goto ret_fast;
}
mc_refresh ();
mc_stat (src_vpath, &sbuf);
dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
if (dstat_ok && check_same_file (s, &sbuf, d, &dbuf, &return_status))
goto ret_fast;
if (!dstat_ok)
; /* destination doesn't exist */
else if (!ctx->dive_into_subdirs)
move_over = TRUE;
else
{
vfs_path_t *tmp;
tmp = dst_vpath;
dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
vfs_path_free (tmp);
}
d = vfs_path_as_str (dst_vpath);
/* Check if the user inputted an existing dir */
retry_dst_stat:
if (mc_stat (dst_vpath, &dbuf) == 0)
{
if (move_over)
{
return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, TRUE, TRUE, NULL);
if (return_status != FILE_CONT)
goto ret;
goto oktoret;
}
else if (ctx->skip_all)
return_status = FILE_SKIPALL;
else
{
if (S_ISDIR (dbuf.st_mode))
return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), d);
else
return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), d);
if (return_status == FILE_SKIPALL)
ctx->skip_all = TRUE;
if (return_status == FILE_RETRY)
goto retry_dst_stat;
}
goto ret_fast;
}
retry_rename:
if (mc_rename (src_vpath, dst_vpath) == 0)
{
return_status = FILE_CONT;
goto ret;
}
if (errno != EXDEV)
{
if (!ctx->skip_all)
{
return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
if (return_status == FILE_SKIPALL)
ctx->skip_all = TRUE;
if (return_status == FILE_RETRY)
goto retry_rename;
}
goto ret;
}
/* Failed because of filesystem boundary -> copy dir instead */
if (panel != NULL)
{
/* In case of single directory, calculate totals. In case of many directories,
totals are calcuated already. */
return_status =
panel_operate_init_totals (panel, src_vpath, &sbuf, ctx, FILEGUI_DIALOG_ONE_ITEM);
if (return_status != FILE_CONT)
goto ret;
}
return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, FALSE, TRUE, NULL);
if (return_status != FILE_CONT)
goto ret;
oktoret:
/* FIXME: there is no need to update progress and check buttons
at the finish of single directory operation. */
if (panel == NULL)
{
file_progress_show_source (ctx, NULL);
file_progress_show_target (ctx, NULL);
file_progress_show (ctx, 0, 0, "", FALSE);
return_status = check_progress_buttons (ctx);
if (return_status != FILE_CONT)
goto ret;
}
mc_refresh ();
erase_dir_after_copy (tctx, ctx, src_vpath, &return_status);
ret:
erase_list = free_linklist (erase_list);
ret_fast:
vfs_path_free (src_vpath);
vfs_path_free (dst_vpath);
return return_status;
}
/* --------------------------------------------------------------------------------------------- */
/* {{{ Panel operate routines */
/**
@ -1388,118 +1681,6 @@ check_single_entry (const WPanel * panel, gboolean force_single, struct stat *sr
return ok ? source : NULL;
}
/* --------------------------------------------------------------------------------------------- */
/**
* panel_compute_totals:
*
* compute the number of files and the number of bytes
* used up by the whole selection, recursing directories
* as required. In addition, it checks to see if it will
* overwrite any files by doing the copy.
*/
static FileProgressStatus
panel_compute_totals (const WPanel * panel, dirsize_status_msg_t * sm, size_t * ret_count,
uintmax_t * ret_total, gboolean compute_symlinks)
{
int i;
size_t dir_count = 0;
for (i = 0; i < panel->dir.len; i++)
{
struct stat *s;
if (!panel->dir.list[i].f.marked)
continue;
s = &panel->dir.list[i].st;
if (S_ISDIR (s->st_mode))
{
vfs_path_t *p;
FileProgressStatus status;
p = vfs_path_append_new (panel->cwd_vpath, panel->dir.list[i].fname, (char *) NULL);
status = compute_dir_size (p, sm, &dir_count, ret_count, ret_total, compute_symlinks);
vfs_path_free (p);
if (status != FILE_CONT)
return status;
}
else
{
(*ret_count)++;
*ret_total += (uintmax_t) s->st_size;
}
}
return FILE_CONT;
}
/* --------------------------------------------------------------------------------------------- */
/** Initialize variables for progress bars */
static FileProgressStatus
panel_operate_init_totals (const WPanel * panel, const vfs_path_t * source,
const struct stat *source_stat, file_op_context_t * ctx,
filegui_dialog_type_t dialog_type)
{
FileProgressStatus status;
#ifdef ENABLE_BACKGROUND
if (mc_global.we_are_background)
return FILE_CONT;
#endif
if (verbose && file_op_compute_totals)
{
dirsize_status_msg_t dsm;
memset (&dsm, 0, sizeof (dsm));
dsm.allow_skip = TRUE;
status_msg_init (STATUS_MSG (&dsm), _("Directory scanning"), 0, dirsize_status_init_cb,
dirsize_status_update_cb, dirsize_status_deinit_cb);
ctx->progress_count = 0;
ctx->progress_bytes = 0;
if (source == NULL)
status = panel_compute_totals (panel, &dsm, &ctx->progress_count, &ctx->progress_bytes,
ctx->follow_links);
else if (S_ISDIR (source_stat->st_mode))
{
size_t dir_count = 0;
status = compute_dir_size (source, &dsm, &dir_count, &ctx->progress_count,
&ctx->progress_bytes, ctx->follow_links);
}
else
{
ctx->progress_count++;
ctx->progress_bytes += (uintmax_t) source_stat->st_size;
status = FILE_CONT;
}
status_msg_deinit (STATUS_MSG (&dsm));
ctx->progress_totals_computed = (status == FILE_CONT);
if (status == FILE_SKIP)
status = FILE_CONT;
}
else
{
status = FILE_CONT;
ctx->progress_count = panel->marked;
ctx->progress_bytes = panel->total;
ctx->progress_totals_computed = FALSE;
}
file_op_context_create_ui (ctx, TRUE, dialog_type);
return status;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Generate user prompt for panel operation.
@ -1744,32 +1925,11 @@ operate_single_file (const WPanel * panel, FileOperation operation, file_op_tota
break;
case OP_MOVE:
{
vfs_path_t *dest_vpath;
dest_vpath = vfs_path_from_str (dest);
/* try rename */
if (mc_rename (src_vpath, dest_vpath) == 0)
value = FILE_CONT;
else
{
/* copy + delete */
value =
panel_operate_init_totals (panel, src_vpath, src_stat, ctx,
dialog_type);
if (value == FILE_CONT)
{
if (is_file)
value = move_file_file (tctx, ctx, src, dest);
else
value = move_dir_dir (tctx, ctx, src, dest);
}
}
vfs_path_free (dest_vpath);
break;
}
if (is_file)
value = move_file_file (panel, tctx, ctx, src, dest);
else
value = do_move_dir_dir (panel, tctx, ctx, src, dest);
break;
default:
/* Unknown file operation */
@ -1844,9 +2004,9 @@ operate_one_file (const WPanel * panel, FileOperation operation, file_op_total_c
case OP_MOVE:
if (is_file)
value = move_file_file (tctx, ctx, src, dest);
value = move_file_file (NULL, tctx, ctx, src, dest);
else
value = move_dir_dir (tctx, ctx, src, dest);
value = do_move_dir_dir (NULL, tctx, ctx, src, dest);
break;
default:
@ -2701,120 +2861,7 @@ copy_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const cha
FileProgressStatus
move_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d)
{
struct stat sbuf, dbuf;
FileProgressStatus return_status = FILE_CONT;
gboolean move_over = FALSE;
gboolean dstat_ok;
vfs_path_t *src_vpath, *dst_vpath;
src_vpath = vfs_path_from_str (s);
dst_vpath = vfs_path_from_str (d);
file_progress_show_source (ctx, src_vpath);
file_progress_show_target (ctx, dst_vpath);
if (check_progress_buttons (ctx) == FILE_ABORT)
{
return_status = FILE_ABORT;
goto ret_fast;
}
mc_refresh ();
mc_stat (src_vpath, &sbuf);
dstat_ok = (mc_stat (dst_vpath, &dbuf) == 0);
if (dstat_ok && check_same_file (s, &sbuf, d, &dbuf, &return_status))
goto ret_fast;
if (!dstat_ok)
; /* destination doesn't exist */
else if (!ctx->dive_into_subdirs)
move_over = TRUE;
else
{
vfs_path_t *tmp;
tmp = dst_vpath;
dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
vfs_path_free (tmp);
}
d = vfs_path_as_str (dst_vpath);
/* Check if the user inputted an existing dir */
retry_dst_stat:
if (mc_stat (dst_vpath, &dbuf) == 0)
{
if (move_over)
{
return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, TRUE, TRUE, NULL);
if (return_status != FILE_CONT)
goto ret;
goto oktoret;
}
else if (ctx->skip_all)
return_status = FILE_SKIPALL;
else
{
if (S_ISDIR (dbuf.st_mode))
return_status = file_error (_("Cannot overwrite directory \"%s\"\n%s"), d);
else
return_status = file_error (_("Cannot overwrite file \"%s\"\n%s"), d);
if (return_status == FILE_SKIPALL)
ctx->skip_all = TRUE;
if (return_status == FILE_RETRY)
goto retry_dst_stat;
}
goto ret_fast;
}
retry_rename:
if (mc_rename (src_vpath, dst_vpath) == 0)
{
return_status = FILE_CONT;
goto ret;
}
if (errno != EXDEV)
{
if (!ctx->skip_all)
{
return_status = files_error (_("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
if (return_status == FILE_SKIPALL)
ctx->skip_all = TRUE;
if (return_status == FILE_RETRY)
goto retry_rename;
}
goto ret;
}
/* Failed because of filesystem boundary -> copy dir instead */
return_status = copy_dir_dir (tctx, ctx, s, d, FALSE, FALSE, TRUE, NULL);
if (return_status != FILE_CONT)
goto ret;
oktoret:
file_progress_show_source (ctx, NULL);
file_progress_show_target (ctx, NULL);
file_progress_show (ctx, 0, 0, "", FALSE);
return_status = check_progress_buttons (ctx);
if (return_status != FILE_CONT)
goto ret;
mc_refresh ();
erase_dir_after_copy (tctx, ctx, src_vpath, &return_status);
ret:
erase_list = free_linklist (erase_list);
ret_fast:
vfs_path_free (src_vpath);
vfs_path_free (dst_vpath);
return return_status;
return do_move_dir_dir (NULL, tctx, ctx, s, d);
}
/* }}} */