mc/src/view.c

3402 lines
85 KiB
C
Raw Normal View History

/*
Internal file viewer for the Midnight Commander
1998-02-27 07:54:42 +03:00
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
1998-02-27 07:54:42 +03:00
1997 Norbert Warmuth
1998 Pavel Machek
2004 Roland Illig <roland.illig@gmx.de>
2005-02-08 01:31:30 +03:00
2005 Roland Illig <roland.illig@gmx.de>
1998-02-27 07:54:42 +03:00
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.
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
1998-02-27 07:54:42 +03:00
2005-02-08 01:31:30 +03:00
#ifdef HAVE_CONFIG_H
# include <config.h>
1998-02-27 07:54:42 +03:00
#endif
2005-02-08 01:31:30 +03:00
#include <assert.h>
2005-02-08 01:31:30 +03:00
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
2005-02-08 12:04:03 +03:00
#include <stdlib.h>
1998-02-27 07:54:42 +03:00
#include <string.h>
2005-02-08 01:31:30 +03:00
#include <sys/types.h>
1998-02-27 07:54:42 +03:00
#include <sys/stat.h>
2005-02-08 12:04:03 +03:00
#include <unistd.h>
#include "global.h"
#include "tty.h"
#include "cmd.h" /* For view_other_cmd */
#include "dialog.h" /* Needed by widget.h */
1998-02-27 07:54:42 +03:00
#include "widget.h" /* Needed for buttonbar_new */
#include "color.h"
#include "mouse.h"
#include "help.h"
#include "key.h" /* For mi_getch() */
#include "layout.h"
#include "setup.h"
1998-02-27 07:54:42 +03:00
#include "wtools.h" /* For query_set_sel() */
#include "dir.h"
2002-10-31 01:48:59 +03:00
#include "panel.h" /* Needed for current_panel and other_panel */
1998-02-27 07:54:42 +03:00
#include "win.h"
#include "execute.h"
#include "main.h" /* slow_terminal */
1998-02-27 07:54:42 +03:00
#include "view.h"
#include "charsets.h"
#include "selcodepage.h"
1998-02-27 07:54:42 +03:00
/* Block size for reading files in parts */
#define VIEW_PAGE_SIZE ((size_t) 8192)
#define VIEW_COORD_CACHE_GRANUL 1024
1998-02-27 07:54:42 +03:00
typedef unsigned char byte;
/* Offset in bytes into a file */
typedef unsigned long offset_type;
#define INVALID_OFFSET ((offset_type) -1)
#define OFFSETTYPE_MAX (~((offset_type) 0))
#define OFFSETTYPE_PRIX "lX"
#define OFFSETTYPE_PRId "lu"
/* A width or height on the screen */
typedef unsigned int screen_dimen;
/* A cache entry for mapping offsets into line/column pairs and vice versa.
* cc_offset, cc_line, and cc_column are the 0-based values of the offset,
* line and column of that cache entry. cc_nroff_column is the column
* corresponding to cc_offset in nroff mode.
*/
struct coord_cache_entry {
offset_type cc_offset;
offset_type cc_line;
offset_type cc_column;
offset_type cc_nroff_column;
};
/* A node for building a change list on change_list */
struct hexedit_change_node {
struct hexedit_change_node *next;
offset_type offset;
byte value;
};
/* data sources of the view */
enum view_ds {
DS_NONE, /* No data available */
DS_STDIO_PIPE, /* Data comes from a pipe using popen/pclose */
DS_VFS_PIPE, /* Data comes from a piped-in VFS file */
DS_FILE, /* Data comes from a VFS file */
DS_STRING /* Data comes from a string in memory */
};
struct area {
screen_dimen top, left;
screen_dimen height, width;
};
struct WView {
Widget widget;
char *filename; /* Name of the file */
char *command; /* Command used to pipe data in */
enum view_ds datasource; /* Where the displayed data comes from */
/* stdio pipe data source */
FILE *ds_stdio_pipe; /* Output of a shell command */
/* vfs pipe data source */
int ds_vfs_pipe; /* Non-seekable vfs file descriptor */
/* vfs file data source */
int ds_file_fd; /* File with random access */
off_t ds_file_filesize; /* Size of the file */
off_t ds_file_offset; /* Offset of the currently loaded data */
byte *ds_file_data; /* Currently loaded data */
size_t ds_file_datalen; /* Number of valid bytes in file_data */
size_t ds_file_datasize; /* Number of allocated bytes in file_data */
/* string data source */
byte *ds_string_data; /* The characters of the string */
size_t ds_string_len; /* The length of the string */
/* Growing buffers information */
gboolean growbuf_in_use; /* Use the growing buffers? */
byte **growbuf_blockptr; /* Pointer to the block pointers */
size_t growbuf_blocks; /* The number of blocks in *block_ptr */
size_t growbuf_lastindex; /* Number of bytes in the last page of the
growing buffer */
gboolean growbuf_finished; /* TRUE when all data has been read. */
/* Editor modes */
gboolean hex_mode; /* Hexview or Hexedit */
gboolean hexedit_mode; /* Hexedit */
gboolean hexview_in_text; /* Is the hexview cursor in the text area? */
gboolean text_nroff_mode; /* Nroff-style highlighting */
gboolean text_wrap_mode; /* Wrap text lines to fit them on the screen */
gboolean magic_mode; /* Preprocess the file using external programs */
/* Additional editor state */
gboolean hexedit_lownibble; /* Are we editing the last significant nibble? */
GArray *coord_cache; /* Cache for mapping offsets to cursor positions */
/* Display information */
screen_dimen dpy_frame_size;/* Size of the frame surrounding the real viewer */
offset_type dpy_topleft; /* Offset of the byte in the top left corner */
offset_type dpy_text_column;/* Number of skipped columns in non-wrap
* text mode */
2005-05-21 14:21:35 +04:00
gboolean dpy_complete; /* Has the last call to display() reached the
* end of file? */
offset_type hex_cursor; /* Hexview cursor position in file */
screen_dimen cursor_col; /* Cursor column */
screen_dimen cursor_row; /* Cursor row */
struct hexedit_change_node *change_list; /* Linked list of changes */
struct area status_area; /* Where the status line is displayed */
struct area ruler_area; /* Where the ruler is displayed */
struct area data_area; /* Where the data is displayed */
int dirty; /* Number of skipped updates */
/* Mode variables */
int bytes_per_line; /* Number of bytes per line in hex mode */
/* Search variables */
offset_type search_start; /* First character to start searching from */
offset_type found_len; /* Length of found string or 0 if none was found */
char *search_exp; /* The search expression */
int direction; /* 1= forward; -1 backward */
void (*last_search)(WView *, char *);
/* Pointer to the last search command */
gboolean want_to_quit; /* Prepare for cleanup ... */
/* Markers */
int marker; /* mark to use */
offset_type marks [10]; /* 10 marks: 0..9 */
int move_dir; /* return value from widget:
* 0 do nothing
* -1 view previous file
* 1 view next file
*/
offset_type update_steps; /* The number of bytes between percent
* increments */
offset_type update_activate;/* Last point where we updated the status */
};
/* {{{ Global Variables }}} */
1998-02-27 07:54:42 +03:00
/* Maxlimit for skipping updates */
int max_dirt_limit = 10;
1998-02-27 07:54:42 +03:00
/* If set, show a ruler */
static enum {
RULER_NONE,
RULER_TOP,
RULER_BOTTOM
} ruler = RULER_NONE;
1998-02-27 07:54:42 +03:00
/* Scrolling is done in pages or line increments */
int mouse_move_pages_viewer = 1;
/* wrap mode default */
int global_wrap_mode = 1;
1998-02-27 07:54:42 +03:00
int default_hex_mode = 0;
static int default_hexedit_mode = 0;
1998-02-27 07:54:42 +03:00
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;
1999-12-10 19:04:35 +03:00
static const char hex_char[] = "0123456789ABCDEF";
1998-02-27 07:54:42 +03:00
int mcview_remember_file_position = FALSE;
/* {{{ Function Prototypes }}} */
/* Our widget callback */
static cb_ret_t view_callback (Widget *, widget_msg_t, int);
2002-10-31 01:48:59 +03:00
static int regexp_view_search (WView * view, char *pattern, char *string,
int match_type);
static void view_labels (WView * view);
static void view_init_growbuf (WView *);
static void view_place_cursor (WView *view);
static void display (WView *);
/* {{{ Helper Functions }}} */
/* difference or zero */
static inline screen_dimen
dimen_doz (screen_dimen a, screen_dimen b)
{
return (a >= b) ? a - b : 0;
}
static inline screen_dimen
dimen_min (screen_dimen a, screen_dimen b)
{
return (a < b) ? a : b;
}
static inline offset_type
offset_doz (offset_type a, offset_type b)
{
return (a >= b) ? a - b : 0;
}
2005-06-28 02:36:00 +04:00
static inline offset_type
offset_rounddown (offset_type a, offset_type b)
{
assert (b != 0);
return a - a % b;
}
/* {{{ Simple Primitive Functions for WView }}} */
static inline gboolean
view_is_in_panel (WView *view)
{
return (view->dpy_frame_size != 0);
}
static void
view_compute_areas (WView *view)
{
struct area view_area;
screen_dimen height, rest, y;
/* The viewer is surrounded by a frame of size view->dpy_frame_size.
* Inside that frame, there are: The status line (at the top),
* the data area and an optional ruler, which is shown above or
* below the data area. */
view_area.top = view->dpy_frame_size;
view_area.left = view->dpy_frame_size;
view_area.height = dimen_doz(view->widget.lines, 2 * view->dpy_frame_size);
view_area.width = dimen_doz(view->widget.cols, 2 * view->dpy_frame_size);
/* Most coordinates of the areas equal those of the whole viewer */
view->status_area = view_area;
view->ruler_area = view_area;
view->data_area = view_area;
/* Compute the heights of the areas */
rest = view_area.height;
height = dimen_min(rest, 1);
view->status_area.height = height;
rest -= height;
height = dimen_min(rest, (ruler == RULER_NONE || view->hex_mode) ? 0 : 2);
view->ruler_area.height = height;
rest -= height;
view->data_area.height = rest;
/* Compute the position of the areas */
y = view->status_area.top + view->status_area.height;
if (ruler == RULER_TOP) {
view->ruler_area.top = y;
y += view->ruler_area.height;
}
view->data_area.top = y;
y += view->data_area.height;
if (ruler == RULER_BOTTOM) {
view->ruler_area.top = y;
y += view->ruler_area.height;
}
}
/* {{{ Growing buffer }}} */
static void
view_init_growbuf (WView *view)
{
view->growbuf_in_use = TRUE;
view->growbuf_blockptr = NULL;
view->growbuf_blocks = 0;
view->growbuf_lastindex = 0;
view->growbuf_finished = FALSE;
}
static void
view_growbuf_free (WView *view)
{
size_t i;
assert (view->growbuf_in_use);
for (i = 0; i < view->growbuf_blocks; i++)
g_free (view->growbuf_blockptr[i]);
g_free (view->growbuf_blockptr);
view->growbuf_blockptr = NULL;
view->growbuf_in_use = FALSE;
}
static offset_type
view_growbuf_filesize (WView *view)
{
assert(view->growbuf_in_use);
if (view->growbuf_blocks == 0)
return 0;
else
return ((offset_type) view->growbuf_blocks - 1)
* VIEW_PAGE_SIZE + view->growbuf_lastindex;
}
/* Copies the output from the pipe to the growing buffer, until either
* the end-of-pipe is reached or the interval [0..ofs) or the growing
* buffer is completely filled. */
static void
view_growbuf_read_until (WView *view, offset_type ofs)
{
ssize_t nread;
byte *p;
size_t bytesfree;
gboolean short_read;
assert (view->growbuf_in_use);
if (view->growbuf_finished)
return;
short_read = FALSE;
while (view_growbuf_filesize (view) < ofs || short_read) {
if (view->growbuf_blocks == 0 || view->growbuf_lastindex == VIEW_PAGE_SIZE) {
/* Append a new block to the growing buffer */
byte *newblock = g_try_malloc (VIEW_PAGE_SIZE);
byte **newblocks = g_try_malloc (sizeof (*newblocks) * (view->growbuf_blocks + 1));
if (!newblock || !newblocks) {
g_free (newblock);
g_free (newblocks);
return;
}
memcpy (newblocks, view->growbuf_blockptr, sizeof (*newblocks) * view->growbuf_blocks);
g_free (view->growbuf_blockptr);
view->growbuf_blockptr = newblocks;
view->growbuf_blockptr[view->growbuf_blocks++] = newblock;
view->growbuf_lastindex = 0;
}
p = view->growbuf_blockptr[view->growbuf_blocks - 1] + view->growbuf_lastindex;
bytesfree = VIEW_PAGE_SIZE - view->growbuf_lastindex;
if (view->datasource == DS_STDIO_PIPE) {
nread = fread (p, 1, bytesfree, view->ds_stdio_pipe);
if (nread == 0) {
view->growbuf_finished = TRUE;
(void) pclose (view->ds_stdio_pipe);
display (view);
close_error_pipe (0, NULL);
view->ds_stdio_pipe = NULL;
return;
}
} else {
assert (view->datasource == DS_VFS_PIPE);
do {
nread = mc_read (view->ds_vfs_pipe, p, bytesfree);
} while (nread == -1 && errno == EINTR);
if (nread == -1 || nread == 0) {
view->growbuf_finished = TRUE;
(void) mc_close (view->ds_vfs_pipe);
view->ds_vfs_pipe = -1;
return;
}
}
short_read = (nread < bytesfree);
view->growbuf_lastindex += nread;
}
}
static int
get_byte_growing_buffer (WView *view, offset_type byte_index)
{
offset_type pageno = byte_index / VIEW_PAGE_SIZE;
offset_type pageindex = byte_index % VIEW_PAGE_SIZE;
assert (view->growbuf_in_use);
if ((size_t) pageno != pageno)
return -1;
view_growbuf_read_until (view, byte_index + 1);
if (view->growbuf_blocks == 0)
return -1;
if (pageno < view->growbuf_blocks - 1)
return view->growbuf_blockptr[pageno][pageindex];
if (pageno == view->growbuf_blocks - 1 && pageindex < view->growbuf_lastindex)
return view->growbuf_blockptr[pageno][pageindex];
return -1;
}
/* {{{ Data sources }}} */
/*
The data source provides the viewer with data from either a file, a
string or the output of a command. The get_byte() function can be
used to get the value of a byte at a specific offset. If the offset
is out of range, -1 is returned. The function get_byte_indexed(a,b)
returns the byte at the offset a+b, or -1 if a+b is out of range.
The view_set_byte() function has the effect that later calls to
get_byte() will return the specified byte for this offset. This
function is designed only for use by the hexedit component after
saving its changes. Inspect the source before you want to use it for
other purposes.
The view_get_filesize() function returns the current size of the
data source. If the growing buffer is used, this size may increase
later on. Use the view_may_still_grow() function when you want to
know if the size can change later.
*/
static offset_type
view_get_filesize (WView *view)
{
switch (view->datasource) {
case DS_NONE:
return 0;
case DS_STDIO_PIPE:
case DS_VFS_PIPE:
return view_growbuf_filesize (view);
case DS_FILE:
return view->ds_file_filesize;
case DS_STRING:
return view->ds_string_len;
default:
assert(!"Unknown datasource type");
return 0;
}
}
2005-06-28 02:36:00 +04:00
static inline gboolean
view_may_still_grow (WView *view)
{
return (view->growbuf_in_use && !view->growbuf_finished);
}
/* returns TRUE if the idx lies in the half-open interval
* [offset; offset + size), FALSE otherwise.
*/
static inline gboolean
already_loaded (offset_type offset, offset_type idx, size_t size)
{
return (offset <= idx && idx - offset < size);
}
static inline void
view_file_load_data (WView *view, offset_type byte_index)
{
offset_type blockoffset;
ssize_t res;
size_t bytes_read;
2005-07-01 03:16:14 +04:00
assert (view->datasource == DS_FILE);
if (already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
return;
blockoffset = offset_rounddown (byte_index, view->ds_file_datasize);
if (mc_lseek (view->ds_file_fd, blockoffset, SEEK_SET) == -1)
goto error;
bytes_read = 0;
while (bytes_read < view->ds_file_datasize) {
res = mc_read (view->ds_file_fd, view->ds_file_data + bytes_read, view->ds_file_datasize - bytes_read);
if (res == -1)
goto error;
if (res == 0)
break;
bytes_read += (size_t) res;
}
view->ds_file_offset = blockoffset;
if (bytes_read > view->ds_file_filesize - view->ds_file_offset) {
/* the file has grown in the meantime -- stick to the old size */
view->ds_file_datalen = view->ds_file_filesize - view->ds_file_offset;
} else {
view->ds_file_datalen = bytes_read;
}
return;
error:
view->ds_file_datalen = 0;
}
static int
get_byte_none (WView *view, offset_type byte_index)
{
assert (view->datasource == DS_NONE);
(void) &view;
(void) byte_index;
return -1;
}
static inline int
get_byte_file (WView *view, offset_type byte_index)
{
assert (view->datasource == DS_FILE);
view_file_load_data (view, byte_index);
if (already_loaded(view->ds_file_offset, byte_index, view->ds_file_datalen))
return view->ds_file_data[byte_index - view->ds_file_offset];
return -1;
}
static int
get_byte_string (WView *view, offset_type byte_index)
{
assert (view->datasource == DS_STRING);
if (byte_index < view->ds_string_len)
return view->ds_string_data[byte_index];
return -1;
}
static inline int
get_byte (WView *view, offset_type offset)
{
switch (view->datasource) {
case DS_STDIO_PIPE:
case DS_VFS_PIPE:
return get_byte_growing_buffer (view, offset);
case DS_FILE:
return get_byte_file (view, offset);
case DS_STRING:
return get_byte_string (view, offset);
case DS_NONE:
return get_byte_none (view, offset);
}
assert(!"Unknown datasource type");
return -1;
}
static inline int
get_byte_indexed (WView *view, offset_type base, offset_type ofs)
{
if (base <= OFFSETTYPE_MAX - ofs)
return get_byte (view, base + ofs);
return -1;
}
static void
view_set_byte (WView *view, offset_type offset, byte b)
{
(void) &b;
assert (offset < view_get_filesize (view));
assert (view->datasource == DS_FILE);
view->ds_file_datalen = 0; /* just force reloading */
}
static void
view_set_datasource_none (WView *view)
{
view->datasource = DS_NONE;
}
static void
view_set_datasource_vfs_pipe (WView *view, int fd)
{
assert (fd != -1);
view->datasource = DS_VFS_PIPE;
view->ds_vfs_pipe = fd;
view_init_growbuf (view);
}
static void
view_set_datasource_stdio_pipe (WView *view, FILE *fp)
{
assert (fp != NULL);
view->datasource = DS_STDIO_PIPE;
view->ds_stdio_pipe = fp;
view_init_growbuf (view);
}
static void
view_set_datasource_string (WView *view, const char *s)
{
view->datasource = DS_STRING;
view->ds_string_data = (byte *) g_strdup (s);
view->ds_string_len = strlen (s);
}
static void
view_set_datasource_file (WView *view, int fd, const struct stat *st)
{
view->datasource = DS_FILE;
view->ds_file_fd = fd;
view->ds_file_filesize = st->st_size;
view->ds_file_offset = 0;
view->ds_file_data = g_malloc (4096);
view->ds_file_datalen = 0;
view->ds_file_datasize = 4096;
}
static void
view_close_datasource (WView *view)
{
switch (view->datasource) {
case DS_NONE:
break;
case DS_STDIO_PIPE:
if (view->ds_stdio_pipe != NULL) {
(void) pclose (view->ds_stdio_pipe);
display (view);
close_error_pipe (0, NULL);
view->ds_stdio_pipe = NULL;
}
view_growbuf_free (view);
break;
case DS_VFS_PIPE:
if (view->ds_vfs_pipe != -1) {
(void) mc_close (view->ds_vfs_pipe);
view->ds_vfs_pipe = -1;
}
view_growbuf_free (view);
break;
case DS_FILE:
(void) mc_close (view->ds_file_fd);
view->ds_file_fd = -1;
g_free (view->ds_file_data);
view->ds_file_data = NULL;
break;
case DS_STRING:
g_free (view->ds_string_data);
view->ds_string_data = NULL;
break;
default:
assert (!"Unknown datasource type");
}
view->datasource = DS_NONE;
}
/* {{{ The Coordinate Cache }}} */
/*
This cache provides you with a fast lookup to map file offsets into
line/column pairs and vice versa. The interface to the mapping is
provided by the functions view_coord_to_offset() and
view_offset_to_coord().
The cache is implemented as a simple sorted array holding entries
that map some of the offsets to their line/column pair. Entries that
are not cached themselves are interpolated (exactly) from their
neighbor entries. The algorithm used for determining the line/column
for a specific offset needs to be kept synchronized with the one used
in display().
*/
enum ccache_type {
CCACHE_OFFSET,
CCACHE_LINECOL
};
static inline gboolean
coord_cache_entry_less (const struct coord_cache_entry *a,
const struct coord_cache_entry *b, enum ccache_type crit,
gboolean nroff_mode)
{
if (crit == CCACHE_OFFSET)
return (a->cc_offset < b->cc_offset);
if (a->cc_line < b->cc_line)
return TRUE;
if (a->cc_line == b->cc_line) {
if (nroff_mode) {
return (a->cc_nroff_column < b->cc_nroff_column);
} else {
return (a->cc_column < b->cc_column);
}
}
return FALSE;
}
#ifdef MC_ENABLE_DEBUGGING_CODE
static void view_coord_to_offset (WView *, offset_type *, offset_type, offset_type);
static void view_offset_to_coord (WView *, offset_type *, offset_type *, offset_type);
static void
view_ccache_dump (WView *view)
{
FILE *f;
offset_type offset, line, column, nextline_offset, filesize;
guint i;
const struct coord_cache_entry *cache;
assert (view->coord_cache != NULL);
filesize = view_get_filesize (view);
cache = &(g_array_index (view->coord_cache, struct coord_cache_entry, 0));
f = fopen("mcview-ccache.out", "w");
if (f == NULL)
return;
(void)setvbuf(f, NULL, _IONBF, 0);
/* cache entries */
for (i = 0; i < view->coord_cache->len; i++) {
(void) fprintf (f,
"entry %8u "
"offset %8"OFFSETTYPE_PRId" "
"line %8"OFFSETTYPE_PRId" "
"column %8"OFFSETTYPE_PRId" "
"nroff_column %8"OFFSETTYPE_PRId"\n",
(unsigned int) i, cache[i].cc_offset, cache[i].cc_line,
cache[i].cc_column, cache[i].cc_nroff_column);
}
(void)fprintf (f, "\n");
/* offset -> line/column translation */
for (offset = 0; offset < filesize; offset++) {
view_offset_to_coord (view, &line, &column, offset);
(void)fprintf (f,
"offset %8"OFFSETTYPE_PRId" "
"line %8"OFFSETTYPE_PRId" "
"column %8"OFFSETTYPE_PRId"\n",
offset, line, column);
}
/* line/column -> offset translation */
for (line = 0; TRUE; line++) {
view_coord_to_offset (view, &nextline_offset, line + 1, 0);
(void)fprintf (f, "nextline_offset %8"OFFSETTYPE_PRId"\n",
nextline_offset);
for (column = 0; TRUE; column++) {
view_coord_to_offset (view, &offset, line, column);
if (offset >= nextline_offset)
break;
(void)fprintf (f, "line %8"OFFSETTYPE_PRId" column %8"OFFSETTYPE_PRId" offset %8"OFFSETTYPE_PRId"\n",
line, column, offset);
}
if (nextline_offset >= filesize - 1)
break;
}
(void)fclose (f);
}
#endif
2005-06-28 02:36:00 +04:00
static inline gboolean
is_nroff_sequence (WView *view, offset_type offset)
{
int c0, c1, c2;
/* The following commands are ordered to speed up the calculation. */
c1 = get_byte_indexed (view, offset, 1);
if (c1 == -1 || c1 != '\b')
return FALSE;
c0 = get_byte_indexed (view, offset, 0);
if (c0 == -1 || !is_printable(c0))
return FALSE;
c2 = get_byte_indexed (view, offset, 2);
if (c2 == -1 || !is_printable(c2))
return FALSE;
return (c0 == c2 || c0 == '_' || (c0 == '+' && c2 == 'o'));
}
/* Find and return the index of the last cache entry that is
* smaller than ''coord'', according to the criterion ''sort_by''. */
static inline guint
view_ccache_find (WView *view, const struct coord_cache_entry *cache,
const struct coord_cache_entry *coord, enum ccache_type sort_by)
{
guint base, i, limit;
limit = view->coord_cache->len;
assert (limit != 0);
base = 0;
while (limit > 1) {
i = base + limit / 2;
if (coord_cache_entry_less (coord, &cache[i], sort_by, view->text_nroff_mode)) {
/* continue the search in the lower half of the cache */
} else {
/* continue the search in the upper half of the cache */
base = i;
}
limit = (limit + 1) / 2;
}
return base;
}
/* Look up the missing components of ''coord'', which are given by
* ''lookup_what''. The function returns the smallest value that
* matches the existing components of ''coord''.
*/
static void
view_ccache_lookup (WView *view, struct coord_cache_entry *coord,
enum ccache_type lookup_what)
{
guint i;
struct coord_cache_entry *cache, current, next, entry;
enum ccache_type sorter;
offset_type limit;
enum {
NROFF_START,
NROFF_BACKSPACE,
NROFF_CONTINUATION
} nroff_state;
if (!view->coord_cache) {
view->coord_cache = g_array_new (FALSE, FALSE, sizeof(struct coord_cache_entry));
current.cc_offset = 0;
current.cc_line = 0;
current.cc_column = 0;
current.cc_nroff_column = 0;
g_array_append_val (view->coord_cache, current);
}
sorter = (lookup_what == CCACHE_OFFSET) ? CCACHE_LINECOL : CCACHE_OFFSET;
retry:
/* find the two neighbor entries in the cache */
cache = &(g_array_index (view->coord_cache, struct coord_cache_entry, 0));
i = view_ccache_find (view, cache, coord, sorter);
/* now i points to the lower neighbor in the cache */
current = cache[i];
if (i + 1 < view->coord_cache->len)
limit = cache[i + 1].cc_offset;
else
limit = current.cc_offset + VIEW_COORD_CACHE_GRANUL;
entry = current;
nroff_state = NROFF_START;
for (; current.cc_offset < limit; current = next) {
int c;
if ((c = get_byte (view, current.cc_offset)) == -1)
break;
if (!coord_cache_entry_less (&current, coord, sorter, view->text_nroff_mode)) {
if (lookup_what == CCACHE_OFFSET
&& view->text_nroff_mode
&& nroff_state != NROFF_START) {
/* don't break here */
} else {
break;
}
}
/* Provide useful default values for ''next'' */
next.cc_offset = current.cc_offset + 1;
next.cc_line = current.cc_line;
next.cc_column = current.cc_column + 1;
next.cc_nroff_column = current.cc_nroff_column + 1;
/* and override some of them as necessary. */
if (c == '\r') {
next.cc_column = current.cc_column;
next.cc_nroff_column = current.cc_nroff_column;
} else if (nroff_state == NROFF_BACKSPACE) {
next.cc_nroff_column = current.cc_nroff_column - 1;
} else if (c == '\t') {
next.cc_column = offset_rounddown (current.cc_column, 8) + 8;
next.cc_nroff_column =
offset_rounddown (current.cc_nroff_column, 8) + 8;
} else if (c == '\n') {
next.cc_line = current.cc_line + 1;
next.cc_column = 0;
next.cc_nroff_column = 0;
} else {
/* Use all default values from above */
}
switch (nroff_state) {
case NROFF_START:
case NROFF_CONTINUATION:
if (is_nroff_sequence (view, current.cc_offset))
nroff_state = NROFF_BACKSPACE;
else
nroff_state = NROFF_START;
break;
case NROFF_BACKSPACE:
nroff_state = NROFF_CONTINUATION;
break;
}
/* Cache entries must guarantee that for each i < j,
* line[i] <= line[j] and column[i] < column[j]. In the case of
* nroff sequences and '\r' characters, this is not guaranteed,
* so we cannot save them. */
if (nroff_state == NROFF_START && c != '\r')
entry = next;
}
if (i + 1 == view->coord_cache->len && entry.cc_offset != cache[i].cc_offset) {
g_array_append_val (view->coord_cache, entry);
goto retry;
}
if (lookup_what == CCACHE_OFFSET) {
coord->cc_offset = current.cc_offset;
} else {
coord->cc_line = current.cc_line;
coord->cc_column = current.cc_column;
coord->cc_nroff_column = current.cc_nroff_column;
}
}
static void
view_coord_to_offset (WView *view, offset_type *ret_offset,
offset_type line, offset_type column)
{
struct coord_cache_entry coord;
coord.cc_line = line;
coord.cc_column = column;
coord.cc_nroff_column = column;
view_ccache_lookup (view, &coord, CCACHE_OFFSET);
*ret_offset = coord.cc_offset;
}
static void
view_offset_to_coord (WView *view, offset_type *ret_line,
offset_type *ret_column, offset_type offset)
{
struct coord_cache_entry coord;
coord.cc_offset = offset;
view_ccache_lookup (view, &coord, CCACHE_LINECOL);
*ret_line = coord.cc_line;
*ret_column = (view->text_nroff_mode)
? coord.cc_nroff_column
: coord.cc_column;
}
/* {{{ Cursor Movement }}} */
/*
The following variables have to do with the current position and are
updated by the cursor movement functions.
In all viewer modes, dpy_topleft marks the offset of the top-left
corner on the screen. In hex mode, hex_cursor is the offset of the
cursor. In non-wrapping text mode, dpy_text_column is the number of
2005-06-06 03:43:49 +04:00
columns that are hidden on the left side on the screen.
In hex mode, dpy_topleft is updated by the view_fix_cursor_position()
function in order to keep the other functions simple. In text
non-wrap mode dpy_topleft and dpy_text_column are normalized such
that dpy_text_column < view_get_datacolumns().
*/
/* prototypes for functions used by view_moveto_bottom() */
static void view_move_up (WView *, offset_type);
static void view_moveto_bol (WView *);
static void
view_fix_cursor_position (WView *view)
{
if (view->hex_mode) {
const offset_type bytes = view->bytes_per_line;
const offset_type displaysize = view->data_area.height * bytes;
const offset_type cursor = view->hex_cursor;
offset_type topleft = view->dpy_topleft;
if (topleft + displaysize <= cursor)
topleft = offset_rounddown (cursor, bytes)
- (displaysize - bytes);
if (cursor < topleft)
topleft = offset_rounddown (cursor, bytes);
view->dpy_topleft = topleft;
} else if (view->text_wrap_mode) {
offset_type line, col, columns;
columns = view->data_area.width;
view_offset_to_coord (view, &line, &col, view->dpy_topleft + view->dpy_text_column);
if (columns != 0)
col = offset_rounddown (col, columns);
view_coord_to_offset (view, &(view->dpy_topleft), line, col);
view->dpy_text_column = 0;
} else {
/* nothing to do */
}
}
static void
view_movement_fixups (WView *view, gboolean reset_search)
{
view_fix_cursor_position (view);
if (reset_search) {
view->search_start = view->dpy_topleft;
view->found_len = 0;
}
view->dirty++;
}
static void
view_moveto_top (WView *view)
{
view->dpy_topleft = 0;
if (view->hex_mode) {
view->hex_cursor = 0;
} else if (view->text_wrap_mode) {
/* nothing to do */
} else {
view->dpy_text_column = 0;
}
view_movement_fixups (view, TRUE);
}
static void
view_moveto_bottom (WView *view)
{
offset_type datalines, lines_up, filesize, last_offset;
if (view->growbuf_in_use)
view_growbuf_read_until (view, OFFSETTYPE_MAX);
filesize = view_get_filesize (view);
last_offset = offset_doz(filesize, 1);
datalines = view->data_area.height;
lines_up = offset_doz(datalines, 1);
if (view->hex_mode) {
view->hex_cursor = filesize;
view_move_up (view, lines_up);
view->hex_cursor = last_offset;
} else {
view->dpy_topleft = last_offset;
view_moveto_bol (view);
view_move_up (view, lines_up);
}
view_movement_fixups (view, TRUE);
}
static void
view_moveto_bol (WView *view)
{
if (view->hex_mode) {
view->hex_cursor -= view->hex_cursor % view->bytes_per_line;
} else if (view->text_wrap_mode) {
/* do nothing */
} else {
offset_type line, column;
view_offset_to_coord (view, &line, &column, view->dpy_topleft);
view_coord_to_offset (view, &(view->dpy_topleft), line, 0);
view->dpy_text_column = 0;
}
view_movement_fixups (view, TRUE);
}
static void
view_moveto_eol (WView *view)
{
if (view->hex_mode) {
offset_type filesize, bol;
bol = offset_rounddown (view->hex_cursor, view->bytes_per_line);
if (get_byte_indexed (view, bol, view->bytes_per_line - 1) != -1) {
view->hex_cursor = bol + view->bytes_per_line - 1;
} else {
filesize = view_get_filesize (view);
view->hex_cursor = offset_doz(filesize, 1);
}
} else if (view->text_wrap_mode) {
/* nothing to do */
} else {
offset_type line, col;
view_offset_to_coord (view, &line, &col, view->dpy_topleft);
view_coord_to_offset (view, &(view->dpy_topleft), line, OFFSETTYPE_MAX);
}
view_movement_fixups (view, FALSE);
}
static void
view_moveto_offset (WView *view, offset_type offset)
{
if (view->hex_mode) {
view->hex_cursor = offset;
view->dpy_topleft = offset - offset % view->bytes_per_line;
} else {
view->dpy_topleft = offset;
}
view_movement_fixups (view, TRUE);
}
static void
view_moveto (WView *view, offset_type line, offset_type col)
{
offset_type offset;
view_coord_to_offset (view, &offset, line, col);
view_moveto_offset (view, offset);
}
static void
view_move_up (WView *view, offset_type lines)
{
if (view->hex_mode) {
offset_type bytes = lines * view->bytes_per_line;
if (view->hex_cursor >= bytes) {
view->hex_cursor -= bytes;
view->dpy_topleft = offset_doz (view->dpy_topleft, bytes);
} else {
view->hex_cursor %= view->bytes_per_line;
}
} else if (view->text_wrap_mode) {
const screen_dimen width = view->data_area.width;
offset_type i, col, line, linestart;
for (i = 0; i < lines; i++) {
view_offset_to_coord (view, &line, &col, view->dpy_topleft);
if (col >= width) {
col -= width;
} else if (line >= 1) {
view_coord_to_offset (view, &linestart, line, 0);
view_offset_to_coord (view, &line, &col, linestart - 1);
/* if the only thing that would be displayed were a
* single newline character, advance to the previous
* part of the line. */
if (col > 0 && col % width == 0)
col -= width;
else
col -= col % width;
} else {
/* nothing to do */
}
view_coord_to_offset (view, &(view->dpy_topleft), line, col);
}
} else {
offset_type line, column;
view_offset_to_coord (view, &line, &column, view->dpy_topleft);
line = offset_doz(line, lines);
view_coord_to_offset (view, &(view->dpy_topleft), line, column);
}
view_movement_fixups (view, (lines != 1));
}
static void
view_move_down (WView *view, offset_type lines)
{
if (view->hex_mode) {
offset_type i, limit, last_byte;
last_byte = view_get_filesize (view);
if (last_byte >= (offset_type) view->bytes_per_line)
limit = last_byte - view->bytes_per_line;
else
limit = 0;
for (i = 0; i < lines && view->hex_cursor < limit; i++) {
view->hex_cursor += view->bytes_per_line;
if (lines != 1)
view->dpy_topleft += view->bytes_per_line;
}
} else if (view->text_wrap_mode) {
offset_type line, col, i;
for (i = 0; i < lines; i++) {
offset_type new_offset, chk_line, chk_col;
view_offset_to_coord (view, &line, &col, view->dpy_topleft);
col += view->data_area.width;
view_coord_to_offset (view, &new_offset, line, col);
/* skip to the next line if the only thing that would be
* displayed is the newline character. */
view_offset_to_coord (view, &chk_line, &chk_col, new_offset);
if (chk_line == line && chk_col == col
&& get_byte (view, new_offset) == '\n')
new_offset++;
view->dpy_topleft = new_offset;
}
} else {
offset_type line, col;
view_offset_to_coord (view, &line, &col, view->dpy_topleft);
line += lines;
view_coord_to_offset (view, &(view->dpy_topleft), line, col);
}
view_movement_fixups (view, (lines != 1));
}
static void
view_move_left (WView *view, offset_type columns)
{
if (view->hex_mode) {
assert (columns == 1);
if (view->hexview_in_text || !view->hexedit_lownibble) {
if (view->hex_cursor > 0)
view->hex_cursor--;
}
if (!view->hexview_in_text)
view->hexedit_lownibble = !view->hexedit_lownibble;
} else if (view->text_wrap_mode) {
/* nothing to do */
} else {
if (view->dpy_text_column >= columns)
view->dpy_text_column -= columns;
else
view->dpy_text_column = 0;
}
view_movement_fixups (view, FALSE);
}
static void
view_move_right (WView *view, offset_type columns)
{
if (view->hex_mode) {
assert (columns == 1);
if (view->hexview_in_text || view->hexedit_lownibble) {
if (get_byte_indexed (view, view->hex_cursor, 1) != -1)
view->hex_cursor++;
}
if (!view->hexview_in_text)
view->hexedit_lownibble = !view->hexedit_lownibble;
} else if (view->text_wrap_mode) {
/* nothing to do */
} else {
view->dpy_text_column += columns;
}
view_movement_fixups (view, FALSE);
}
/* {{{ Miscellaneous functions }}} */
1998-02-27 07:54:42 +03:00
/* Both views */
1998-12-03 00:27:27 +03:00
static void
1998-02-27 07:54:42 +03:00
view_done (WView *view)
{
if (mcview_remember_file_position && view->filename != NULL) {
char *canon_fname;
offset_type line, col;
canon_fname = vfs_canon (view->filename);
view_offset_to_coord (view, &line, &col, view->dpy_topleft);
save_file_position (canon_fname, line + 1, col);
g_free (canon_fname);
}
view_close_datasource (view);
if (view->coord_cache) {
g_array_free (view->coord_cache, TRUE), view->coord_cache = NULL;
}
g_free (view->filename), view->filename = NULL;
g_free (view->command), view->command = NULL;
1998-02-27 07:54:42 +03:00
default_hex_mode = view->hex_mode;
default_nroff_flag = view->text_nroff_mode;
default_magic_flag = view->magic_mode;
global_wrap_mode = view->text_wrap_mode;
1998-02-27 07:54:42 +03:00
}
static void
view_show_error (WView *view, const char *msg)
1998-02-27 07:54:42 +03:00
{
view_close_datasource (view);
if (view_is_in_panel (view)) {
view_set_datasource_string (view, msg);
} else {
message (1, MSG_ERROR, "%s", msg);
1998-02-27 07:54:42 +03:00
}
}
static gboolean
view_load_command_output (WView *view, const char *command)
1998-02-27 07:54:42 +03:00
{
FILE *fp;
view_close_datasource (view);
1998-02-27 07:54:42 +03:00
open_error_pipe ();
if ((fp = popen (command, "r")) == NULL) {
/* Avoid two messages. Message from stderr has priority. */
display (view);
if (!close_error_pipe (view_is_in_panel (view) ? -1 : 1, NULL))
view_show_error (view, _(" Cannot spawn child process "));
return FALSE;
}
1998-02-27 07:54:42 +03:00
/* First, check if filter produced any output */
view_set_datasource_stdio_pipe (view, fp);
if (get_byte (view, 0) == -1) {
view_close_datasource (view);
/* Avoid two messages. Message from stderr has priority. */
display (view);
if (!close_error_pipe (view_is_in_panel (view) ? -1 : 1, NULL))
view_show_error (view, _("Empty output from child filter"));
return FALSE;
}
return TRUE;
1998-02-27 07:54:42 +03:00
}
gboolean
view_load (WView *view, const char *command, const char *file,
int start_line)
1998-02-27 07:54:42 +03:00
{
int i, type;
int fd = -1;
char tmp[BUF_MEDIUM];
struct stat st;
gboolean retval = FALSE;
1998-02-27 07:54:42 +03:00
assert (view->bytes_per_line != 0);
view_done (view);
1998-02-27 07:54:42 +03:00
/* Set up the state */
view_set_datasource_none (view);
view->filename = g_strdup (file);
1998-02-27 07:54:42 +03:00
view->command = 0;
/* Clear the markers */
view->marker = 0;
for (i = 0; i < 10; i++)
2002-10-31 01:48:59 +03:00
view->marks[i] = 0;
if (!view_is_in_panel (view)) {
view->dpy_text_column = 0;
1998-02-27 07:54:42 +03:00
}
if (command && (view->magic_mode || file[0] == '\0')) {
retval = view_load_command_output (view, command);
} else if (file[0]) {
/* Open the file */
if ((fd = mc_open (file, O_RDONLY | O_NONBLOCK)) == -1) {
g_snprintf (tmp, sizeof (tmp), _(" Cannot open \"%s\"\n %s "),
file, unix_error_string (errno));
view_show_error (view, tmp);
goto finish;
}
/* Make sure we are working with a regular file */
if (mc_fstat (fd, &st) == -1) {
mc_close (fd);
g_snprintf (tmp, sizeof (tmp), _(" Cannot stat \"%s\"\n %s "),
file, unix_error_string (errno));
view_show_error (view, tmp);
goto finish;
}
if (!S_ISREG (st.st_mode)) {
mc_close (fd);
view_show_error (view, _(" Cannot view: not a regular file "));
goto finish;
}
if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1) {
/* Must be one of those nice files that grow (/proc) */
view_set_datasource_vfs_pipe (view, fd);
} else {
type = get_compression_type (fd);
if (view->magic_mode && (type != COMPRESSION_NONE)) {
g_free (view->filename);
view->filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
}
view_set_datasource_file (view, fd, &st);
}
retval = TRUE;
}
1998-02-27 07:54:42 +03:00
2002-10-31 01:48:59 +03:00
finish:
view->command = g_strdup (command);
view->dpy_topleft = 0;
view->search_start = 0;
1998-02-27 07:54:42 +03:00
view->found_len = 0;
view->dpy_text_column = 0;
2002-10-31 01:48:59 +03:00
view->last_search = 0; /* Start a new search */
1998-02-27 07:54:42 +03:00
assert (view->bytes_per_line != 0);
if (mcview_remember_file_position && file != NULL && start_line == 0) {
long line, col;
char *canon_fname;
canon_fname = vfs_canon (file);
load_file_position (file, &line, &col);
g_free (canon_fname);
view_moveto (view, offset_doz(line, 1), col);
} else if (start_line > 0) {
view_moveto (view, start_line - 1, 0);
}
I improved the movement keys of the internal viewer a little bit. Now in wrap mode the End key and cursor up key behave much better (e.g. when viewing binary files with lots of wrapped lines). It's not perfekt but it's better than it used to be. Tue Apr 28 06:52:24 1998 Norbert Warmuth <k3190@fh-sw.de> * gnome/gcmd.c (gnome_open_terminal): Changed my_system(1,...) to my_system(EXECUTE_AS_SHELL,...) Tue Apr 28 06:06:03 1998 Norbert Warmuth <k3190@fh-sw.de> * vfs/extfs.c (extfs_open, extfs_close): Changed my_system(1,...) to my_system(EXECUTE_AS_SHELL,...), this fixes the broken copyin and copyout in 4.1.32. Tue Apr 28 06:11:08 1998 Norbert Warmuth <k3190@fh-sw.de> * view.c (toggle_wrap_mode, toggle_hex_mode): Force recalculation of bottom_first (we mustn't use an already calculated and cached value because it is invalid for the new mode and the End key would not move to the end of the file). * configure.in: Renamed the option `--with-our-slang' to `--with-included-slang' (this one looks better because we also have an `--with-included-gettext'). Make the option `--with-ext2undel' recognice a given path. * cmd.c (view_file_at_line): In plain view (F13) set the default magic flag to zero in order to view the file content unprocessed (esp. don't uncompress files if they are compressed). The view_simple_cmd got broken when the default magic flag in view.c was changed from 0 to 1. * view.c (do_view_init, goto_line): Set wrap mode temporary off to make goto line number work, i.e. `line number' now always means line number in file and not line number on screen (in wrap mode one long line wrapped once is displayed in two lines on the screen). That's important when the viewer is invoked from the find file dialog to display even in wrap mode approxiamtly the part of the file where we found the content we searched for. (move_forward2): In wrap mode lines were sometimes counted wrong causing cursor up to move more than one line. (move_backward2): Fixed the movement in wrap mode. (change_viewer): Always re-init viewer when we have a filename, i. e. if the viewer is invoked with simple_view_cmd then we can switch with the F8 key between unprocessed file content und uncompressed file content. (view_init): re-init view also when magic flag was altered
1998-04-28 18:19:48 +04:00
view->hexedit_lownibble = FALSE;
view->hexview_in_text = FALSE;
1998-02-27 07:54:42 +03:00
view->change_list = NULL;
return retval;
1998-02-27 07:54:42 +03:00
}
/* {{{ Display management }}} */
static void
view_update_bytes_per_line (WView *view)
1998-02-27 07:54:42 +03:00
{
const screen_dimen cols = view->data_area.width;
int bytes;
if (cols < 8)
bytes = 1;
else if (cols < 80)
bytes = ((cols - 8) / 17) * 4;
else
bytes = ((cols - 8) / 18) * 4;
if (bytes == 0)
bytes = 1; /* To avoid division by 0 */
view->bytes_per_line = bytes;
1998-02-27 07:54:42 +03:00
view->dirty = max_dirt_limit + 1; /* To force refresh */
}
static void
view_percent (WView *view, offset_type p)
1998-02-27 07:54:42 +03:00
{
const screen_dimen right = view->status_area.left + view->status_area.width;
const screen_dimen height = view->status_area.height;
1998-02-27 07:54:42 +03:00
int percent;
offset_type filesize;
1998-02-27 07:54:42 +03:00
if (height < 1 || right < 4)
return;
if (view_may_still_grow (view))
return;
filesize = view_get_filesize (view);
if (filesize == 0 || view->dpy_complete)
percent = 100;
else if (p > (INT_MAX / 100))
percent = p / (filesize / 100);
else
percent = p * 100 / filesize;
1998-02-27 07:54:42 +03:00
widget_move (view, view->dpy_frame_size, right - 4);
printw (str_unconst ("%3d%%"), percent);
1998-02-27 07:54:42 +03:00
}
static void
view_display_status (WView *view)
1998-02-27 07:54:42 +03:00
{
const screen_dimen top = view->status_area.top;
const screen_dimen left = view->status_area.left;
const screen_dimen width = view->status_area.width;
const screen_dimen height = view->status_area.height;
const char *file_label, *file_name;
screen_dimen file_label_width;
1998-02-27 07:54:42 +03:00
int i;
if (height < 1)
return;
1998-02-27 07:54:42 +03:00
attrset (SELECTED_COLOR);
widget_move (view, top, left);
hline (' ', width);
file_label = _("File: %s");
file_label_width = strlen (file_label) - 2;
file_name = view->filename ? view->filename
: view->command ? view->command
: "";
if (width < file_label_width + 6)
addstr ((char *) name_trunc (file_name, width));
2002-10-31 01:48:59 +03:00
else {
i = (width > 22 ? 22 : width) - file_label_width;
printw (str_unconst (file_label), name_trunc (file_name, i));
if (width > 46) {
widget_move (view, top, left + 24);
/* FIXME: the format strings need to be changed when offset_type changes */
2002-10-31 01:48:59 +03:00
if (view->hex_mode)
printw (str_unconst (_("Offset 0x%08lx")), view->hex_cursor);
else {
offset_type line, col;
view_offset_to_coord (view, &line, &col, view->dpy_topleft);
printw (str_unconst (_("Line %lu Col %lu")),
(unsigned long) line + 1,
(unsigned long) (view->text_wrap_mode ? col : view->dpy_text_column));
}
2002-10-31 01:48:59 +03:00
}
if (width > 62) {
offset_type filesize;
filesize = view_get_filesize (view);
widget_move (view, top, left + 43);
if (!view_may_still_grow (view)) {
printw (str_unconst (_("%s bytes")), size_trunc (filesize));
} else {
printw (str_unconst (_(">= %s bytes")), size_trunc (filesize));
}
1998-02-27 07:54:42 +03:00
}
if (width > 26) {
view_percent (view, view->hex_mode
? view->hex_cursor
: view->dpy_topleft);
}
1998-02-27 07:54:42 +03:00
}
attrset (SELECTED_COLOR);
}
static inline void
view_display_clean (WView *view)
1998-02-27 07:54:42 +03:00
{
attrset (NORMAL_COLOR);
widget_erase ((Widget *) view);
2005-04-19 01:02:55 +04:00
if (view->dpy_frame_size != 0) {
2002-10-31 01:48:59 +03:00
draw_double_box (view->widget.parent, view->widget.y,
view->widget.x, view->widget.lines,
view->widget.cols);
}
1998-02-27 07:54:42 +03:00
}
#define view_add_character(view,c) addch (c)
#define view_add_one_vline() one_vline()
#define view_add_string(view,s) addstr (s)
#define view_gotoyx(v,r,c) widget_move (v,r,c)
typedef enum {
MARK_NORMAL = 0,
MARK_SELECTED = 1,
MARK_CURSOR = 2,
MARK_CHANGED = 3
} mark_t;
2005-04-19 01:02:55 +04:00
static inline int
view_count_backspaces (WView *view, off_t offset)
{
int backspaces = 0;
while (offset >= 2 * backspaces
&& get_byte (view, offset - 2 * backspaces) == '\b')
backspaces++;
return backspaces;
}
static void
view_display_ruler (WView *view)
{
static const char ruler_chars[] = "|----*----";
const screen_dimen top = view->ruler_area.top;
const screen_dimen left = view->ruler_area.left;
const screen_dimen width = view->ruler_area.width;
const screen_dimen height = view->ruler_area.height;
const screen_dimen line_row = (ruler == RULER_TOP) ? 0 : 1;
const screen_dimen nums_row = (ruler == RULER_TOP) ? 1 : 0;
char r_buff[10];
offset_type cl;
screen_dimen c;
if (ruler == RULER_NONE || height < 1)
return;
attrset (MARKED_COLOR);
for (c = 0; c < width; c++) {
cl = view->dpy_text_column + c;
if (line_row < height) {
view_gotoyx (view, top + line_row, left + c);
view_add_character (view, ruler_chars[cl % 10]);
}
if ((cl != 0) && (cl % 10) == 0) {
g_snprintf (r_buff, sizeof (r_buff), "%"OFFSETTYPE_PRId, cl);
if (nums_row < height) {
widget_move (view, top + nums_row, left + c - 1);
view_add_string (view, r_buff);
}
}
}
attrset (NORMAL_COLOR);
}
static void
view_display_hex (WView *view)
/* FIXME: prevent any screen overflows */
1998-02-27 07:54:42 +03:00
{
const screen_dimen top = view->data_area.top;
const screen_dimen left = view->data_area.left;
const screen_dimen height = view->data_area.height;
const screen_dimen width = view->data_area.width;
const screen_dimen text_start = width - view->bytes_per_line;
const screen_dimen hex_start = 9;
screen_dimen row, col;
offset_type from;
1998-02-27 07:54:42 +03:00
int c;
mark_t boldflag = MARK_NORMAL;
1998-02-27 07:54:42 +03:00
struct hexedit_change_node *curr = view->change_list;
char hex_buff[10]; /* A temporary buffer for sprintf and mvwaddstr */
int bytes; /* Number of bytes already printed on the line */
view_display_clean (view);
1998-02-27 07:54:42 +03:00
/* Find the first displayable changed byte */
from = view->dpy_topleft;
while (curr && (curr->offset < from)) {
curr = curr->next;
1998-02-27 07:54:42 +03:00
}
attrset (NORMAL_COLOR);
for (row = 0; get_byte (view, from) != -1 && row < height; row++) {
/* Print the hex offset */
attrset (MARKED_COLOR);
g_snprintf (hex_buff, sizeof (hex_buff), "%08"OFFSETTYPE_PRIX, from);
view_gotoyx (view, top + row, left);
view_add_string (view, hex_buff);
attrset (NORMAL_COLOR);
col = hex_start;
/* Each hex number is two digits */
hex_buff[2] = '\0';
for (bytes = 0;
bytes < view->bytes_per_line
&& (c = get_byte (view, from)) != -1;
bytes++, from++) {
/* Display and mark changed bytes */
if (curr && from == curr->offset) {
c = curr->value;
curr = curr->next;
boldflag = MARK_CHANGED;
attrset (VIEW_UNDERLINED_COLOR);
}
if (view->found_len && from >= view->search_start
&& from < view->search_start + view->found_len) {
boldflag = MARK_SELECTED;
attrset (MARKED_COLOR);
}
/* Display the navigation cursor */
if (from == view->hex_cursor) {
if (!view->hexview_in_text) {
view->cursor_row = row;
view->cursor_col = col;
}
boldflag = MARK_CURSOR;
attrset (view->hexview_in_text
? MARKED_SELECTED_COLOR
: VIEW_UNDERLINED_COLOR);
}
1998-02-27 07:54:42 +03:00
/* Print a hex number (sprintf is too slow) */
hex_buff[0] = hex_char[(c >> 4)];
hex_buff[1] = hex_char[c & 15];
view_gotoyx (view, top + row, left + col);
view_add_string (view, hex_buff);
col += 3;
/* Turn off the cursor or changed byte highlighting here */
if (boldflag == MARK_CURSOR || boldflag == MARK_CHANGED)
attrset (NORMAL_COLOR);
if ((bytes & 3) == 3 && bytes + 1 < view->bytes_per_line) {
/* Turn off the search highlighting */
if (boldflag == MARK_SELECTED
&& from == view->search_start + view->found_len - 1)
attrset (NORMAL_COLOR);
/* Hex numbers are printed in the groups of four */
/* Groups are separated by a vline */
view_gotoyx (view, top + row, left + col - 1);
view_add_character (view, ' ');
view_gotoyx (view, top + row, left + col);
if (view->data_area.width < 80)
col += 1;
else {
view_add_one_vline ();
col += 2;
}
if (boldflag != MARK_NORMAL
&& from == view->search_start + view->found_len - 1)
attrset (MARKED_COLOR);
}
if (boldflag != MARK_NORMAL
&& from < view->search_start + view->found_len - 1
&& bytes != view->bytes_per_line - 1) {
view_gotoyx (view, top + row, left + col);
view_add_character (view, ' ');
}
/* Print corresponding character on the text side */
view_gotoyx (view, top + row, left + text_start + bytes);
c = convert_to_display_c (c);
if (!is_printable (c))
c = '.';
switch (boldflag) {
case MARK_NORMAL:
break;
case MARK_SELECTED:
attrset (MARKED_COLOR);
break;
case MARK_CURSOR:
if (view->hexview_in_text) {
/* Our side is active */
view->cursor_col = text_start + bytes;
view->cursor_row = row;
attrset (VIEW_UNDERLINED_COLOR);
} else {
/* Other side is active */
attrset (MARKED_SELECTED_COLOR);
1998-02-27 07:54:42 +03:00
}
break;
case MARK_CHANGED:
attrset (VIEW_UNDERLINED_COLOR);
break;
}
view_add_character (view, c);
if (boldflag != MARK_NORMAL) {
boldflag = MARK_NORMAL;
attrset (NORMAL_COLOR);
}
}
}
view_place_cursor (view);
view->dpy_complete = (get_byte (view, from) == -1);
}
static void
view_display_text (WView * view)
{
const screen_dimen left = view->data_area.left;
const screen_dimen top = view->data_area.top;
const screen_dimen width = view->data_area.width;
const screen_dimen height = view->data_area.height;
screen_dimen row, col;
offset_type from;
int c;
mark_t boldflag = MARK_NORMAL;
struct hexedit_change_node *curr = view->change_list;
view_display_clean (view);
view_display_ruler (view);
/* Find the first displayable changed byte */
from = view->dpy_topleft;
while (curr && (curr->offset < from)) {
curr = curr->next;
}
attrset (NORMAL_COLOR);
for (row = 0, col = 0; row < height && (c = get_byte (view, from)) != -1; from++) {
if (view->text_nroff_mode && c == '\b') {
int c_prev;
int c_next;
if ((c_next = get_byte_indexed (view, from, 1)) != -1
&& is_printable (c_next)
&& from >= 1
&& (c_prev = get_byte (view, from - 1)) != -1
&& is_printable (c_prev)
&& (c_prev == c_next || c_prev == '_'
|| (c_prev == '+' && c_next == 'o'))) {
if (col == 0) {
if (row == 0) {
/* We're inside an nroff character sequence at the
* beginning of the screen -- just skip the
* backspace and continue with the next character. */
continue;
1998-02-27 07:54:42 +03:00
}
row--;
col = width;
1998-02-27 07:54:42 +03:00
}
col--;
boldflag = MARK_SELECTED;
if (c_prev == '_' && (c_next != '_' || view_count_backspaces (view, from) == 1))
attrset (VIEW_UNDERLINED_COLOR);
else
attrset (MARKED_COLOR);
continue;
1998-02-27 07:54:42 +03:00
}
}
if ((c == '\n') || (col >= width && view->text_wrap_mode)) {
col = 0;
row++;
if (c == '\n' || row >= height)
continue;
}
if (c == '\r')
continue;
if (c == '\t') {
offset_type line, column;
view_offset_to_coord (view, &line, &column, from);
col += (8 - column % 8);
if (view->text_wrap_mode && col >= width && width != 0) {
row += col / width;
col %= width;
}
continue;
}
if (view->found_len != 0
&& from >= view->search_start
&& from < view->search_start + view->found_len) {
boldflag = MARK_SELECTED;
attrset (SELECTED_COLOR);
}
if (col >= view->dpy_text_column
&& col - view->dpy_text_column < width) {
view_gotoyx (view, top + row, left + (col - view->dpy_text_column));
c = convert_to_display_c (c);
if (!is_printable (c))
c = '.';
view_add_character (view, c);
}
col++;
if (boldflag != MARK_NORMAL) {
boldflag = MARK_NORMAL;
attrset (NORMAL_COLOR);
}
1998-02-27 07:54:42 +03:00
}
view->dpy_complete = (get_byte (view, from) == -1);
1998-02-27 07:54:42 +03:00
}
/* Displays as much data from view->dpy_topleft as fits on the screen */
static void
display (WView *view)
{
view_compute_areas (view);
if (view->hex_mode) {
view_display_hex (view);
} else {
view_display_text (view);
}
view_display_status (view);
}
1998-12-03 00:27:27 +03:00
static void
1998-02-27 07:54:42 +03:00
view_place_cursor (WView *view)
{
screen_dimen row, col;
1998-02-27 07:54:42 +03:00
row = view->data_area.top + view->cursor_row;
col = view->data_area.left + view->cursor_col;
if (!view->hexview_in_text && view->hexedit_lownibble)
col++;
widget_move (&view->widget, row, col);
1998-02-27 07:54:42 +03:00
}
static void
view_update (WView *view)
1998-02-27 07:54:42 +03:00
{
static int dirt_limit = 1;
2002-10-31 01:48:59 +03:00
if (view->dirty > dirt_limit) {
1998-02-27 07:54:42 +03:00
/* Too many updates skipped -> force a update */
display (view);
view->dirty = 0;
/* Raise the update skipping limit */
dirt_limit++;
if (dirt_limit > max_dirt_limit)
dirt_limit = max_dirt_limit;
}
2002-10-31 01:48:59 +03:00
if (view->dirty) {
if (is_idle ()) {
1998-02-27 07:54:42 +03:00
/* We have time to update the screen properly */
display (view);
view->dirty = 0;
if (dirt_limit > 1)
dirt_limit--;
} else {
/* We are busy -> skipping full update,
only the status line is updated */
view_display_status (view);
1998-02-27 07:54:42 +03:00
}
/* Here we had a refresh, if fast scrolling does not work
restore the refresh, although this should not happen */
}
}
/* {{{ Hex editor }}} */
static void
enqueue_change (struct hexedit_change_node **head,
struct hexedit_change_node *node)
{
/* chnode always either points to the head of the list or
* to one of the ->next fields in the list. The value at
* this location will be overwritten with the new node. */
struct hexedit_change_node **chnode = head;
while (*chnode != NULL && (*chnode)->offset < node->offset)
chnode = &((*chnode)->next);
node->next = *chnode;
*chnode = node;
}
static cb_ret_t
view_handle_editkey (WView *view, int key)
{
struct hexedit_change_node *node;
byte byte_val;
/* Has there been a change at this position? */
node = view->change_list;
while (node && (node->offset != view->hex_cursor))
node = node->next;
if (!view->hexview_in_text) {
/* Hex editing */
unsigned int hexvalue = 0;
if (key >= '0' && key <= '9')
hexvalue = 0 + (key - '0');
else if (key >= 'A' && key <= 'F')
hexvalue = 10 + (key - 'A');
else if (key >= 'a' && key <= 'f')
hexvalue = 10 + (key - 'a');
else
return MSG_NOT_HANDLED;
if (node)
byte_val = node->value;
else
byte_val = get_byte (view, view->hex_cursor);
if (view->hexedit_lownibble) {
byte_val = (byte_val & 0xf0) | (hexvalue);
} else {
byte_val = (byte_val & 0x0f) | (hexvalue << 4);
}
} else {
/* Text editing */
if (key < 256 && (is_printable (key) || (key == '\n')))
byte_val = key;
else
return MSG_NOT_HANDLED;
}
if (!node) {
node = g_new (struct hexedit_change_node, 1);
node->offset = view->hex_cursor;
node->value = byte_val;
enqueue_change (&view->change_list, node);
} else {
node->value = byte_val;
}
view->dirty++;
view_update (view);
view_move_right (view, 1);
return MSG_HANDLED;
}
static void
free_change_list (WView *view)
{
struct hexedit_change_node *curr, *next;
for (curr = view->change_list; curr != NULL; curr = next) {
next = curr->next;
g_free (curr);
}
view->change_list = NULL;
view->dirty++;
}
static gboolean
view_hexedit_save_changes (WView *view)
{
struct hexedit_change_node *curr, *next;
int fp, answer;
char *text, *error;
retry_save:
fp = mc_open (view->filename, O_WRONLY);
if (fp == -1)
goto save_error;
for (curr = view->change_list; curr != NULL; curr = next) {
next = curr->next;
if (mc_lseek (fp, curr->offset, SEEK_SET) == -1
|| mc_write (fp, &(curr->value), 1) != 1)
goto save_error;
/* delete the saved item from the change list */
view->change_list = next;
view->dirty++;
view_set_byte (view, curr->offset, curr->value);
g_free (curr);
}
if (mc_close (fp) == -1) {
error = g_strdup (strerror (errno));
message (D_ERROR, _(" Save file "),
_(" Error while closing the file: \n %s \n"
" Data may have been written or not. "), error);
g_free (error);
}
view_update (view);
return TRUE;
save_error:
error = g_strdup (strerror (errno));
text = g_strdup_printf (_(" Cannot save file: \n %s "), error);
g_free (error);
(void) mc_close (fp);
answer = query_dialog (_(" Save file "), text, D_ERROR,
2, _("&Retry"), _("&Cancel"));
g_free (text);
if (answer == 0)
goto retry_save;
return FALSE;
}
/* {{{ Miscellaneous functions }}} */
static gboolean
view_ok_to_quit (WView *view)
{
int r;
if (!view->change_list)
return 1;
r = query_dialog (_("Quit"),
_(" File was modified, Save with exit? "), D_NORMAL, 3,
_("&Cancel quit"), _("&Yes"), _("&No"));
switch (r) {
case 1:
return view_hexedit_save_changes (view);
case 2:
free_change_list (view);
return TRUE;
default:
return FALSE;
}
}
1998-02-27 07:54:42 +03:00
static inline void
my_define (Dlg_head *h, int idx, const char *text, void (*fn) (WView *),
WView *view)
1998-02-27 07:54:42 +03:00
{
buttonbar_set_label_data (h, idx, text, (buttonbarfn) fn, view);
1998-02-27 07:54:42 +03:00
}
/* Case insensitive search of text in data */
static int
icase_search_p (WView *view, char *text, char *data, int nothing)
{
const char *q;
int lng;
const int direction = view->direction;
(void) nothing;
/* If we are searching backwards, reverse the string */
if (direction == -1) {
g_strreverse (text);
g_strreverse (data);
}
q = _icase_search (text, data, &lng);
1998-02-27 07:54:42 +03:00
if (direction == -1) {
g_strreverse (text);
g_strreverse (data);
}
if (q != 0) {
if (direction > 0)
view->search_start = q - data - lng;
else
view->search_start = strlen (data) - (q - data);
1998-02-27 07:54:42 +03:00
view->found_len = lng;
return 1;
1998-02-27 07:54:42 +03:00
}
return 0;
1998-02-27 07:54:42 +03:00
}
static char *
grow_string_buffer (char *text, gulong *size)
1998-02-27 07:54:42 +03:00
{
char *new;
/* The grow steps */
*size += 160;
new = g_realloc (text, *size);
if (text == NULL) {
*new = '\0';
1998-02-27 07:54:42 +03:00
}
return new;
}
static char *
get_line_at (WView *view, offset_type *p, offset_type *skipped)
1998-02-27 07:54:42 +03:00
{
char *buffer = NULL;
gulong buffer_size = 0;
offset_type usable_size = 0;
2002-10-31 01:48:59 +03:00
int ch;
const int direction = view->direction;
offset_type pos = *p;
offset_type i = 0;
int prev = '\0';
*skipped = 0;
if (pos == 0 && direction == -1)
return 0;
/* skip over all the possible zeros in the file */
while ((ch = get_byte (view, pos)) == 0) {
if (pos == 0 && direction == -1)
return 0;
2002-10-31 01:48:59 +03:00
pos += direction;
i++;
}
*skipped = i;
if (i == 0 && (pos != 0 || direction == -1)) {
prev = get_byte (view, pos - direction);
if ((prev == -1) || (prev == '\n'))
prev = '\0';
}
for (i = 1; ch != -1; ch = get_byte (view, pos)) {
if (i >= usable_size) {
1998-02-27 07:54:42 +03:00
buffer = grow_string_buffer (buffer, &buffer_size);
usable_size = buffer_size - 2; /* prev & null terminator */
1998-02-27 07:54:42 +03:00
}
buffer[i++] = ch;
if (pos == 0 && direction == -1)
break;
pos += direction;
if (ch == '\n' || ch == '\0') {
i--; /* Strip newline/zero */
1998-02-27 07:54:42 +03:00
break;
}
1998-02-27 07:54:42 +03:00
}
2002-10-31 01:48:59 +03:00
if (buffer) {
buffer[0] = prev;
buffer[i] = '\0';
/* If we are searching backwards, reverse the string */
if (direction == -1) {
g_strreverse (buffer + 1);
}
1998-02-27 07:54:42 +03:00
}
*p = pos;
return buffer;
}
static void
search_update_steps (WView *view)
{
offset_type filesize = view_get_filesize (view);
if (filesize != 0)
view->update_steps = 40000;
else /* viewing a data stream, not a file */
view->update_steps = filesize / 100;
1998-02-27 07:54:42 +03:00
/* Do not update the percent display but every 20 ks */
if (view->update_steps < 20000)
view->update_steps = 20000;
1998-02-27 07:54:42 +03:00
}
static void
2002-10-31 01:48:59 +03:00
search (WView *view, char *text,
int (*search) (WView *, char *, char *, int))
1998-02-27 07:54:42 +03:00
{
char *s = NULL; /* The line we read from the view buffer */
offset_type p, beginning, search_start;
int found_len;
1998-02-27 07:54:42 +03:00
int search_status;
Dlg_head *d = 0;
2002-10-31 01:48:59 +03:00
/* Used to keep track of where the line starts, when looking forward
* is the index before transfering the line; the reverse case uses
* the position returned after the line has been read */
offset_type forward_line_start;
offset_type reverse_line_start;
offset_type t;
2002-10-31 01:48:59 +03:00
if (verbose) {
d = create_message (D_NORMAL, _("Search"), _("Searching %s"), text);
1998-02-27 07:54:42 +03:00
mc_refresh ();
}
found_len = view->found_len;
search_start = view->search_start;
2002-10-31 01:48:59 +03:00
if (view->direction == 1) {
p = search_start + ((found_len) ? 1 : 0);
1998-02-27 07:54:42 +03:00
} else {
p = search_start - ((found_len && search_start >= 1) ? 1 : 0);
1998-02-27 07:54:42 +03:00
}
beginning = p;
/* Compute the percent steps */
search_update_steps (view);
view->update_activate = 0;
1998-02-27 07:54:42 +03:00
enable_interrupt_key ();
2002-10-31 01:48:59 +03:00
for (;; g_free (s)) {
if (p >= view->update_activate) {
view->update_activate += view->update_steps;
2002-10-31 01:48:59 +03:00
if (verbose) {
view_percent (view, p);
1998-02-27 07:54:42 +03:00
mc_refresh ();
}
if (got_interrupt ())
break;
}
forward_line_start = p;
s = get_line_at (view, &p, &t);
1998-02-27 07:54:42 +03:00
reverse_line_start = p;
1998-02-27 07:54:42 +03:00
if (!s)
break;
1998-02-27 07:54:42 +03:00
search_status = (*search) (view, text, s + 1, match_normal);
2002-10-31 01:48:59 +03:00
if (search_status < 0) {
g_free (s);
1998-02-27 07:54:42 +03:00
break;
}
1998-02-27 07:54:42 +03:00
if (search_status == 0)
continue;
/* We found the string */
2002-10-31 01:48:59 +03:00
/* Handle ^ and $ when regexp search starts at the middle of the line */
if (*s && !view->search_start && (search == regexp_view_search)) {
if ((*text == '^' && view->direction == 1)
|| (view->direction == -1 && text[strlen (text) - 1] == '$')
) {
continue;
}
1998-02-27 07:54:42 +03:00
}
/* Record the position used to continue the search */
if (view->direction == 1)
t += forward_line_start;
1998-02-27 07:54:42 +03:00
else
t = reverse_line_start ? reverse_line_start + 3 : 0;
1998-02-27 07:54:42 +03:00
view->search_start += t;
2002-10-31 01:48:59 +03:00
if (t != beginning) {
view->dpy_topleft = t;
1998-02-27 07:54:42 +03:00
}
g_free (s);
1998-02-27 07:54:42 +03:00
break;
}
disable_interrupt_key ();
if (verbose) {
1998-02-27 07:54:42 +03:00
dlg_run_done (d);
destroy_dlg (d);
}
if (!s) {
message (0, _("Search"), _(" Search string not found "));
1998-02-27 07:54:42 +03:00
view->found_len = 0;
}
}
/* Search buffer (its size is len) in the complete buffer
* returns the position where the block was found or INVALID_OFFSET
* if not found */
static offset_type
block_search (WView *view, const char *buffer, int len)
1998-02-27 07:54:42 +03:00
{
int direction = view->direction;
const char *d = buffer;
char b;
offset_type e;
1998-02-27 07:54:42 +03:00
enable_interrupt_key ();
if (direction == 1)
e = view->search_start + ((view->found_len) ? 1 : 0);
else
e = view->search_start
- ((view->found_len && view->search_start >= 1) ? 1 : 0);
1998-02-27 07:54:42 +03:00
search_update_steps (view);
view->update_activate = 0;
2002-10-31 01:48:59 +03:00
if (direction == -1) {
for (d += len - 1;; e--) {
if (e <= view->update_activate) {
view->update_activate -= view->update_steps;
if (verbose) {
view_percent (view, e);
mc_refresh ();
}
if (got_interrupt ())
break;
1998-02-27 07:54:42 +03:00
}
b = get_byte (view, e);
if (*d == b) {
if (d == buffer) {
disable_interrupt_key ();
return e;
}
d--;
} else {
e += buffer + len - 1 - d;
d = buffer + len - 1;
}
if (e == 0)
1998-02-27 07:54:42 +03:00
break;
}
} else {
while (get_byte (view, e) != -1) {
if (e >= view->update_activate) {
view->update_activate += view->update_steps;
if (verbose) {
view_percent (view, e);
mc_refresh ();
}
if (got_interrupt ())
break;
}
b = get_byte (view, e++);
2002-10-31 01:48:59 +03:00
if (*d == b) {
d++;
if (d - buffer == len) {
disable_interrupt_key ();
return e - len;
}
} else {
e -= d - buffer;
d = buffer;
}
1998-02-27 07:54:42 +03:00
}
}
1998-02-27 07:54:42 +03:00
disable_interrupt_key ();
return INVALID_OFFSET;
1998-02-27 07:54:42 +03:00
}
/*
* Search in the hex mode. Supported input:
* - numbers (oct, dec, hex). Each of them matches one byte.
* - strings in double quotes. Matches exactly without quotes.
*/
1998-02-27 07:54:42 +03:00
static void
hex_search (WView *view, const char *text)
1998-02-27 07:54:42 +03:00
{
char *buffer; /* Parsed search string */
char *cur; /* Current position in it */
2002-10-31 01:48:59 +03:00
int block_len; /* Length of the search string */
offset_type pos; /* Position of the string in the file */
2002-10-31 01:48:59 +03:00
int parse_error = 0;
1998-02-27 07:54:42 +03:00
if (!*text) {
view->found_len = 0;
return;
}
/* buffer will never be longer that text */
buffer = g_new (char, strlen (text));
cur = buffer;
/* First convert the string to a stream of bytes */
while (*text) {
int val;
int ptr;
/* Skip leading spaces */
if (*text == ' ' || *text == '\t') {
text++;
continue;
}
/* %i matches octal, decimal, and hexadecimal numbers */
if (sscanf (text, "%i%n", &val, &ptr) > 0) {
/* Allow signed and unsigned char in the user input */
if (val < -128 || val > 255) {
parse_error = 1;
1998-02-27 07:54:42 +03:00
break;
}
*cur++ = (char) val;
text += ptr;
continue;
}
1998-02-27 07:54:42 +03:00
/* Try quoted string, strip quotes */
if (*text == '"') {
const char *next_quote;
text++;
next_quote = strchr (text, '"');
if (next_quote) {
memcpy (cur, text, next_quote - text);
cur += next_quote - text;
text = next_quote + 1;
continue;
}
/* fall through */
1998-02-27 07:54:42 +03:00
}
parse_error = 1;
break;
1998-02-27 07:54:42 +03:00
}
block_len = cur - buffer;
/* No valid bytes in the user input */
if (block_len <= 0 || parse_error) {
message (0, _("Search"), _("Invalid hex search expression"));
g_free (buffer);
view->found_len = 0;
return;
}
1998-02-27 07:54:42 +03:00
/* Then start the search */
pos = block_search (view, buffer, block_len);
g_free (buffer);
if (pos == INVALID_OFFSET) {
message (0, _("Search"), _(" Search string not found "));
1998-02-27 07:54:42 +03:00
view->found_len = 0;
return;
}
2002-10-31 01:48:59 +03:00
view->search_start = pos;
1998-02-27 07:54:42 +03:00
view->found_len = block_len;
/* Set the edit cursor to the search position, left nibble */
view->hex_cursor = view->search_start;
view->hexedit_lownibble = FALSE;
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
/* Adjust the file offset */
view->dpy_topleft = pos - pos % view->bytes_per_line;
1998-02-27 07:54:42 +03:00
}
2002-10-31 01:48:59 +03:00
static int
regexp_view_search (WView *view, char *pattern, char *string,
int match_type)
1998-02-27 07:54:42 +03:00
{
static regex_t r;
static char *old_pattern = NULL;
static int old_type;
regmatch_t pmatch[1];
int i, flags = REG_ICASE;
2002-10-31 01:48:59 +03:00
if (!old_pattern || strcmp (old_pattern, pattern)
|| old_type != match_type) {
if (old_pattern) {
1998-02-27 07:54:42 +03:00
regfree (&r);
g_free (old_pattern);
1998-02-27 07:54:42 +03:00
old_pattern = 0;
}
2002-10-31 01:48:59 +03:00
for (i = 0; pattern[i] != 0; i++) {
if (isupper ((unsigned char) pattern[i])) {
1998-02-27 07:54:42 +03:00
flags = 0;
break;
}
}
flags |= REG_EXTENDED;
2002-10-31 01:48:59 +03:00
if (regcomp (&r, pattern, flags)) {
message (1, MSG_ERROR, _(" Invalid regular expression "));
1998-02-27 07:54:42 +03:00
return -1;
}
old_pattern = g_strdup (pattern);
1998-02-27 07:54:42 +03:00
old_type = match_type;
}
if (regexec (&r, string, 1, pmatch, 0) != 0)
return 0;
view->found_len = pmatch[0].rm_eo - pmatch[0].rm_so;
view->search_start = pmatch[0].rm_so;
return 1;
}
2002-10-31 01:48:59 +03:00
static void
do_regexp_search (WView *view, char *regexp)
1998-02-27 07:54:42 +03:00
{
view->search_exp = regexp;
search (view, regexp, regexp_view_search);
/* Had a refresh here */
view->dirty++;
view_update (view);
1998-02-27 07:54:42 +03:00
}
2002-10-31 01:48:59 +03:00
static void
do_normal_search (WView *view, char *text)
1998-02-27 07:54:42 +03:00
{
view->search_exp = text;
if (view->hex_mode)
hex_search (view, text);
2002-10-31 01:48:59 +03:00
else
1998-02-27 07:54:42 +03:00
search (view, text, icase_search_p);
/* Had a refresh here */
view->dirty++;
view_update (view);
1998-02-27 07:54:42 +03:00
}
/* Real view only */
2002-10-31 01:48:59 +03:00
static void
view_help_cmd (void)
1998-02-27 07:54:42 +03:00
{
interactive_display (NULL, "[Internal File Viewer]");
1998-02-27 07:54:42 +03:00
/*
2002-10-31 01:48:59 +03:00
view_refresh (0);
*/
1998-02-27 07:54:42 +03:00
}
/* Toggle between hexview and hexedit mode */
2002-10-31 01:48:59 +03:00
static void
toggle_hexedit_mode (WView *view)
1998-02-27 07:54:42 +03:00
{
view->hexedit_mode = !view->hexedit_mode;
view_labels (view);
view->dirty++;
view_update (view);
}
/* Toggle between wrapped and unwrapped view */
static void
toggle_wrap_mode (WView *view)
{
view->text_wrap_mode = !view->text_wrap_mode;
if (view->text_wrap_mode) {
view_fix_cursor_position (view);
} else {
offset_type line;
view_offset_to_coord (view, &line, &(view->dpy_text_column), view->dpy_topleft);
view_coord_to_offset (view, &(view->dpy_topleft), line, 0);
1998-02-27 07:54:42 +03:00
}
view_labels (view);
view->dirty++;
view_update (view);
1998-02-27 07:54:42 +03:00
}
/* Toggle between hex view and text view */
static void
1998-02-27 07:54:42 +03:00
toggle_hex_mode (WView *view)
{
view->hex_mode = !view->hex_mode;
1998-02-27 07:54:42 +03:00
2002-10-31 01:48:59 +03:00
if (view->hex_mode) {
view->hex_cursor = view->dpy_topleft;
view->dpy_topleft =
offset_rounddown (view->dpy_topleft, view->bytes_per_line);
1998-02-27 07:54:42 +03:00
view->widget.options |= W_WANT_CURSOR;
} else {
view->dpy_topleft = view->hex_cursor;
view_moveto_bol (view);
1998-02-27 07:54:42 +03:00
view->widget.options &= ~W_WANT_CURSOR;
}
altered_hex_mode = 1;
view_labels (view);
view->dirty++;
view_update (view);
1998-02-27 07:54:42 +03:00
}
/* Text view */
static void
1998-02-27 07:54:42 +03:00
goto_line (WView *view)
{
char *answer, *answer_end, prompt[BUF_SMALL];
offset_type line, col;
1998-02-27 07:54:42 +03:00
view_offset_to_coord (view, &line, &col, view->dpy_topleft);
2002-10-31 01:48:59 +03:00
g_snprintf (prompt, sizeof (prompt),
_(" The current line number is %d.\n"
" Enter the new line number:"), (int) (line + 1));
answer = input_dialog (_(" Goto line "), prompt, "");
if (answer && answer[0] != '\0') {
errno = 0;
line = strtoul (answer, &answer_end, 10);
if (*answer_end == '\0' && errno == 0 && line >= 1)
view_moveto (view, line - 1, 0);
}
g_free (answer);
1998-02-27 07:54:42 +03:00
view->dirty++;
view_update (view);
1998-02-27 07:54:42 +03:00
}
/* Hex view */
static void
goto_addr (WView *view)
{
2002-10-31 01:48:59 +03:00
char *line, *error, prompt[BUF_SMALL];
offset_type addr;
2002-10-31 01:48:59 +03:00
g_snprintf (prompt, sizeof (prompt),
_(" The current address is 0x%lx.\n"
" Enter the new address:"), view->hex_cursor);
line = input_dialog (_(" Goto Address "), prompt, "");
2002-10-31 01:48:59 +03:00
if (line) {
if (*line) {
addr = strtoul (line, &error, 0);
if ((*error == '\0') && get_byte (view, addr) != -1) {
view_moveto_offset (view, addr);
2002-10-31 01:48:59 +03:00
}
}
g_free (line);
}
view->dirty++;
view_update (view);
}
1998-02-27 07:54:42 +03:00
/* Both views */
1998-12-03 00:27:27 +03:00
static void
1998-02-27 07:54:42 +03:00
regexp_search (WView *view, int direction)
{
char *regexp = str_unconst ("");
1998-02-27 07:54:42 +03:00
static char *old = 0;
/* This is really an F6 key handler */
2002-10-31 01:48:59 +03:00
if (view->hex_mode) {
/* Save it without a confirmation prompt */
if (view->change_list)
view_hexedit_save_changes (view);
1998-02-27 07:54:42 +03:00
return;
}
2002-10-31 01:48:59 +03:00
if (old)
regexp = old;
regexp = input_dialog (_("Search"), _(" Enter regexp:"), regexp);
2002-10-31 01:48:59 +03:00
if ((!regexp)) {
return;
}
2002-10-31 01:48:59 +03:00
if ((!*regexp)) {
g_free (regexp);
1998-02-27 07:54:42 +03:00
return;
}
g_free (old);
1998-02-27 07:54:42 +03:00
old = regexp;
view->direction = direction;
do_regexp_search (view, regexp);
view->last_search = do_regexp_search;
}
static void
1998-02-27 07:54:42 +03:00
regexp_search_cmd (WView *view)
{
regexp_search (view, 1);
}
/* Both views */
1998-12-03 00:27:27 +03:00
static void
normal_search_cmd (WView *view)
1998-02-27 07:54:42 +03:00
{
static char *old;
char *exp = old ? old : str_unconst ("");
enum {
SEARCH_DLG_HEIGHT = 8,
SEARCH_DLG_WIDTH = 58
};
static int replace_backwards;
int treplace_backwards = replace_backwards;
static QuickWidget quick_widgets[] = {
{quick_button, 6, 10, 5, SEARCH_DLG_HEIGHT, N_("&Cancel"), 0,
B_CANCEL,
0, 0, NULL},
{quick_button, 2, 10, 5, SEARCH_DLG_HEIGHT, N_("&OK"), 0, B_ENTER,
0, 0, NULL},
{quick_checkbox, 3, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT,
N_("&Backwards"), 0, 0,
0, 0, NULL},
{quick_input, 3, SEARCH_DLG_WIDTH, 3, SEARCH_DLG_HEIGHT, "", 52, 0,
0, 0, N_("Search")},
{quick_label, 2, SEARCH_DLG_WIDTH, 2, SEARCH_DLG_HEIGHT,
N_(" Enter search string:"), 0, 0,
0, 0, 0},
NULL_QuickWidget
};
static QuickDialog Quick_input = {
SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, N_("Search"),
"[Input Line Keys]", quick_widgets, 0
};
convert_to_display (old);
quick_widgets[2].result = &treplace_backwards;
quick_widgets[3].str_result = &exp;
quick_widgets[3].text = exp;
if (quick_dialog (&Quick_input) == B_CANCEL) {
convert_from_input (old);
1998-02-27 07:54:42 +03:00
return;
}
replace_backwards = treplace_backwards;
convert_from_input (old);
if ((!exp)) {
return;
}
if ((!*exp)) {
g_free (exp);
return;
}
g_free (old);
1998-02-27 07:54:42 +03:00
old = exp;
2002-10-31 01:48:59 +03:00
convert_from_input (exp);
view->direction = replace_backwards ? -1 : 1;
1998-02-27 07:54:42 +03:00
do_normal_search (view, exp);
view->last_search = do_normal_search;
}
1998-12-03 00:27:27 +03:00
static void
1998-02-27 07:54:42 +03:00
change_viewer (WView *view)
{
I improved the movement keys of the internal viewer a little bit. Now in wrap mode the End key and cursor up key behave much better (e.g. when viewing binary files with lots of wrapped lines). It's not perfekt but it's better than it used to be. Tue Apr 28 06:52:24 1998 Norbert Warmuth <k3190@fh-sw.de> * gnome/gcmd.c (gnome_open_terminal): Changed my_system(1,...) to my_system(EXECUTE_AS_SHELL,...) Tue Apr 28 06:06:03 1998 Norbert Warmuth <k3190@fh-sw.de> * vfs/extfs.c (extfs_open, extfs_close): Changed my_system(1,...) to my_system(EXECUTE_AS_SHELL,...), this fixes the broken copyin and copyout in 4.1.32. Tue Apr 28 06:11:08 1998 Norbert Warmuth <k3190@fh-sw.de> * view.c (toggle_wrap_mode, toggle_hex_mode): Force recalculation of bottom_first (we mustn't use an already calculated and cached value because it is invalid for the new mode and the End key would not move to the end of the file). * configure.in: Renamed the option `--with-our-slang' to `--with-included-slang' (this one looks better because we also have an `--with-included-gettext'). Make the option `--with-ext2undel' recognice a given path. * cmd.c (view_file_at_line): In plain view (F13) set the default magic flag to zero in order to view the file content unprocessed (esp. don't uncompress files if they are compressed). The view_simple_cmd got broken when the default magic flag in view.c was changed from 0 to 1. * view.c (do_view_init, goto_line): Set wrap mode temporary off to make goto line number work, i.e. `line number' now always means line number in file and not line number on screen (in wrap mode one long line wrapped once is displayed in two lines on the screen). That's important when the viewer is invoked from the find file dialog to display even in wrap mode approxiamtly the part of the file where we found the content we searched for. (move_forward2): In wrap mode lines were sometimes counted wrong causing cursor up to move more than one line. (move_backward2): Fixed the movement in wrap mode. (change_viewer): Always re-init viewer when we have a filename, i. e. if the viewer is invoked with simple_view_cmd then we can switch with the F8 key between unprocessed file content und uncompressed file content. (view_init): re-init view also when magic flag was altered
1998-04-28 18:19:48 +04:00
char *s;
char *t;
1998-02-27 07:54:42 +03:00
I improved the movement keys of the internal viewer a little bit. Now in wrap mode the End key and cursor up key behave much better (e.g. when viewing binary files with lots of wrapped lines). It's not perfekt but it's better than it used to be. Tue Apr 28 06:52:24 1998 Norbert Warmuth <k3190@fh-sw.de> * gnome/gcmd.c (gnome_open_terminal): Changed my_system(1,...) to my_system(EXECUTE_AS_SHELL,...) Tue Apr 28 06:06:03 1998 Norbert Warmuth <k3190@fh-sw.de> * vfs/extfs.c (extfs_open, extfs_close): Changed my_system(1,...) to my_system(EXECUTE_AS_SHELL,...), this fixes the broken copyin and copyout in 4.1.32. Tue Apr 28 06:11:08 1998 Norbert Warmuth <k3190@fh-sw.de> * view.c (toggle_wrap_mode, toggle_hex_mode): Force recalculation of bottom_first (we mustn't use an already calculated and cached value because it is invalid for the new mode and the End key would not move to the end of the file). * configure.in: Renamed the option `--with-our-slang' to `--with-included-slang' (this one looks better because we also have an `--with-included-gettext'). Make the option `--with-ext2undel' recognice a given path. * cmd.c (view_file_at_line): In plain view (F13) set the default magic flag to zero in order to view the file content unprocessed (esp. don't uncompress files if they are compressed). The view_simple_cmd got broken when the default magic flag in view.c was changed from 0 to 1. * view.c (do_view_init, goto_line): Set wrap mode temporary off to make goto line number work, i.e. `line number' now always means line number in file and not line number on screen (in wrap mode one long line wrapped once is displayed in two lines on the screen). That's important when the viewer is invoked from the find file dialog to display even in wrap mode approxiamtly the part of the file where we found the content we searched for. (move_forward2): In wrap mode lines were sometimes counted wrong causing cursor up to move more than one line. (move_backward2): Fixed the movement in wrap mode. (change_viewer): Always re-init viewer when we have a filename, i. e. if the viewer is invoked with simple_view_cmd then we can switch with the F8 key between unprocessed file content und uncompressed file content. (view_init): re-init view also when magic flag was altered
1998-04-28 18:19:48 +04:00
if (*view->filename) {
2002-10-31 01:48:59 +03:00
altered_magic_flag = 1;
view->magic_mode = !view->magic_mode;
2002-10-31 01:48:59 +03:00
s = g_strdup (view->filename);
t = g_strdup (view->command);
I improved the movement keys of the internal viewer a little bit. Now in wrap mode the End key and cursor up key behave much better (e.g. when viewing binary files with lots of wrapped lines). It's not perfekt but it's better than it used to be. Tue Apr 28 06:52:24 1998 Norbert Warmuth <k3190@fh-sw.de> * gnome/gcmd.c (gnome_open_terminal): Changed my_system(1,...) to my_system(EXECUTE_AS_SHELL,...) Tue Apr 28 06:06:03 1998 Norbert Warmuth <k3190@fh-sw.de> * vfs/extfs.c (extfs_open, extfs_close): Changed my_system(1,...) to my_system(EXECUTE_AS_SHELL,...), this fixes the broken copyin and copyout in 4.1.32. Tue Apr 28 06:11:08 1998 Norbert Warmuth <k3190@fh-sw.de> * view.c (toggle_wrap_mode, toggle_hex_mode): Force recalculation of bottom_first (we mustn't use an already calculated and cached value because it is invalid for the new mode and the End key would not move to the end of the file). * configure.in: Renamed the option `--with-our-slang' to `--with-included-slang' (this one looks better because we also have an `--with-included-gettext'). Make the option `--with-ext2undel' recognice a given path. * cmd.c (view_file_at_line): In plain view (F13) set the default magic flag to zero in order to view the file content unprocessed (esp. don't uncompress files if they are compressed). The view_simple_cmd got broken when the default magic flag in view.c was changed from 0 to 1. * view.c (do_view_init, goto_line): Set wrap mode temporary off to make goto line number work, i.e. `line number' now always means line number in file and not line number on screen (in wrap mode one long line wrapped once is displayed in two lines on the screen). That's important when the viewer is invoked from the find file dialog to display even in wrap mode approxiamtly the part of the file where we found the content we searched for. (move_forward2): In wrap mode lines were sometimes counted wrong causing cursor up to move more than one line. (move_backward2): Fixed the movement in wrap mode. (change_viewer): Always re-init viewer when we have a filename, i. e. if the viewer is invoked with simple_view_cmd then we can switch with the F8 key between unprocessed file content und uncompressed file content. (view_init): re-init view also when magic flag was altered
1998-04-28 18:19:48 +04:00
2002-10-31 01:48:59 +03:00
view_done (view);
view_load (view, t, s, 0);
2002-10-31 01:48:59 +03:00
g_free (s);
g_free (t);
2002-10-31 01:48:59 +03:00
view_labels (view);
view->dirty++;
view_update (view);
1998-02-27 07:54:42 +03:00
}
}
static void
1998-02-27 07:54:42 +03:00
change_nroff (WView *view)
{
view->text_nroff_mode = !view->text_nroff_mode;
1998-02-27 07:54:42 +03:00
altered_nroff_flag = 1;
view_labels (view);
view->dirty++;
view_update (view);
1998-02-27 07:54:42 +03:00
}
/* Real view only */
static void
view_quit_cmd (WView *view)
1998-02-27 07:54:42 +03:00
{
if (view_ok_to_quit (view))
dlg_stop (view->widget.parent);
1998-02-27 07:54:42 +03:00
}
/* Define labels and handlers for functional keys */
static void
1998-02-27 07:54:42 +03:00
view_labels (WView *view)
{
Dlg_head *h = view->widget.parent;
2002-10-31 01:48:59 +03:00
buttonbar_set_label (h, 1, gettext_ui("ButtonBar|Help"), view_help_cmd);
my_define (h, 10, gettext_ui("ButtonBar|Quit"), view_quit_cmd, view);
my_define (h, 4, view->hex_mode
? gettext_ui("ButtonBar|Ascii")
: gettext_ui("ButtonBar|Hex"),
toggle_hex_mode, view);
my_define (h, 5, view->hex_mode
? gettext_ui("ButtonBar|Goto")
: gettext_ui("ButtonBar|Line"),
view->hex_mode ? goto_addr : goto_line, view);
my_define (h, 6, view->hex_mode
? gettext_ui("ButtonBar|Save")
: gettext_ui("ButtonBar|RxSrch"),
regexp_search_cmd, view);
1998-02-27 07:54:42 +03:00
if (view->hex_mode) {
if (view->hexedit_mode) {
my_define (h, 2, gettext_ui("ButtonBar|View"),
toggle_hexedit_mode, view);
} else if (view->datasource == DS_FILE) {
my_define (h, 2, gettext_ui("ButtonBar|Edit"),
toggle_hexedit_mode, view);
} else {
my_define (h, 2, "", NULL, view);
}
} else {
my_define (h, 2, view->text_wrap_mode
? gettext_ui("ButtonBar|UnWrap")
: gettext_ui("ButtonBar|Wrap"),
toggle_wrap_mode, view);
}
2002-10-31 01:48:59 +03:00
my_define (h, 7, view->hex_mode
? gettext_ui("ButtonBar|HxSrch")
: gettext_ui("ButtonBar|Search"),
normal_search_cmd, view);
my_define (h, 8, view->magic_mode
? gettext_ui("ButtonBar|Raw")
: gettext_ui("ButtonBar|Parse"),
change_viewer, view);
1998-02-27 07:54:42 +03:00
/* don't override the key to access the main menu */
if (!view_is_in_panel (view)) {
my_define (h, 9, view->text_nroff_mode
? gettext_ui("ButtonBar|Unform")
: gettext_ui("ButtonBar|Format"),
change_nroff, view);
my_define (h, 3, gettext_ui("ButtonBar|Quit"), view_quit_cmd, view);
1998-02-27 07:54:42 +03:00
}
buttonbar_redraw (h);
1998-02-27 07:54:42 +03:00
}
/* Check for left and right arrows, possibly with modifiers */
static cb_ret_t
1998-02-27 07:54:42 +03:00
check_left_right_keys (WView *view, int c)
{
if (c == KEY_LEFT) {
view_move_left (view, 1);
return MSG_HANDLED;
}
if (c == KEY_RIGHT) {
view_move_right (view, 1);
return MSG_HANDLED;
}
1998-02-27 07:54:42 +03:00
/* Ctrl with arrows moves by 10 postions in the unwrap mode */
if (view->hex_mode || view->text_wrap_mode)
return MSG_NOT_HANDLED;
if (c == (KEY_M_CTRL | KEY_LEFT)) {
if (view->dpy_text_column >= 10)
view->dpy_text_column -= 10;
else
view->dpy_text_column = 0;
view->dirty++;
return MSG_HANDLED;
}
if (c == (KEY_M_CTRL | KEY_RIGHT)) {
if (view->dpy_text_column <= OFFSETTYPE_MAX - 10)
view->dpy_text_column += 10;
else
view->dpy_text_column = OFFSETTYPE_MAX;
view->dirty++;
return MSG_HANDLED;
}
return MSG_NOT_HANDLED;
1998-02-27 07:54:42 +03:00
}
static void
continue_search (WView *view)
{
2002-10-31 01:48:59 +03:00
if (view->last_search) {
(*view->last_search) (view, view->search_exp);
} else {
/* if not... then ask for an expression */
normal_search_cmd (view);
}
}
static void view_cmk_move_up (void *w, int n) {
view_move_up ((WView *) w, n);
}
static void view_cmk_move_down (void *w, int n) {
view_move_down ((WView *) w, n);
}
static void view_cmk_moveto_top (void *w, int n) {
(void) &n;
view_moveto_top ((WView *) w);
}
static void view_cmk_moveto_bottom (void *w, int n) {
(void) &n;
view_moveto_bottom ((WView *) w);
}
1998-02-27 07:54:42 +03:00
/* Both views */
static cb_ret_t
1998-02-27 07:54:42 +03:00
view_handle_key (WView *view, int c)
{
c = convert_from_input_c (c);
1998-02-27 07:54:42 +03:00
if (view->hex_mode) {
2002-10-31 01:48:59 +03:00
switch (c) {
case '\t':
view->hexview_in_text = !view->hexview_in_text;
2002-10-31 01:48:59 +03:00
view->dirty++;
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
case XCTRL ('a'):
view_moveto_bol (view);
2002-10-31 01:48:59 +03:00
view->dirty++;
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
case XCTRL ('b'):
view_move_left (view, 1);
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
case XCTRL ('e'):
view_moveto_eol (view);
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
case XCTRL ('f'):
view_move_right (view, 1);
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
}
if (view->hexedit_mode
&& view_handle_editkey (view, c) == MSG_HANDLED)
return MSG_HANDLED;
1998-02-27 07:54:42 +03:00
}
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
if (check_left_right_keys (view, c))
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
if (check_movement_keys (c, view->data_area.height + 1, view,
view_cmk_move_up, view_cmk_move_down,
view_cmk_moveto_top, view_cmk_moveto_bottom))
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
switch (c) {
1998-02-27 07:54:42 +03:00
case '?':
regexp_search (view, -1);
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
case '/':
regexp_search (view, 1);
return MSG_HANDLED;
1998-02-27 07:54:42 +03:00
/* Continue search */
2002-10-31 01:48:59 +03:00
case XCTRL ('s'):
1998-02-27 07:54:42 +03:00
case 'n':
2002-10-31 01:48:59 +03:00
case KEY_F (17):
continue_search (view);
return MSG_HANDLED;
1998-02-27 07:54:42 +03:00
2002-10-31 01:48:59 +03:00
case XCTRL ('r'):
if (view->last_search) {
(*view->last_search) (view, view->search_exp);
1998-02-27 07:54:42 +03:00
} else {
normal_search_cmd (view);
1998-02-27 07:54:42 +03:00
}
return MSG_HANDLED;
1998-02-27 07:54:42 +03:00
/* toggle ruler */
2002-10-31 01:48:59 +03:00
case ALT ('r'):
switch (ruler) {
1998-02-27 07:54:42 +03:00
case 0:
2002-10-31 01:48:59 +03:00
ruler = 1;
break;
1998-02-27 07:54:42 +03:00
case 1:
2002-10-31 01:48:59 +03:00
ruler = 2;
break;
1998-02-27 07:54:42 +03:00
default:
2002-10-31 01:48:59 +03:00
ruler = 0;
break;
1998-02-27 07:54:42 +03:00
}
view->dirty++;
return MSG_HANDLED;
1998-02-27 07:54:42 +03:00
case 'h':
view_move_left (view, 1);
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
case 'j':
case '\n':
case 'e':
view_move_down (view, 1);
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
case 'd':
view_move_down (view, (view->data_area.height + 1) / 2);
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
case 'u':
view_move_up (view, (view->data_area.height + 1) / 2);
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
case 'k':
case 'y':
view_move_up (view, 1);
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
case 'l':
view_move_right (view, 1);
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
case ' ':
case 'f':
view_move_down (view, view->data_area.height);
return MSG_HANDLED;
1998-02-27 07:54:42 +03:00
2002-10-31 01:48:59 +03:00
case XCTRL ('o'):
view_other_cmd ();
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
/* Unlike Ctrl-O, run a new shell if the subshell is not running. */
1998-02-27 07:54:42 +03:00
case '!':
exec_shell ();
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
case 'b':
view_move_up (view, view->data_area.height);
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
case KEY_IC:
view_move_up (view, 2);
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
case KEY_DC:
view_move_down (view, 2);
return MSG_HANDLED;
1998-02-27 07:54:42 +03:00
case 'm':
view->marks[view->marker] = view->dpy_topleft;
return MSG_HANDLED;
1998-02-27 07:54:42 +03:00
case 'r':
view->dpy_topleft = view->marks[view->marker];
1998-02-27 07:54:42 +03:00
view->dirty++;
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
/* Use to indicate parent that we want to see the next/previous file */
/* Does not work in panel mode */
2002-10-31 01:48:59 +03:00
case XCTRL ('f'):
case XCTRL ('b'):
if (!view_is_in_panel (view))
2002-10-31 01:48:59 +03:00
view->move_dir = c == XCTRL ('f') ? 1 : -1;
/* FALLTHROUGH */
1998-02-27 07:54:42 +03:00
case 'q':
2002-10-31 01:48:59 +03:00
case XCTRL ('g'):
1998-02-27 07:54:42 +03:00
case ESC_CHAR:
if (view_ok_to_quit (view))
view->want_to_quit = TRUE;
return MSG_HANDLED;
1998-02-27 07:54:42 +03:00
#ifdef HAVE_CHARSET
2002-10-31 01:48:59 +03:00
case XCTRL ('t'):
do_select_codepage ();
view->dirty++;
view_update (view);
return MSG_HANDLED;
2002-10-31 01:48:59 +03:00
#endif /* HAVE_CHARSET */
#ifdef MC_ENABLE_DEBUGGING_CODE
case 't': /* mnemonic: "test" */
view_ccache_dump (view);
return MSG_HANDLED;
#endif
1998-02-27 07:54:42 +03:00
}
if (c >= '0' && c <= '9')
view->marker = c - '0';
/* Key not used */
return MSG_NOT_HANDLED;
1998-02-27 07:54:42 +03:00
}
/* Both views */
static int
2002-10-31 01:48:59 +03:00
view_event (WView *view, Gpm_Event *event, int *result)
1998-02-27 07:54:42 +03:00
{
screen_dimen y, x;
1998-02-27 07:54:42 +03:00
*result = MOU_NORMAL;
/* We are not interested in the release events */
if (!(event->type & (GPM_DOWN | GPM_DRAG)))
return 0;
/* Wheel events */
if ((event->buttons & GPM_B_UP) && (event->type & GPM_DOWN)) {
view_move_up (view, 2);
return 1;
}
if ((event->buttons & GPM_B_DOWN) && (event->type & GPM_DOWN)) {
view_move_down (view, 2);
return 1;
}
x = event->x;
y = event->y;
/* Scrolling left and right */
if (!view->text_wrap_mode) {
if (x < view->data_area.width * 1/4) {
view_move_left (view, 1);
goto processed;
} else if (x < view->data_area.width * 3/4) {
/* ignore the click */
} else {
view_move_right (view, 1);
goto processed;
1998-02-27 07:54:42 +03:00
}
}
/* Scrolling up and down */
if (y < view->data_area.top + view->data_area.height * 1/3) {
if (mouse_move_pages_viewer)
view_move_up (view, view->data_area.height / 2);
else
view_move_up (view, 1);
goto processed;
} else if (y < view->data_area.top + view->data_area.height * 2/3) {
/* ignore the click */
} else {
if (mouse_move_pages_viewer)
view_move_down (view, view->data_area.height / 2);
else
view_move_down (view, 1);
goto processed;
}
1998-02-27 07:54:42 +03:00
return 0;
processed:
*result = MOU_REPEAT;
return 1;
1998-02-27 07:54:42 +03:00
}
/* Real view only */
1998-12-03 00:27:27 +03:00
static int
1998-02-27 07:54:42 +03:00
real_view_event (Gpm_Event *event, void *x)
{
WView *view = (WView *) x;
1998-02-27 07:54:42 +03:00
int result;
2002-10-31 01:48:59 +03:00
if (view_event (view, event, &result))
view_update (view);
1998-02-27 07:54:42 +03:00
return result;
}
static void
1998-02-27 07:54:42 +03:00
view_adjust_size (Dlg_head *h)
{
2002-10-31 01:48:59 +03:00
WView *view;
1998-02-27 07:54:42 +03:00
WButtonBar *bar;
/* Look up the viewer and the buttonbar, we assume only two widgets here */
view = (WView *) find_widget_type (h, view_callback);
bar = find_buttonbar (h);
2002-10-31 01:48:59 +03:00
widget_set_size (&view->widget, 0, 0, LINES - 1, COLS);
widget_set_size ((Widget *) bar, LINES - 1, 0, 1, COLS);
view_compute_areas (view);
2002-10-31 01:48:59 +03:00
view_update_bytes_per_line (view);
1998-02-27 07:54:42 +03:00
}
/* Callback for the view dialog */
static cb_ret_t
view_dialog_callback (Dlg_head *h, dlg_msg_t msg, int parm)
{
switch (msg) {
case DLG_RESIZE:
view_adjust_size (h);
return MSG_HANDLED;
default:
return default_dlg_callback (h, msg, parm);
}
}
1998-02-27 07:54:42 +03:00
/* Real view only */
int
mc_internal_viewer (const char *command, const char *file,
int *move_dir_p, int start_line)
1998-02-27 07:54:42 +03:00
{
gboolean succeeded;
1998-02-27 07:54:42 +03:00
WView *wview;
WButtonBar *bar;
Dlg_head *view_dlg;
1998-02-27 07:54:42 +03:00
/* Create dialog and widgets, put them on the dialog */
view_dlg =
create_dlg (0, 0, LINES, COLS, NULL, view_dialog_callback,
"[Internal File Viewer]", NULL, DLG_WANT_TAB);
1998-02-27 07:54:42 +03:00
wview = view_new (0, 0, COLS, LINES - 1, 0);
1998-02-27 07:54:42 +03:00
bar = buttonbar_new (1);
1998-02-27 07:54:42 +03:00
add_widget (view_dlg, bar);
add_widget (view_dlg, wview);
1998-02-27 07:54:42 +03:00
succeeded = view_load (wview, command, file, start_line);
if (succeeded) {
run_dlg (view_dlg);
1998-02-27 07:54:42 +03:00
if (move_dir_p)
*move_dir_p = wview->move_dir;
} else {
if (move_dir_p)
*move_dir_p = 0;
1998-02-27 07:54:42 +03:00
}
destroy_dlg (view_dlg);
return succeeded;
1998-02-27 07:54:42 +03:00
}
static void
view_hook (void *v)
{
WView *view = (WView *) v;
WPanel *panel;
/* If the user is busy typing, wait until he finishes to update the
screen */
2002-10-31 01:48:59 +03:00
if (!is_idle ()) {
1998-02-27 07:54:42 +03:00
if (!hook_present (idle_hook, view_hook))
add_hook (&idle_hook, view_hook, v);
return;
}
delete_hook (&idle_hook, view_hook);
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
if (get_current_type () == view_listing)
panel = current_panel;
1998-02-27 07:54:42 +03:00
else if (get_other_type () == view_listing)
panel = other_panel;
else
return;
view_load (view, 0, panel->dir.list[panel->selected].fname, 0);
1998-02-27 07:54:42 +03:00
display (view);
}
static cb_ret_t
view_callback (Widget *w, widget_msg_t msg, int parm)
1998-02-27 07:54:42 +03:00
{
WView *view = (WView *) w;
cb_ret_t i;
Dlg_head *h = view->widget.parent;
view_compute_areas (view);
view_update_bytes_per_line (view);
switch (msg) {
1998-02-27 07:54:42 +03:00
case WIDGET_INIT:
if (view_is_in_panel (view))
1998-02-27 07:54:42 +03:00
add_hook (&select_file_hook, view_hook, view);
else
view_labels (view);
return MSG_HANDLED;
1998-02-27 07:54:42 +03:00
case WIDGET_DRAW:
display (view);
return MSG_HANDLED;
1998-02-27 07:54:42 +03:00
case WIDGET_CURSOR:
if (view->hex_mode)
view_place_cursor (view);
return MSG_HANDLED;
1998-02-27 07:54:42 +03:00
case WIDGET_KEY:
i = view_handle_key ((WView *) view, parm);
if (view->want_to_quit && !view_is_in_panel (view))
dlg_stop (h);
1998-02-27 07:54:42 +03:00
else {
view_update (view);
1998-02-27 07:54:42 +03:00
}
return i;
case WIDGET_FOCUS:
view_labels (view);
return MSG_HANDLED;
case WIDGET_DESTROY:
view_done (view);
if (view_is_in_panel (view))
delete_hook (&select_file_hook, view_hook);
return MSG_HANDLED;
default:
return default_proc (msg, parm);
1998-02-27 07:54:42 +03:00
}
}
WView *
view_new (int y, int x, int cols, int lines, int is_panel)
{
WView *view = g_new0 (WView, 1);
size_t i;
2002-10-31 01:48:59 +03:00
1998-02-27 07:54:42 +03:00
init_widget (&view->widget, y, x, lines, cols,
view_callback,
real_view_event);
widget_want_cursor (view->widget, 0);
1998-02-27 07:54:42 +03:00
view->filename = NULL;
view->command = NULL;
view_set_datasource_none (view);
view->growbuf_in_use = FALSE;
/* leave the other growbuf fields uninitialized */
view->hex_mode = default_hex_mode;
view->hexedit_mode = default_hexedit_mode;
view->hexview_in_text = FALSE;
view->text_nroff_mode = default_nroff_flag;
view->text_wrap_mode = global_wrap_mode;
view->magic_mode = default_magic_flag;
view->hexedit_lownibble = FALSE;
view->coord_cache = NULL;
view->dpy_frame_size = is_panel ? 1 : 0;
view->dpy_topleft = 0;
view->dpy_text_column = 0;
view->dpy_complete = FALSE;
view->hex_cursor = 0;
view->cursor_col = 0;
view->cursor_row = 0;
view->change_list = NULL;
/* {status,ruler,data}_area are left uninitialized */
view->dirty = 0;
view->bytes_per_line = 1;
view->search_start = 0;
view->found_len = 0;
view->search_exp = NULL;
view->direction = 1; /* forward */
view->last_search = 0; /* it's a function */
view->want_to_quit = FALSE;
view->marker = 0;
for (i = 0; i < sizeof(view->marks) / sizeof(view->marks[0]); i++)
view->marks[i] = 0;
view->move_dir = 0;
view->update_steps = 0;
view->update_activate = 0;
1998-02-27 07:54:42 +03:00
return view;
}