📄 view.c
字号:
/* {{{ Copyright notice */
/* View file module for the Midnight Commander
Copyright (C) 1994, 1995, 1996 The Free Software Foundation
Written by: 1994, 1995, 1998 Miguel de Icaza
1994, 1995 Janne Kukonlehto
1995 Jakub Jelinek
1996 Joseph M. Hinkle
1997 Norbert Warmuth
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. */
/* }}} */
/* {{{ Declarations */
#include <config.h>
#include "x.h"
#include <stdio.h>
#ifdef OS2_NT
# include <io.h>
#endif
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <string.h>
#include "tty.h"
#include <sys/stat.h>
#ifdef HAVE_MMAP
# include <sys/mman.h>
#endif
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <ctype.h> /* For toupper() */
#include <stdlib.h> /* atoi() */
#include <malloc.h>
#include <errno.h>
#include <limits.h>
#include <sys/param.h>
#include "mem.h"
#include "mad.h"
#include "util.h"
#include "dlg.h" /* Needed by widget.h */
#include "widget.h" /* Needed for buttonbar_new */
#include "color.h"
#include "dialog.h"
#include "file.h"
#include "mouse.h"
#include "global.h"
#include "help.h"
#include "key.h" /* For mi_getch() */
#include "layout.h"
#include "wtools.h" /* For query_set_sel() */
#if defined(HAVE_RX_H) && defined(HAVE_REGCOMP)
# include <rx.h>
#else
# include "regex.h"
#endif
#include "fs.h"
#include "../vfs/vfs.h"
#include "dir.h"
#include "panel.h" /* Needed for current_panel and other_panel */
#include "win.h"
#include "main.h" /* For the externs */
#define WANT_WIDGETS
#include "view.h"
#ifndef MAP_FILE
#define MAP_FILE 0
#endif
/* Block size for reading files in parts */
#define READ_BLOCK 8192
#define VIEW_PAGE_SIZE 8192
#ifdef IS_AIX
# define IFAIX(x) case (x):
#else
# define IFAIX(x)
#endif
/* Maxlimit for skipping updates */
int max_dirt_limit =
#ifdef OS2_NT
0;
#else
10;
#endif
/* Our callback */
static int view_callback (Dlg_head *h, WView *view, int msg, int par);
/* If set, show a ruler */
int ruler = 0;
/* Scrolling is done in pages or line increments */
int mouse_move_pages_viewer = 1;
/* Used to compute the bottom first variable */
int have_fast_cpu = 0;
/* wrap mode default */
int global_wrap_mode = 1;
int default_hex_mode = 0;
int default_hexedit_mode = 0;
int default_magic_flag = 1;
int default_nroff_flag = 1;
int altered_hex_mode = 0;
int altered_magic_flag = 0;
int altered_nroff_flag = 0;
/* }}} */
/* "$Id: view.c 15218 2005-05-11 16:50:39Z weiden $" */
static char hex_char[] = "0123456789ABCDEF";
/* }}} */
/* {{{ Clean-up functions */
void
close_view_file (WView *view)
{
if (view->file != -1){
mc_close (view->file);
view->file = -1;
}
}
void
free_file (WView *view)
{
int i;
#ifdef HAVE_MMAP
if (view->mmapping){
mc_munmap (view->data, view->s.st_size);
close_view_file (view);
} else
#endif /* HAVE_MMAP */
{
if (view->reading_pipe){
/* Check error messages */
if (!view->have_frame)
check_error_pipe ();
/* Close pipe */
pclose (view->stdfile);
view->stdfile = NULL;
/* Ignore errors because we don't want to hear about broken pipe */
close_error_pipe (-1, NULL);
} else
close_view_file (view);
}
/* Block_ptr may be zero if the file was a file with 0 bytes */
if (view->growing_buffer && view->block_ptr){
for (i = 0; i < view->blocks; i++){
free (view->block_ptr [i].data);
}
free (view->block_ptr);
}
}
/* Valid parameters for second parameter to set_monitor */
enum { off, on };
/* Both views */
void
view_done (WView *view)
{
set_monitor (view, off);
#ifndef HAVE_MMAP
/* alex: release core, used to replace mmap */
if (!view->growing_buffer && view->data != NULL)
{
free(view->data);
view->data = NULL;
}
#endif /* HAVE_MMAP */
if (view->view_active){
if (view->localcopy)
mc_ungetlocalcopy (view->filename, view->localcopy, 0);
free_file (view);
free (view->filename);
if (view->command)
free (view->command);
}
view->view_active = 0;
default_hex_mode = view->hex_mode;
default_nroff_flag = view->viewer_nroff_flag;
default_magic_flag = view->viewer_magic_flag;
}
static void view_hook (void *);
void
view_destroy (WView *view)
{
view_done (view);
if (view->have_frame)
delete_hook (&select_file_hook, view_hook);
x_destroy_view (view);
}
static int
get_byte (WView *view, int byte_index)
{
int page = byte_index / VIEW_PAGE_SIZE + 1;
int offset = byte_index % VIEW_PAGE_SIZE;
int i, n;
block_ptr_t *tmp;
if (view->growing_buffer){
if (page > view->blocks){
tmp = xmalloc (sizeof (block_ptr_t) * page, "get_byte");
if (view->block_ptr){
bcopy (view->block_ptr, tmp, sizeof (block_ptr_t) *
view->blocks);
free (view->block_ptr);
}
view->block_ptr = tmp;
for (i = view->blocks; i < page; i++){
char *p = malloc (VIEW_PAGE_SIZE);
view->block_ptr [i].data = p;
if (!p)
return '\n';
if (view->stdfile != NULL)
n = fread (p, 1, VIEW_PAGE_SIZE, view->stdfile);
else
n = mc_read (view->file, p, VIEW_PAGE_SIZE);
if (n != -1)
view->bytes_read += n;
if (view->s.st_size < view->bytes_read){
view->bottom_first = -1; /* Invalidate cache */
view->s.st_size = view->bytes_read;
view->last_byte = view->bytes_read;
if (view->reading_pipe)
view->last_byte = view->first + view->bytes_read;
}
/* To force loading the next page */
if (n == VIEW_PAGE_SIZE && view->reading_pipe){
view->last_byte++;
}
}
view->blocks = page;
}
if (byte_index > view->bytes_read){
return -1;
} else
return view->block_ptr [page-1].data [offset];
} else {
if (byte_index >= view->last_byte)
return -1;
else
return view->data [byte_index];
}
}
static void
enqueue_change (struct hexedit_change_node **head, struct hexedit_change_node *node)
{
struct hexedit_change_node *curr = *head;
while (curr) {
if (node->offset < curr->offset) {
*head = node;
node->next = curr;
return;
}
head = (struct hexedit_change_node **) curr;
curr = curr->next;
}
*head = node;
node->next = curr;
}
static void move_right (WView *);
static void
put_editkey (WView *view, unsigned char key)
{
struct hexedit_change_node *node;
unsigned char byte_val;
if (!view->hexedit_mode || view->growing_buffer != 0)
return;
/* Has there been a change at this position ? */
node = view->change_list;
while (node) {
if (node->offset != view->edit_cursor)
node = node->next;
else
break;
}
if (view->view_side == view_side_left) {
/* Hex editing */
if (key >= '0' && key <= '9')
key -= '0';
else if (key >= 'A' && key <= 'F')
key -= '7';
else if (key >= 'a' && key <= 'f')
key -= 'W';
else
return;
if (node)
byte_val = node->value;
else
byte_val = get_byte(view, view->edit_cursor);
if (view->nib_shift == 0) {
byte_val = (byte_val & 0x0f) | (key << 4);
} else {
byte_val = (byte_val & 0xf0) | (key);
}
} else {
/* Text editing */
byte_val = key;
}
if (!node) {
node = (struct hexedit_change_node *)
xmalloc(sizeof(struct hexedit_change_node), "HexEdit");
if (node) {
#ifndef HAVE_MMAP
/*
** alex@bcs.zaporizhzhe.ua: here we are using file copy
** completely loaded into memory, so we can replace bytes
** in view->data array to allow changes to be reflected
** when user switches back to ascii mode
*/
view->data[view->edit_cursor] = byte_val;
#endif /* HAVE_MMAP */
node->offset = view->edit_cursor;
node->value = byte_val;
enqueue_change (&view->change_list, node);
}
} else {
node->value = byte_val;
}
view->dirty++;
view_update (view);
move_right (view);
}
static void
free_change_list (WView *view)
{
struct hexedit_change_node *n = view->change_list;
while (n) {
view->change_list = n->next;
free (n);
n = view->change_list;
}
view->file_dirty = 0;
view->dirty++;
}
static void
save_edit_changes (WView *view)
{
struct hexedit_change_node *node = view->change_list;
int fp;
fp = open (view->filename, O_WRONLY);
if (fp >= 0) {
while (node) {
lseek (fp, node->offset, SEEK_SET);
write (fp, &node->value, 1);
node = node->next;
}
close (fp);
}
free_change_list (view);
}
static int
view_ok_to_quit (WView *view)
{
int r;
char *text;
if (!view->change_list)
return 1;
query_set_sel (1);
text = copy_strings (_("File: \n\n "), view->filename,
_("\n\nhas been modified, do you want to save the changes?\n"), NULL);
r = query_dialog (_(" Save changes "), text, 2, 3, _("&Yes"), _("&No"), _("&Cancel"));
free (text);
switch (r) {
case 0:
save_edit_changes (view);
return 1;
case 1:
free_change_list (view);
return 1;
default:
return 0;
}
}
static char *
set_view_init_error (WView *view, char *msg)
{
view->growing_buffer = 0;
view->reading_pipe = 0;
view->first = 0;
view->last_byte = 0;
if (msg){
view->bytes_read = strlen (msg);
return strdup (msg);
}
return 0;
}
/* return values: 0 for success, else points to error message */
static char *
init_growing_view (WView *view, char *name, char *filename)
{
view->growing_buffer = 1;
if (name){
view->reading_pipe = 1;
view->s.st_size = 0;
open_error_pipe ();
if ((view->stdfile = popen (name, "r")) == NULL){
close_error_pipe (view->have_frame?-1:1, view->data);
return set_view_init_error (view, _(" Can't spawn child program "));
}
#ifndef HAVE_XVIEW
/* First, check if filter produced any output */
get_byte (view, 0);
if (view->bytes_read <= 0){
pclose (view->stdfile);
view->stdfile = NULL;
close_error_pipe (view->have_frame?-1:1, view->data);
return set_view_init_error (view, _(" Empty output from child filter "));
}
#endif
} else {
view->stdfile = NULL;
if ((view->file = mc_open (filename, O_RDONLY)) == -1)
return set_view_init_error (view, _(" Could not open file "));
}
return 0;
}
/* Load filename into core */
/* returns:
-1 on failure.
if (have_frame), we return success, but data points to a
error message instead of the file buffer (quick_view feature).
*/
static char *load_view_file (WView *view, char *filename)
{
char *cmd;
int type;
if ((view->file = mc_open (filename, O_RDONLY)) < 0){
set_view_init_error (view, 0);
return (copy_strings (_(" Can't open file \""),
filename, "\"\n ",
unix_error_string (errno), " ", 0));
}
if (mc_fstat (view->file, &view->s) < 0){
set_view_init_error (view, 0);
close_view_file (view);
return copy_strings (_(" Can't stat file \n "),
unix_error_string (errno), " ", 0);
}
if (S_ISDIR (view->s.st_mode) || S_ISSOCK (view->s.st_mode)
|| S_ISFIFO (view->s.st_mode)){
close_view_file (view);
return set_view_init_error (view, _(" Can't view: not a regular file "));
}
if (view->s.st_size == 0){
/* Must be one of those nice files that grow (/proc) */
close_view_file (view);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -