📄 file.c
字号:
/* {{{ Copyright */
/* File managing. Important notes on this file:
About the use of dialogs in this file:
If you want to add a new dialog box (or call a routine that pops
up a dialog box), you have to provide a wrapper for background
operations (ie, background operations have to up-call to the parent
process).
For example, instead of using the message() routine, in this
file, you should use one of the stubs that call message with the
proper number of arguments (ie, message_1s, message_2s and so on).
Actually, that is a rule that should be followed by any routines
that may be called from this module.
*/
/* File managing
Copyright (C) 1994, 1995, 1996 The Free Software Foundation
Written by: 1994, 1995 Janne Kukonlehto
1994, 1995 Fred Leeflang
1994, 1995, 1996 Miguel de Icaza
1995, 1996 Jakub Jelinek
1997 Norbert Warmuth
1998 Pavel Machek
The copy code was based in GNU's cp, and was written by:
Torbjorn Granlund, David MacKenzie, and Jim Meyering.
The move code was based in GNU's mv, and was written by:
Mike Parker and David MacKenzie.
Janne Kukonlehto added much error recovery to them for being used
in an interactive program.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* }}} */
/* {{{ Include files */
#include <config.h>
/* Hack: the vfs code should not rely on this */
#define WITH_FULL_PATHS 1
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#ifdef OS2_NT
# include <io.h>
#endif
#include <errno.h>
#include "tty.h"
#include <ctype.h>
#include <malloc.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>
#ifdef SCO_FLAVOR
# include <sys/timeb.h> /* alex: for struct timeb, used in time.h */
#endif /* SCO_FLAVOR */
#if defined(_MSC_VER)
#include <sys/time.h___>
#else
#include <time.h>
#endif
#include <utime.h>
#include "mad.h"
#include "regex.h"
#include "util.h"
#include "dialog.h"
#include "global.h"
/* Needed by query_replace */
#include "color.h"
#include "win.h"
#include "dlg.h"
#include "widget.h"
#define WANT_WIDGETS
#include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
#include "file.h"
#include "layout.h"
#include "widget.h"
#include "wtools.h"
#include "background.h"
/* Needed for current_panel, other_panel and WTree */
#include "dir.h"
#include "panel.h"
#include "tree.h"
#include "key.h"
#include "../vfs/vfs.h"
#include "x.h"
/* }}} */
#if USE_VFS && USE_NETCODE
extern
#else
static
#endif
int do_reget;
/* rcsid [] = "$Id: file.c 15218 2005-05-11 16:50:39Z weiden $" */
int verbose = 1;
/* Recursive operation on subdirectories */
int dive_into_subdirs = 0;
/* When moving directories cross filesystem boundaries delete the successfull
copied files when all files below the directory and its subdirectories
were processed.
If erase_at_end is zero files will be deleted immediately after their
successful copy (Note: this behaviour is not tested and at the moment
it can't be changed at runtime) */
int erase_at_end = 1;
/* Preserve the original files' owner, group, permissions, and
timestamps (owner, group only as root). */
int preserve;
/* The value of the "preserve Attributes" checkbox in the copy file dialog.
We can't use the value of "preserve" because it can change in order to
preserve file attributs when moving files across filesystem boundaries
(we want to keep the value of the checkbox between copy operations). */
int op_preserve = 1;
/* If running as root, preserve the original uid/gid
(we don't want to try chwon for non root)
preserve_uidgid = preserve && uid == 0 */
int preserve_uidgid = 0;
/* The bits to preserve in created files' modes on file copy */
int umask_kill = 0777777;
/* If on, it gets a little scrict with dangerous operations */
int know_not_what_am_i_doing = 0;
int stable_symlinks = 0;
/* The next two are not static, since they are used on background.c */
/* Controls appending to files, shared with filequery.c */
int do_append = 0;
/* With ETA on we have extra screen space */
int eta_extra = 0;
/* result from the recursive query */
int recursive_result;
/* The estimated time of arrival in seconds */
double eta_secs;
/* Used to save the hint line */
static int last_hint_line;
/* mapping operations into names */
char *operation_names [] = { "Copy", "Move", "Delete" };
/* This is a hard link cache */
struct link {
struct link *next;
vfs *vfs;
dev_t dev;
ino_t ino;
short linkcount;
umode_t st_mode;
char name[1];
};
/* the hard link cache */
struct link *linklist = NULL;
/* the files-to-be-erased list */
struct link *erase_list;
/* In copy_dir_dir we use two additional single linked lists: The first -
variable name `parent_dirs' - holds information about already copied
directories and is used to detect cyclic symbolic links.
The second (`dest_dirs' below) holds information about just created
target directories and is used to detect when an directory is copied
into itself (we don't want to copy infinitly).
Both lists don't use the linkcount and name structure members of struct
link. */
struct link *dest_dirs = 0;
struct re_pattern_buffer rx;
struct re_registers regs;
static char *dest_mask = NULL;
/* To symlinks the difference between `follow Links' checked and not
checked is the stat call used (mc_stat resp. mc_lstat) */
int (*xstat)(const char *, struct stat *) = mc_lstat;
static int op_follow_links = 0;
/* File operate window sizes */
#define WX 62
#define WY 10
#define BY 10
#define WX_ETA_EXTRA 12
#define FCOPY_GAUGE_X 14
#define FCOPY_LABEL_X 5
/* Used for button result values */
enum {
REPLACE_YES = B_USER,
REPLACE_NO,
REPLACE_APPEND,
REPLACE_ALWAYS,
REPLACE_UPDATE,
REPLACE_NEVER,
REPLACE_ABORT,
REPLACE_SIZE,
REPLACE_REGET
};
enum {
RECURSIVE_YES,
RECURSIVE_NO,
RECURSIVE_ALWAYS,
RECURSIVE_NEVER,
RECURSIVE_ABORT
};
/* Pointer to the operate dialog */
static Dlg_head *op_dlg;
int showing_eta;
int showing_bps;
unsigned long bps = 0, bps_time = 0;
static char *op_names [] = { N_(" Copy "), N_(" Move "), N_(" Delete ") };
static int selected_button;
static int last_percentage [3];
/* Replace dialog: color set, descriptor and filename */
static int replace_colors [4];
static Dlg_head *replace_dlg;
static char *replace_filename;
static int replace_result;
static struct stat *s_stat, *d_stat;
static int recursive_erase (char *s);
static int erase_file (char *s);
/* Describe the components in the panel operations window */
static WLabel *FileLabel [2];
static WLabel *FileString [2];
static WLabel *ProgressLabel [3];
static WGauge *ProgressGauge [3];
static WLabel *eta_label;
static WLabel *bps_label;
static WLabel *stalled_label;
/* }}} */
/* {{{ File progress display routines */
#ifndef HAVE_X
static int
check_buttons (void)
{
int c;
Gpm_Event event;
c = get_event (&event, 0, 0);
if (c == EV_NONE)
return FILE_CONT;
dlg_process_event (op_dlg, c, &event);
switch (op_dlg->ret_value) {
case FILE_SKIP:
return FILE_SKIP;
break;
case B_CANCEL:
case FILE_ABORT:
return FILE_ABORT;
break;
default:
return FILE_CONT;
}
}
#else
#ifdef HAVE_TK
static int
check_buttons (void)
{
tk_dispatch_all ();
if (op_dlg->running)
return FILE_CONT;
}
#endif /* HAVE_TK */
#ifdef HAVE_XVIEW
static int
check_buttons (void)
{
xv_dispatch_something ();
if (op_dlg->running)
return FILE_CONT;
}
#endif /* HAVE_XVIEW */
#ifdef HAVE_GNOME
#include <gtk/gtk.h>
static int
check_buttons (void)
{
x_flush_events ();
if (op_dlg->running)
return FILE_CONT;
if (op_dlg->ret_value == B_CANCEL)
return FILE_ABORT;
else
return op_dlg->ret_value;
}
#endif /* HAVE_GNOME */
#endif /* HAVE_X */
static int
op_win_callback (struct Dlg_head *h, int id, int msg)
{
switch (msg){
#ifndef HAVE_X
case DLG_DRAW:
attrset (COLOR_NORMAL);
dlg_erase (h);
draw_box (h, 1, 2, h->lines-2, h->cols-4);
return 1;
#endif
}
return 0;
}
void
create_op_win (int op, int with_eta)
{
int i, x_size;
int minus = verbose ? 0 : 3;
int eta_offset = with_eta ? (WX_ETA_EXTRA) / 2 : 0;
#ifdef HAVE_XVIEW
char *sixty = " ";
char *fifteen = " ";
#else
char *sixty = "";
char *fifteen = "";
#endif
replace_result = 0;
recursive_result = 0;
showing_eta = with_eta;
showing_bps = with_eta;
eta_extra = with_eta ? WX_ETA_EXTRA : 0;
x_size = (WX + 4) + eta_extra;
op_dlg = create_dlg (0, 0, WY-minus+4, x_size, dialog_colors,
op_win_callback, "", "opwin", DLG_CENTER);
#ifndef HAVE_X
last_hint_line = the_hint->widget.y;
if ((op_dlg->y + op_dlg->lines) > last_hint_line)
the_hint->widget.y = op_dlg->y + op_dlg->lines+1;
#endif
x_set_dialog_title (op_dlg, "");
tk_new_frame (op_dlg, "b.");
add_widgetl (op_dlg, button_new (BY-minus, WX - 19 + eta_offset, FILE_ABORT,
NORMAL_BUTTON, _("&Abort"), 0, 0, "abort"),
XV_WLAY_RIGHTOF);
add_widgetl (op_dlg, button_new (BY-minus, 14 + eta_offset, FILE_SKIP,
NORMAL_BUTTON, _("&Skip"), 0, 0, "skip"),
XV_WLAY_CENTERROW);
tk_new_frame (op_dlg, "2.");
add_widgetl (op_dlg, ProgressGauge [2] = gauge_new (7, FCOPY_GAUGE_X, 0, 100, 0, "g-1"),
XV_WLAY_RIGHTOF);
add_widgetl (op_dlg, ProgressLabel [2] = label_new (7, FCOPY_LABEL_X, fifteen, "l-1"),
XV_WLAY_NEXTROW);
add_widgetl (op_dlg, bps_label = label_new (7, WX, "", "bps-label"), XV_WLAY_NEXTROW);
tk_new_frame (op_dlg, "1.");
add_widgetl (op_dlg, ProgressGauge [1] = gauge_new (8, FCOPY_GAUGE_X, 0, 100, 0, "g-2"),
XV_WLAY_RIGHTOF);
add_widgetl (op_dlg, ProgressLabel [1] = label_new (8, FCOPY_LABEL_X, fifteen, "l-2"),
XV_WLAY_RIGHTOF);
add_widgetl (op_dlg, stalled_label = label_new (8, WX, "", "stalled"), XV_WLAY_NEXTROW);
tk_new_frame (op_dlg, "0.");
add_widgetl (op_dlg, ProgressGauge [0] = gauge_new (6, FCOPY_GAUGE_X, 0, 100, 0, "g-3"),
XV_WLAY_RIGHTOF);
add_widgetl (op_dlg, ProgressLabel [0] = label_new (6, FCOPY_LABEL_X, fifteen, "l-3"),
XV_WLAY_RIGHTOF);
add_widgetl (op_dlg, eta_label = label_new (6, WX, "", "eta_label"), XV_WLAY_NEXTROW);
tk_new_frame (op_dlg, "f1.");
add_widgetl (op_dlg, FileString [1] = label_new (4, FCOPY_GAUGE_X, sixty, "fs-l-1"),
XV_WLAY_RIGHTOF);
add_widgetl (op_dlg, FileLabel [1] = label_new (4, FCOPY_LABEL_X, fifteen, "fs-l-2"),
XV_WLAY_NEXTROW);
tk_new_frame (op_dlg, "f0.");
add_widgetl (op_dlg, FileString [0] = label_new (3, FCOPY_GAUGE_X, sixty, "fs-x-1"),
XV_WLAY_RIGHTOF);
add_widgetl (op_dlg, FileLabel [0] = label_new (3, FCOPY_LABEL_X, fifteen, "fs-x-2"),
XV_WLAY_NEXTROW);
/* We will manage the dialog without any help, that's why
we have to call init_dlg */
init_dlg (op_dlg);
op_dlg->running = 1;
selected_button = FILE_SKIP;
for (i = 0; i < 3; i++)
last_percentage [i] = -99;
}
void
destroy_op_win (void)
{
#ifdef HAVE_XVIEW
xtoolkit_kill_dialog (op_dlg);
#endif
dlg_run_done (op_dlg);
destroy_dlg (op_dlg);
#ifndef HAVE_X
the_hint->widget.y = last_hint_line;
#endif
}
static int
show_no_bar (int n)
{
if (n >= 0) {
label_set_text (ProgressLabel [n], "");
gauge_show (ProgressGauge [n], 0);
}
return check_buttons ();
}
#ifndef HAVE_X
#define truncFileString(s) name_trunc (s, eta_extra + 47)
#else
#define truncFileString(s) s
#endif
static int
show_source (char *s)
{
if (s != NULL){
#ifdef WITH_FULL_PATHS
int i = strlen (cpanel->cwd);
/* We remove the full path we have added before */
if (!strncmp (s, cpanel->cwd, i)){
if (s[i] == PATH_SEP)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -