mirror of
https://github.com/MidnightCommander/mc
synced 2024-12-23 21:06:52 +03:00
2da73069b9
to view_help_cmd(). (view_handle_key) [HAVE_X]: Disable '!' and Ctrl-O.
2602 lines
63 KiB
C
2602 lines
63 KiB
C
/* {{{ Copyright notice */
|
|
|
|
/* View file module for the Midnight Commander
|
|
Copyright (C) 1994, 1995, 1996 The Free Software Foundation
|
|
Written by: 1994, 1995, 1998 Miguel de Icaza
|
|
1994, 1995 Janne Kukonlehto
|
|
1995 Jakub Jelinek
|
|
1996 Joseph M. Hinkle
|
|
1997 Norbert Warmuth
|
|
1998 Pavel Machek
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
|
|
/* }}} */
|
|
/* {{{ Declarations */
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#ifdef NEEDS_IO_H
|
|
# include <io.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#ifdef HAVE_MMAP
|
|
# include <sys/mman.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
#include <ctype.h> /* For toupper() */
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <sys/param.h>
|
|
|
|
#include "global.h"
|
|
#include "tty.h"
|
|
#include "x.h"
|
|
#include "cmd.h" /* For view_other_cmd */
|
|
#include "dlg.h" /* Needed by widget.h */
|
|
#include "widget.h" /* Needed for buttonbar_new */
|
|
#include "color.h"
|
|
#include "dialog.h"
|
|
#include "mouse.h"
|
|
#include "help.h"
|
|
#include "key.h" /* For mi_getch() */
|
|
#include "layout.h"
|
|
#include "setup.h"
|
|
#include "wtools.h" /* For query_set_sel() */
|
|
#if defined(HAVE_RX_H) && defined(HAVE_REGCOMP)
|
|
# include <rx.h>
|
|
#else
|
|
# include <regex.h>
|
|
#endif
|
|
#include "../vfs/vfs.h"
|
|
#include "dir.h"
|
|
#include "panel.h" /* Needed for current_panel and other_panel */
|
|
#include "win.h"
|
|
#include "main.h" /* For the externs */
|
|
#define WANT_WIDGETS
|
|
#include "view.h"
|
|
|
|
#ifdef HAVE_CHARSET
|
|
#include "charsets.h"
|
|
#include "selcodepage.h"
|
|
#endif
|
|
|
|
#ifndef MAP_FILE
|
|
#define MAP_FILE 0
|
|
#endif
|
|
|
|
/* Block size for reading files in parts */
|
|
#define READ_BLOCK 8192
|
|
#define VIEW_PAGE_SIZE 8192
|
|
|
|
#ifdef IS_AIX
|
|
# define IFAIX(x) case (x):
|
|
#else
|
|
# define IFAIX(x)
|
|
#endif
|
|
|
|
/* Maxlimit for skipping updates */
|
|
int max_dirt_limit =
|
|
#ifdef OS2_NT
|
|
0;
|
|
#else
|
|
10;
|
|
#endif
|
|
|
|
#ifdef HAVE_X
|
|
#define is_idle() 1
|
|
#endif
|
|
|
|
extern Hook *idle_hook;
|
|
|
|
/* Our callback */
|
|
static int view_callback (Dlg_head *h, WView *view, int msg, int par);
|
|
static int regexp_view_search (WView *view, char *pattern, char *string, int match_type);
|
|
|
|
/* If set, show a ruler */
|
|
static int ruler = 0;
|
|
|
|
/* Scrolling is done in pages or line increments */
|
|
int mouse_move_pages_viewer = 1;
|
|
|
|
/* Used to compute the bottom first variable */
|
|
int have_fast_cpu = 0;
|
|
|
|
/* wrap mode default */
|
|
int global_wrap_mode = 1;
|
|
|
|
int default_hex_mode = 0;
|
|
int default_hexedit_mode = 0;
|
|
int default_magic_flag = 1;
|
|
int default_nroff_flag = 1;
|
|
int altered_hex_mode = 0;
|
|
int altered_magic_flag = 0;
|
|
int altered_nroff_flag = 0;
|
|
/* }}} */
|
|
|
|
/* "$Id$" */
|
|
|
|
static const char hex_char[] = "0123456789ABCDEF";
|
|
|
|
/* }}} */
|
|
/* {{{ Clean-up functions */
|
|
|
|
static void
|
|
close_view_file (WView *view)
|
|
{
|
|
if (view->file != -1){
|
|
mc_close (view->file);
|
|
view->file = -1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
free_file (WView *view)
|
|
{
|
|
int i;
|
|
|
|
#ifdef HAVE_MMAP
|
|
if (view->mmapping){
|
|
mc_munmap (view->data, view->s.st_size);
|
|
close_view_file (view);
|
|
} else
|
|
#endif
|
|
{
|
|
if (view->reading_pipe){
|
|
/* Check error messages */
|
|
if (!view->have_frame)
|
|
check_error_pipe ();
|
|
|
|
/* Close pipe */
|
|
pclose (view->stdfile);
|
|
view->stdfile = NULL;
|
|
|
|
/* Ignore errors because we don't want to hear about broken pipe */
|
|
close_error_pipe (-1, NULL);
|
|
} else
|
|
close_view_file (view);
|
|
}
|
|
/* Block_ptr may be zero if the file was a file with 0 bytes */
|
|
if (view->growing_buffer && view->block_ptr){
|
|
for (i = 0; i < view->blocks; i++){
|
|
g_free (view->block_ptr [i].data);
|
|
}
|
|
g_free (view->block_ptr);
|
|
}
|
|
}
|
|
|
|
/* Valid parameters for second parameter to set_monitor */
|
|
enum { off, on };
|
|
|
|
/* Both views */
|
|
static void
|
|
view_done (WView *view)
|
|
{
|
|
set_monitor (view, off);
|
|
|
|
/* alex: release core, used to replace mmap */
|
|
if (!view->mmapping && !view->growing_buffer && view->data != NULL){
|
|
g_free (view->data);
|
|
view->data = NULL;
|
|
}
|
|
|
|
if (view->view_active){
|
|
if (view->localcopy)
|
|
mc_ungetlocalcopy (view->filename, view->localcopy, 0);
|
|
free_file (view);
|
|
g_free (view->filename);
|
|
if (view->command)
|
|
g_free (view->command);
|
|
}
|
|
view->view_active = 0;
|
|
default_hex_mode = view->hex_mode;
|
|
default_nroff_flag = view->viewer_nroff_flag;
|
|
default_magic_flag = view->viewer_magic_flag;
|
|
global_wrap_mode = view->wrap_mode;
|
|
}
|
|
|
|
static void view_hook (void *);
|
|
|
|
static void
|
|
view_destroy (WView *view)
|
|
{
|
|
view_done (view);
|
|
if (view->have_frame)
|
|
delete_hook (&select_file_hook, view_hook);
|
|
x_destroy_view (view);
|
|
}
|
|
|
|
static int
|
|
get_byte (WView *view, unsigned int byte_index)
|
|
{
|
|
int page = byte_index / VIEW_PAGE_SIZE + 1;
|
|
int offset = byte_index % VIEW_PAGE_SIZE;
|
|
int i, n;
|
|
|
|
if (view->growing_buffer){
|
|
if (page > view->blocks){
|
|
view->block_ptr = g_realloc (view->block_ptr,
|
|
sizeof (block_ptr_t) * page);
|
|
for (i = view->blocks; i < page; i++){
|
|
char *p = g_malloc (VIEW_PAGE_SIZE);
|
|
view->block_ptr [i].data = p;
|
|
if (!p)
|
|
return '\n';
|
|
if (view->stdfile != NULL)
|
|
n = fread (p, 1, VIEW_PAGE_SIZE, view->stdfile);
|
|
else
|
|
n = mc_read (view->file, p, VIEW_PAGE_SIZE);
|
|
/*
|
|
* FIXME: Errors are ignored at this point
|
|
* Also should report preliminary EOF
|
|
*/
|
|
if (n != -1)
|
|
view->bytes_read += n;
|
|
if (view->s.st_size < view->bytes_read){
|
|
view->bottom_first = -1; /* Invalidate cache */
|
|
view->s.st_size = view->bytes_read;
|
|
view->last_byte = view->bytes_read;
|
|
if (view->reading_pipe)
|
|
view->last_byte = view->first + view->bytes_read;
|
|
}
|
|
/* To force loading the next page */
|
|
if (n == VIEW_PAGE_SIZE && view->reading_pipe){
|
|
view->last_byte++;
|
|
}
|
|
}
|
|
view->blocks = page;
|
|
}
|
|
if (byte_index > view->bytes_read){
|
|
return -1;
|
|
} else
|
|
return view->block_ptr [page-1].data [offset];
|
|
} else {
|
|
if (byte_index >= view->last_byte)
|
|
return -1;
|
|
else
|
|
return view->data [byte_index];
|
|
}
|
|
}
|
|
|
|
static void
|
|
enqueue_change (struct hexedit_change_node **head, struct hexedit_change_node *node)
|
|
{
|
|
struct hexedit_change_node *curr = *head;
|
|
|
|
while (curr) {
|
|
if (node->offset < curr->offset) {
|
|
*head = node;
|
|
node->next = curr;
|
|
return;
|
|
}
|
|
head = (struct hexedit_change_node **) curr;
|
|
curr = curr->next;
|
|
}
|
|
*head = node;
|
|
node->next = curr;
|
|
}
|
|
|
|
static void move_right (WView *);
|
|
|
|
static void
|
|
put_editkey (WView *view, unsigned char key)
|
|
{
|
|
struct hexedit_change_node *node;
|
|
unsigned char byte_val;
|
|
|
|
if (!view->hexedit_mode || view->growing_buffer != 0)
|
|
return;
|
|
|
|
/* Has there been a change at this position ? */
|
|
node = view->change_list;
|
|
while (node && (node->offset != view->edit_cursor)) {
|
|
node = node->next;
|
|
}
|
|
|
|
if (view->view_side == view_side_left) {
|
|
/* Hex editing */
|
|
|
|
if (key >= '0' && key <= '9')
|
|
key -= '0';
|
|
else if (key >= 'A' && key <= 'F')
|
|
key -= '7';
|
|
else if (key >= 'a' && key <= 'f')
|
|
key -= 'W';
|
|
else
|
|
return;
|
|
|
|
if (node)
|
|
byte_val = node->value;
|
|
else
|
|
byte_val = get_byte(view, view->edit_cursor);
|
|
|
|
if (view->nib_shift == 0) {
|
|
byte_val = (byte_val & 0x0f) | (key << 4);
|
|
} else {
|
|
byte_val = (byte_val & 0xf0) | (key);
|
|
}
|
|
} else {
|
|
/* Text editing */
|
|
byte_val = key;
|
|
}
|
|
if (!node) {
|
|
node = (struct hexedit_change_node *)
|
|
g_new (struct hexedit_change_node, 1);
|
|
|
|
if (node) {
|
|
#ifndef HAVE_MMAP
|
|
/* alex@bcs.zaporizhzhe.ua: here we are using file copy
|
|
* completely loaded into memory, so we can replace bytes in
|
|
* view->data array to allow changes to be reflected when
|
|
* user switches back to ascii mode */
|
|
view->data[view->edit_cursor] = byte_val;
|
|
#endif /* HAVE_MMAP */
|
|
node->offset = view->edit_cursor;
|
|
node->value = byte_val;
|
|
enqueue_change (&view->change_list, node);
|
|
}
|
|
} else {
|
|
node->value = byte_val;
|
|
}
|
|
view->dirty++;
|
|
view_update (view, TRUE);
|
|
move_right (view);
|
|
}
|
|
|
|
static void
|
|
free_change_list (WView *view)
|
|
{
|
|
struct hexedit_change_node *n = view->change_list;
|
|
|
|
while (n) {
|
|
view->change_list = n->next;
|
|
g_free (n);
|
|
n = view->change_list;
|
|
}
|
|
view->file_dirty = 0;
|
|
view->dirty++;
|
|
}
|
|
|
|
static void
|
|
save_edit_changes (WView *view)
|
|
{
|
|
struct hexedit_change_node *node = view->change_list;
|
|
int fp;
|
|
|
|
fp = open (view->filename, O_WRONLY);
|
|
if (fp >= 0) {
|
|
while (node) {
|
|
lseek (fp, node->offset, SEEK_SET);
|
|
write (fp, &node->value, 1);
|
|
node = node->next;
|
|
}
|
|
close (fp);
|
|
}
|
|
free_change_list (view);
|
|
}
|
|
|
|
int
|
|
view_ok_to_quit (WView *view)
|
|
{
|
|
int r;
|
|
char *text;
|
|
|
|
if (!view->change_list)
|
|
return 1;
|
|
|
|
query_set_sel (1);
|
|
text = g_strconcat (_("File: \n\n "), view->filename,
|
|
_("\n\nhas been modified, do you want to save the changes?\n"), NULL);
|
|
|
|
r = query_dialog (_(" Save changes "), text, 2, 3, _("&Yes"), _("&No"), _("&Cancel"));
|
|
g_free (text);
|
|
|
|
switch (r) {
|
|
case 0:
|
|
save_edit_changes (view);
|
|
return 1;
|
|
case 1:
|
|
free_change_list (view);
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static char *
|
|
set_view_init_error (WView *view, char *msg)
|
|
{
|
|
view->growing_buffer = 0;
|
|
view->reading_pipe = 0;
|
|
view->first = 0;
|
|
view->last_byte = 0;
|
|
if (msg){
|
|
view->bytes_read = strlen (msg);
|
|
return g_strdup (msg);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* return values: NULL for success, else points to error message */
|
|
static char *
|
|
init_growing_view (WView *view, char *name, char *filename)
|
|
{
|
|
view->growing_buffer = 1;
|
|
|
|
if (name){
|
|
view->reading_pipe = 1;
|
|
view->s.st_size = 0;
|
|
|
|
open_error_pipe ();
|
|
if ((view->stdfile = popen (name, "r")) == NULL){
|
|
close_error_pipe (view->have_frame?-1:1, view->data);
|
|
return set_view_init_error (view, _(" Can't spawn child program "));
|
|
}
|
|
|
|
/* First, check if filter produced any output */
|
|
get_byte (view, 0);
|
|
if (view->bytes_read <= 0){
|
|
pclose (view->stdfile);
|
|
view->stdfile = NULL;
|
|
close_error_pipe (view->have_frame?-1:1, view->data);
|
|
return set_view_init_error (view, _(" Empty output from child filter "));
|
|
}
|
|
} else {
|
|
view->stdfile = NULL;
|
|
if ((view->file = mc_open (filename, O_RDONLY)) == -1)
|
|
return set_view_init_error (view, _(" Could not open file "));
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Load filename into core */
|
|
/* returns:
|
|
-1 on failure.
|
|
if (have_frame), we return success, but data points to a
|
|
error message instead of the file buffer (quick_view feature).
|
|
*/
|
|
static char *load_view_file (WView *view, char *filename)
|
|
{
|
|
if ((view->file = mc_open (filename, O_RDONLY)) < 0){
|
|
set_view_init_error (view, 0);
|
|
return ( g_strconcat (_(" Can't open file \""),
|
|
filename, "\"\n ",
|
|
unix_error_string (errno), " ", NULL));
|
|
}
|
|
if (mc_fstat (view->file, &view->s) < 0){
|
|
set_view_init_error (view, 0);
|
|
close_view_file (view);
|
|
return g_strconcat (_(" Can't stat file \n "),
|
|
unix_error_string (errno), " ", NULL);
|
|
}
|
|
if (S_ISDIR (view->s.st_mode) || S_ISSOCK (view->s.st_mode)
|
|
|| S_ISFIFO (view->s.st_mode)){
|
|
close_view_file (view);
|
|
return set_view_init_error (view, _(" Can't view: not a regular file "));
|
|
}
|
|
|
|
if (view->s.st_size == 0){
|
|
/* Must be one of those nice files that grow (/proc) */
|
|
close_view_file (view);
|
|
return init_growing_view (view, 0, filename);
|
|
}
|
|
|
|
#ifdef HAVE_MMAP
|
|
view->data = mc_mmap (0, view->s.st_size, PROT_READ, MAP_FILE | MAP_SHARED,
|
|
view->file, 0);
|
|
if ((caddr_t) view->data == (caddr_t) -1)
|
|
goto no_mmap;
|
|
|
|
view->first = 0;
|
|
view->bytes_read = view->s.st_size;
|
|
view->mmapping = 1;
|
|
return NULL;
|
|
#endif
|
|
|
|
no_mmap:
|
|
/* For those OS that dont provide mmap call. Try to load all the
|
|
* file into memory (alex@bcs.zaporizhzhe.ua). Also, mmap can fail
|
|
* for any reason, so we use this as fallback (pavel@ucw.cz) */
|
|
|
|
view->data = (unsigned char*) g_malloc (view->s.st_size);
|
|
if (view->data == NULL
|
|
|| mc_lseek(view->file,0,0) != 0
|
|
|| mc_read(view->file, view->data, view->s.st_size) != view->s.st_size){
|
|
if (view->data != NULL)
|
|
g_free (view->data);
|
|
close_view_file (view);
|
|
return init_growing_view (view, 0, filename);
|
|
}
|
|
view->first = 0;
|
|
view->bytes_read = view->s.st_size;
|
|
return NULL;
|
|
}
|
|
|
|
/* Return zero on success, -1 on failure */
|
|
static int
|
|
do_view_init (WView *view, char *_command, const char *_file, int start_line)
|
|
{
|
|
char *error = 0;
|
|
int i, type;
|
|
|
|
if (view->view_active)
|
|
view_done (view);
|
|
|
|
/* Set up the state */
|
|
view->block_ptr = 0;
|
|
view->data = NULL;
|
|
view->growing_buffer = 0;
|
|
view->reading_pipe = 0;
|
|
view->mmapping = 0;
|
|
view->blocks = 0;
|
|
view->block_ptr = 0;
|
|
view->first = view->bytes_read = 0;
|
|
view->last_byte = 0;
|
|
view->filename = 0;
|
|
view->localcopy = 0;
|
|
view->command = 0;
|
|
view->last = view->first + ((LINES-2) * view->bytes_per_line);
|
|
|
|
/* Clear the markers */
|
|
view->marker = 0;
|
|
for (i = 0; i < 10; i++)
|
|
view->marks [i] = 0;
|
|
|
|
|
|
if (!view->have_frame){
|
|
view->start_col = 0;
|
|
}
|
|
|
|
{
|
|
int fd;
|
|
|
|
if ((fd = mc_open(_file, O_RDONLY)) == -1) {
|
|
char tmp[BUF_MEDIUM];
|
|
g_snprintf (tmp, sizeof (tmp),
|
|
_(" Can't open \"%s\"\n %s "),
|
|
_file, unix_error_string (errno));
|
|
view->filename = g_strdup (_file);
|
|
error = set_view_init_error (view, tmp);
|
|
goto finish;
|
|
}
|
|
if (mc_fstat (fd, &view->s) == -1) {
|
|
char tmp[BUF_MEDIUM];
|
|
g_snprintf (tmp, sizeof (tmp),
|
|
_(" Can't stat \"%s\"\n %s "),
|
|
_file, unix_error_string (errno));
|
|
mc_close(fd);
|
|
view->filename = g_strdup (_file);
|
|
error = set_view_init_error (view, tmp);
|
|
goto finish;
|
|
}
|
|
|
|
if (_file[0] && view->viewer_magic_flag && (is_gunzipable (fd, &type)) != 0)
|
|
view->filename = g_strconcat (_file, decompress_extension(type), NULL);
|
|
else
|
|
view->filename = g_strdup (_file);
|
|
mc_close(fd);
|
|
}
|
|
|
|
if (_command && (view->viewer_magic_flag || _file[0] == '\0'))
|
|
error = init_growing_view (view, _command, view->filename);
|
|
else
|
|
error = load_view_file (view, view->filename);
|
|
|
|
finish:
|
|
if (error){
|
|
if (!view->have_frame){
|
|
message (1, MSG_ERROR, "%s", error);
|
|
g_free (error);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
view->view_active = 1;
|
|
if (_command)
|
|
view->command = g_strdup (_command);
|
|
else
|
|
view->command = 0;
|
|
view->search_start = view->start_display = view->start_save = view->first;
|
|
view->found_len = 0;
|
|
view->start_col = 0;
|
|
view->last_search = 0; /* Start a new search */
|
|
|
|
/* Special case: The data points to the error message */
|
|
if (error){
|
|
view->data = error;
|
|
view->file = -1;
|
|
view->s.st_size = view->bytes_read = strlen (view->data);
|
|
}
|
|
view->last_byte = view->first + view->s.st_size;
|
|
|
|
if (start_line > 1 && !error){
|
|
int saved_wrap_mode = view->wrap_mode;
|
|
|
|
view->wrap_mode = 0;
|
|
get_byte (view, 0);
|
|
view_move_forward (view, start_line - 1);
|
|
view->wrap_mode = saved_wrap_mode;
|
|
}
|
|
view->edit_cursor = view->first;
|
|
view->file_dirty = 0;
|
|
view->nib_shift = 0;
|
|
view->view_side = view_side_left;
|
|
view->change_list = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
view_update_bytes_per_line(WView *view)
|
|
{
|
|
int cols;
|
|
|
|
if (view->have_frame)
|
|
cols = view->widget.cols - 2;
|
|
else
|
|
cols = view->widget.cols;
|
|
|
|
view->bottom_first = -1;
|
|
view->bytes_per_line = ((2 * (cols - 8) / 9) & 0xfffc);
|
|
|
|
if (view->bytes_per_line == 0)
|
|
view->bytes_per_line++; /* To avoid division by 0 */
|
|
|
|
view->dirty = max_dirt_limit + 1; /* To force refresh */
|
|
}
|
|
|
|
/* Both views */
|
|
/* Return zero on success, -1 on failure */
|
|
int
|
|
view_init (WView *view, char *_command, const char *_file, int start_line)
|
|
{
|
|
view_update_bytes_per_line(view);
|
|
|
|
if (!view->view_active || strcmp (_file, view->filename) || altered_magic_flag)
|
|
return do_view_init (view, _command, _file, start_line);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ Screen update functions */
|
|
|
|
#ifndef HAVE_X
|
|
void
|
|
view_percent (WView *view, int p, int w, gboolean update_gui)
|
|
{
|
|
int percent;
|
|
|
|
percent = (view->s.st_size == 0 || view->last_byte == view->last) ? 100 :
|
|
(p > (INT_MAX/100) ?
|
|
p / (view->s.st_size / 100) :
|
|
p * 100 / view->s.st_size);
|
|
|
|
#if 0
|
|
percent = view->s.st_size == 0 ? 100 :
|
|
(view->last_byte == view->last ? 100 :
|
|
(p)*100 / view->s.st_size);
|
|
#endif
|
|
|
|
widget_move (view, view->have_frame, w - 5);
|
|
printw ("%3d%%", percent);
|
|
}
|
|
|
|
void
|
|
view_status (WView *view, gboolean update_gui)
|
|
{
|
|
static int i18n_adjust=0;
|
|
static char *file_label;
|
|
|
|
int w = view->widget.cols - (view->have_frame * 2);
|
|
int i;
|
|
|
|
attrset (SELECTED_COLOR);
|
|
widget_move (view, view->have_frame, view->have_frame);
|
|
hline (' ', w);
|
|
|
|
if (!i18n_adjust) {
|
|
file_label = _("File: %s");
|
|
i18n_adjust = strlen(file_label) - 2;
|
|
}
|
|
|
|
if (w < i18n_adjust + 6)
|
|
addstr (name_trunc (view->filename ? view->filename:
|
|
view->command ? view->command:"", w));
|
|
else{
|
|
i = (w > 22 ? 22 : w ) - i18n_adjust ;
|
|
printw (file_label, name_trunc (view->filename ? view->filename:
|
|
view->command ? view->command:"", i));
|
|
if (w > 46){
|
|
widget_move (view, view->have_frame, 24 + view->have_frame );
|
|
if (view->hex_mode)
|
|
printw (_("Offset 0x%08x"), view->edit_cursor);
|
|
else
|
|
printw (_("Col %d"), -view->start_col);
|
|
}
|
|
if (w > 62){
|
|
widget_move (view, view->have_frame, 43 + view->have_frame);
|
|
printw (_("%s bytes"), size_trunc (view->s.st_size));
|
|
}
|
|
if (w > 70){
|
|
printw (" ");
|
|
if (view->growing_buffer)
|
|
addstr (_(" [grow]"));
|
|
}
|
|
if (w > 26) {
|
|
view_percent (view,
|
|
view->hex_mode ? view->edit_cursor : view->start_display,
|
|
view->widget.cols - view->have_frame + 1,
|
|
update_gui);
|
|
}
|
|
}
|
|
attrset (SELECTED_COLOR);
|
|
}
|
|
|
|
#define view_set_color(view,font) attrset (font)
|
|
|
|
static inline void
|
|
view_display_clean (WView *view, int height, int width)
|
|
{
|
|
/* FIXME: Should I use widget_erase only and repaint the box? */
|
|
if (view->have_frame){
|
|
int i;
|
|
|
|
draw_double_box (view->widget.parent, view->widget.y, view->widget.x,
|
|
view->widget.lines, view->widget.cols);
|
|
for (i = 1; i < height; i++){
|
|
widget_move (view, i, 1);
|
|
printw ("%*s", width-1, "");
|
|
}
|
|
} else
|
|
widget_erase ((Widget *) view);
|
|
}
|
|
|
|
#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)
|
|
#endif
|
|
|
|
/* Both the text mode and gnome editions use this */
|
|
#define BOLD_COLOR MARKED_COLOR
|
|
#define UNDERLINE_COLOR VIEW_UNDERLINED_COLOR
|
|
#define MARK_COLOR SELECTED_COLOR
|
|
#define DEF_COLOR NORMAL_COLOR
|
|
|
|
#ifndef PORT_HAS_VIEW_FREEZE
|
|
# define view_freeze(view)
|
|
# define view_thaw(view)
|
|
#endif
|
|
|
|
#ifdef HAVE_GNOME
|
|
# define PICK_COLOR(a,b) BOLD_COLOR : DEF_COLOR
|
|
#else
|
|
# define PICK_COLOR(a,b) a : b
|
|
#endif
|
|
|
|
/* Shows the file pointed to by *start_display on view_win */
|
|
static long
|
|
display (WView *view)
|
|
{
|
|
#ifdef HAVE_X
|
|
# define frame_shift 0
|
|
# define STATUS_LINES 0
|
|
#else
|
|
const int frame_shift = view->have_frame;
|
|
# define STATUS_LINES 1
|
|
#endif
|
|
int col = 0 + frame_shift;
|
|
int row = STATUS_LINES + frame_shift;
|
|
int height, width;
|
|
unsigned long from;
|
|
int c;
|
|
int boldflag = 0;
|
|
struct hexedit_change_node *curr = view->change_list;
|
|
|
|
height = view->widget.lines - frame_shift;
|
|
width = view->widget.cols - frame_shift;
|
|
from = view->start_display;
|
|
view_set_color (view, DEF_COLOR);
|
|
|
|
view_freeze (view);
|
|
view_display_clean (view, height, width);
|
|
|
|
/* Optionally, display a ruler */
|
|
if ((!view->hex_mode) && (ruler)){
|
|
char r_buff[10];
|
|
int cl;
|
|
|
|
view_set_color (view, BOLD_COLOR);
|
|
for (c = frame_shift; c < width; c++) {
|
|
cl = c-view->start_col;
|
|
if (ruler == 1)
|
|
view_gotoyx (view, row, c);
|
|
else
|
|
view_gotoyx (view, row+height-2, c);
|
|
r_buff[0] = '-';
|
|
if ((cl % 10) == 0)
|
|
r_buff[0] = '|';
|
|
else
|
|
if ((cl % 5) == 0)
|
|
r_buff[0] = '*';
|
|
view_add_character (view, r_buff[0]);
|
|
if ((cl != 0) && (cl % 10) == 0){
|
|
g_snprintf(r_buff, sizeof (r_buff), "%03d", cl);
|
|
if (ruler == 1) {
|
|
widget_move (view, row + 1, c - 1);
|
|
} else {
|
|
widget_move (view, row + height - 3, c - 1);
|
|
}
|
|
view_add_string (view, r_buff);
|
|
}
|
|
}
|
|
view_set_color (view, DEF_COLOR);
|
|
if (ruler == 1)
|
|
row += 2;
|
|
else
|
|
height -= 2;
|
|
}
|
|
|
|
/* Find the first displayable changed byte */
|
|
while (curr && (curr->offset < from)) {
|
|
curr = curr->next;
|
|
}
|
|
if (view->hex_mode){
|
|
char hex_buff[10]; /* A temporary buffer for sprintf and mvwaddstr */
|
|
int bytes; /* Number of bytes already printed on the line */
|
|
|
|
/* Start of text column */
|
|
int text_start = width - view->bytes_per_line - 1 + frame_shift;
|
|
|
|
for (;row < height && from < view->last_byte; row++){
|
|
/* Print the hex offset */
|
|
view_set_color (view, BOLD_COLOR);
|
|
g_snprintf (hex_buff, sizeof (hex_buff), "%08X", (int) (from - view->first));
|
|
view_gotoyx (view, row, frame_shift);
|
|
view_add_string (view, hex_buff);
|
|
view_set_color (view, DEF_COLOR);
|
|
|
|
/* Hex dump starts from column nine */
|
|
col = 9;
|
|
|
|
/* Each hex number is two digits */
|
|
hex_buff[2] = 0;
|
|
for (bytes = 0; bytes < view->bytes_per_line
|
|
&& from < view->last_byte; bytes++, from++){
|
|
/* Display and mark changed bytes */
|
|
if (curr && from == curr->offset) {
|
|
c = curr->value;
|
|
curr = curr->next;
|
|
boldflag = 3;
|
|
view_set_color (view, 7);
|
|
} else
|
|
c = (unsigned char) get_byte (view, from);
|
|
|
|
if (view->found_len && from >= view->search_start
|
|
&& from < view->search_start + view->found_len){
|
|
boldflag = 1;
|
|
view_set_color (view, BOLD_COLOR);
|
|
}
|
|
/* Display the navigation cursor */
|
|
if (from == view->edit_cursor) {
|
|
if (view->view_side == view_side_left){
|
|
view->cursor_row = row;
|
|
view->cursor_col = col;
|
|
}
|
|
boldflag = 2;
|
|
view_set_color (view, view->view_side == view_side_left ? PICK_COLOR (15, 33));
|
|
}
|
|
|
|
/* 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, row, col);
|
|
view_add_string (view, hex_buff);
|
|
col += 3;
|
|
/* Turn off the cursor or changed byte highlighting here */
|
|
if (boldflag > 1)
|
|
view_set_color (view, DEF_COLOR);
|
|
if ((bytes & 3) == 3 && bytes + 1 < view->bytes_per_line){
|
|
/* Turn off the search highlighting */
|
|
if (boldflag == 1 &&
|
|
from == view->search_start + view->found_len - 1)
|
|
view_set_color (view, DEF_COLOR);
|
|
|
|
/* Hex numbers are printed in the groups of four */
|
|
/* Groups are separated by a vline */
|
|
|
|
view_gotoyx (view, row, col-1);
|
|
view_add_character (view, ' ');
|
|
view_gotoyx (view, row, col);
|
|
view_add_one_vline ();
|
|
col += 2;
|
|
|
|
if (boldflag && from==view->search_start+view->found_len-1)
|
|
view_set_color (view, BOLD_COLOR);
|
|
|
|
}
|
|
if (boldflag &&
|
|
from < view->search_start + view->found_len - 1 &&
|
|
bytes != view->bytes_per_line - 1) {
|
|
view_gotoyx (view, row, col);
|
|
view_add_character (view, ' ');
|
|
}
|
|
|
|
/* Print the corresponding ascii character */
|
|
view_gotoyx (view, row, text_start + bytes);
|
|
|
|
#ifdef HAVE_CHARSET
|
|
c = conv_displ[ c ];
|
|
#endif
|
|
|
|
if (!is_printable (c))
|
|
c = '.';
|
|
switch (boldflag) {
|
|
default:
|
|
break;
|
|
case 1:
|
|
view_set_color (view, BOLD_COLOR);
|
|
goto setcursor;
|
|
case 2:
|
|
view_set_color (view, view->view_side == view_side_left ? PICK_COLOR (33, 15));
|
|
goto setcursor;
|
|
case 3:
|
|
view_set_color (view, 7);
|
|
|
|
setcursor:
|
|
if (view->view_side == view_side_right){
|
|
view->cursor_col = text_start + bytes;
|
|
view->cursor_row = row;
|
|
}
|
|
}
|
|
view_add_character (view, c);
|
|
|
|
if (boldflag){
|
|
boldflag = 0;
|
|
view_set_color (view, DEF_COLOR);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (view->growing_buffer && from == view->last_byte)
|
|
get_byte (view, from);
|
|
for (; row < height && from < view->last_byte; from++){
|
|
c = get_byte (view, from);
|
|
if ((c == '\n') || (col == width && view->wrap_mode)){
|
|
col = frame_shift;
|
|
row++;
|
|
if (c == '\n' || row >= height)
|
|
continue;
|
|
}
|
|
if (c == '\r')
|
|
continue;
|
|
if (c == '\t'){
|
|
col = ((col - frame_shift)/8)*8 + 8 + frame_shift;
|
|
continue;
|
|
}
|
|
if (view->viewer_nroff_flag && c == '\b'){
|
|
if (from + 1 < view->last_byte
|
|
&& is_printable (get_byte (view, from + 1)) &&
|
|
from > view->first
|
|
&& is_printable (get_byte (view, from - 1)))
|
|
{
|
|
if (col <= frame_shift){
|
|
/* So it has to be wrap_mode - do not need to check for it */
|
|
if (row == 1 + frame_shift){
|
|
from++;
|
|
continue; /* There had to be a bold character on the rightmost position
|
|
of the previous undisplayed line */
|
|
}
|
|
row--;
|
|
col = width;
|
|
}
|
|
col--;
|
|
boldflag = 1;
|
|
if (get_byte (view, from - 1) == '_' && get_byte (view, from + 1) != '_')
|
|
view_set_color (view, UNDERLINE_COLOR);
|
|
else
|
|
view_set_color (view, BOLD_COLOR);
|
|
continue;
|
|
}
|
|
}
|
|
if (view->found_len && from >= view->search_start
|
|
&& from < view->search_start + view->found_len){
|
|
boldflag = 1;
|
|
view_set_color (view, MARK_COLOR);
|
|
}
|
|
if (col >= frame_shift-view->start_col
|
|
&& col < width-view->start_col)
|
|
{
|
|
view_gotoyx (view, row, col+view->start_col);
|
|
|
|
#ifdef HAVE_CHARSET
|
|
c = conv_displ[ c ];
|
|
#endif
|
|
|
|
if (!is_printable (c))
|
|
c = '.';
|
|
|
|
view_add_character (view, c);
|
|
}
|
|
col++;
|
|
if (boldflag){
|
|
boldflag = 0;
|
|
view_set_color (view, DEF_COLOR);
|
|
}
|
|
|
|
/* Very last thing */
|
|
if (view->growing_buffer && from+1 == view->last_byte)
|
|
get_byte (view, from+1);
|
|
}
|
|
}
|
|
view->last = from;
|
|
view_thaw (view);
|
|
return from;
|
|
}
|
|
|
|
static void
|
|
view_place_cursor (WView *view)
|
|
{
|
|
int shift;
|
|
|
|
if (view->view_side == view_side_left)
|
|
shift = view->nib_shift;
|
|
else
|
|
shift = 0;
|
|
|
|
widget_move (&view->widget, view->cursor_row, view->cursor_col + shift);
|
|
}
|
|
|
|
void
|
|
view_update (WView *view, gboolean update_gui)
|
|
{
|
|
static int dirt_limit = 1;
|
|
|
|
if (view->dirty > dirt_limit){
|
|
/* Too many updates skipped -> force a update */
|
|
display (view);
|
|
view_status (view, update_gui);
|
|
view->dirty = 0;
|
|
/* Raise the update skipping limit */
|
|
dirt_limit++;
|
|
if (dirt_limit > max_dirt_limit)
|
|
dirt_limit = max_dirt_limit;
|
|
}
|
|
if (view->dirty){
|
|
if (is_idle ()){
|
|
/* We have time to update the screen properly */
|
|
display (view);
|
|
view_status (view, update_gui);
|
|
view->dirty = 0;
|
|
if (dirt_limit > 1)
|
|
dirt_limit--;
|
|
} else {
|
|
/* We are busy -> skipping full update,
|
|
only the status line is updated */
|
|
view_status (view, update_gui);
|
|
}
|
|
/* Here we had a refresh, if fast scrolling does not work
|
|
restore the refresh, although this should not happen */
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
my_define (Dlg_head *h, int idx, char *text,
|
|
void (*fn)(WView *), WView *view)
|
|
{
|
|
define_label_data (h, (Widget *) view, idx, text, (buttonbarfn) fn, view);
|
|
}
|
|
|
|
/* }}} */
|
|
/* {{{ Movement functions */
|
|
/* If the last parameter is nonzero, it means we want get the count of lines
|
|
from current up to the the upto position inclusive */
|
|
static long
|
|
move_forward2 (WView *view, long current, int lines, long upto)
|
|
{
|
|
unsigned long q, p;
|
|
int line;
|
|
int col = 0;
|
|
|
|
if (view->hex_mode){
|
|
p = current + lines * view->bytes_per_line;
|
|
p = (p >= view->last_byte) ? current : p;
|
|
if (lines == 1) {
|
|
q = view->edit_cursor + view->bytes_per_line;
|
|
line = q / view->bytes_per_line;
|
|
col = (view->last_byte-1) / view->bytes_per_line;
|
|
view->edit_cursor = (line > col) ? view->edit_cursor : q;
|
|
view->edit_cursor = (view->edit_cursor < view->last_byte) ?
|
|
view->edit_cursor : view->last_byte-1;
|
|
q = current + ((LINES-2) * view->bytes_per_line);
|
|
p = (view->edit_cursor < q) ? current : p;
|
|
} else {
|
|
view->edit_cursor = (view->edit_cursor < p) ?
|
|
p : view->edit_cursor;
|
|
}
|
|
return p;
|
|
} else {
|
|
if (upto){
|
|
lines = -1;
|
|
q = upto;
|
|
} else
|
|
q = view->last_byte;
|
|
if (get_byte (view, q) != '\n')
|
|
q++;
|
|
for (line = col = 0, p = current; p < q; p++){
|
|
int c;
|
|
|
|
if (lines != -1 && line >= lines)
|
|
return p;
|
|
|
|
c = get_byte (view, p);
|
|
|
|
if (view->wrap_mode){
|
|
if (c == '\r')
|
|
continue; /* This characters is never displayed */
|
|
else if (c == '\t')
|
|
col = ((col - view->have_frame)/8)*8 +8+ view->have_frame;
|
|
else
|
|
col++;
|
|
if (view->viewer_nroff_flag && c == '\b'){
|
|
if (p + 1 < view->last_byte
|
|
&& is_printable (get_byte (view, p + 1))
|
|
&& p > view->first
|
|
&& is_printable (get_byte (view, p - 1)))
|
|
col -= 2;
|
|
} else if (col == vwidth){
|
|
/* FIXME: the c in is_printable was a p, that is a bug,
|
|
I suspect I got that fix from Jakub, same applies
|
|
for d. */
|
|
int d = get_byte (view, p+2);
|
|
|
|
if (p + 2 >= view->last_byte || !is_printable (c) ||
|
|
!view->viewer_nroff_flag || get_byte (view, p + 1) != '\b' ||
|
|
!is_printable (d)){
|
|
col = 0;
|
|
|
|
if (c == '\n' || get_byte (view, p+1) != '\n')
|
|
line++;
|
|
}
|
|
} else if (c == '\n'){
|
|
line++;
|
|
col = 0;
|
|
}
|
|
} else if (c == '\n')
|
|
line++;
|
|
}
|
|
if (upto)
|
|
return line;
|
|
}
|
|
return current;
|
|
}
|
|
|
|
/* returns the new current pointer */
|
|
/* Cause even the forward routine became very complex, we in the wrap_mode
|
|
just find the nearest '\n', use move_forward2(p, 0, q) to get the count
|
|
of lines up to there and then use move_forward2(p, something, 0), which we
|
|
return */
|
|
static long
|
|
move_backward2 (WView *view, unsigned long current, int lines)
|
|
{
|
|
long p, q, pm;
|
|
int line;
|
|
|
|
if (!view->hex_mode && current == view->first)
|
|
return current;
|
|
|
|
if (view->hex_mode){
|
|
p = current - lines * view->bytes_per_line;
|
|
p = (p < view->first) ? view->first : p;
|
|
if (lines == 1) {
|
|
q = view->edit_cursor - view->bytes_per_line;
|
|
view->edit_cursor = (q < view->first) ? view->edit_cursor : q;
|
|
p = (view->edit_cursor >= current) ? current : p;
|
|
} else {
|
|
q = p + ((LINES-2) * view->bytes_per_line);
|
|
view->edit_cursor = (view->edit_cursor >= q) ?
|
|
p : view->edit_cursor;
|
|
}
|
|
return p;
|
|
} else {
|
|
if (current == view->last_byte
|
|
&& get_byte (view, current - 1) != '\n')
|
|
/* There is one virtual '\n' at the end,
|
|
so that the last line is shown */
|
|
line = 1;
|
|
else
|
|
line = 0;
|
|
for (q = p = current - 1; p >= view->first; p--)
|
|
if (get_byte (view, p) == '\n' || p == view->first) {
|
|
pm = p > view->first ? p + 1 : view->first;
|
|
if (!view->wrap_mode){
|
|
if (line == lines)
|
|
return pm;
|
|
line++;
|
|
} else {
|
|
line += move_forward2 (view, pm, 0, q);
|
|
if (line >= lines){
|
|
if (line == lines)
|
|
return pm;
|
|
else
|
|
return move_forward2 (view, pm, line - lines, 0);
|
|
}
|
|
q = p + 1;
|
|
}
|
|
}
|
|
}
|
|
return p > view->first ? p : view->first;
|
|
}
|
|
|
|
void
|
|
view_move_backward (WView *view, int i)
|
|
{
|
|
view->search_start = view->start_display =
|
|
move_backward2 (view, view->start_display, i);
|
|
view->found_len = 0;
|
|
view->last = view->first + ((LINES-2) * view->bytes_per_line);
|
|
view->dirty++;
|
|
}
|
|
|
|
static long
|
|
get_bottom_first (WView *view, int do_not_cache, int really)
|
|
{
|
|
int bottom_first;
|
|
|
|
if (!have_fast_cpu && !really)
|
|
return INT_MAX;
|
|
|
|
if (!do_not_cache && view->bottom_first != -1)
|
|
return view->bottom_first;
|
|
|
|
/* Force loading */
|
|
if (view->growing_buffer){
|
|
int old_last_byte;
|
|
|
|
old_last_byte = -1;
|
|
while (old_last_byte != view->last_byte){
|
|
old_last_byte = view->last_byte;
|
|
get_byte (view, view->last_byte+VIEW_PAGE_SIZE);
|
|
}
|
|
}
|
|
|
|
bottom_first = move_backward2 (view, view->last_byte, vheight - 1);
|
|
|
|
if (view->hex_mode)
|
|
bottom_first = (bottom_first + view->bytes_per_line - 1)
|
|
/ view->bytes_per_line * view->bytes_per_line;
|
|
view->bottom_first = bottom_first;
|
|
|
|
return view->bottom_first;
|
|
}
|
|
|
|
void
|
|
view_move_forward (WView *view, int i)
|
|
{
|
|
view->start_display = move_forward2 (view, view->start_display, i, 0);
|
|
if (!view->reading_pipe && view->start_display > get_bottom_first (view, 0, 0))
|
|
view->start_display = view->bottom_first;
|
|
view->search_start = view->start_display;
|
|
view->found_len = 0;
|
|
view->last = view->first + ((LINES-2) * view->bytes_per_line);
|
|
view->dirty++;
|
|
}
|
|
|
|
|
|
static void
|
|
move_to_top (WView *view)
|
|
{
|
|
view->search_start = view->start_display = view->first;
|
|
view->found_len = 0;
|
|
view->last = view->first + ((LINES-2) * view->bytes_per_line);
|
|
view->nib_shift = 0;
|
|
view->edit_cursor = view->start_display;
|
|
view->dirty++;
|
|
}
|
|
|
|
static void
|
|
move_to_bottom (WView *view)
|
|
{
|
|
view->search_start = view->start_display = get_bottom_first (view, 0, 1);
|
|
view->found_len = 0;
|
|
view->last = view->first + ((LINES-2) * view->bytes_per_line);
|
|
view->edit_cursor = (view->edit_cursor < view->start_display) ?
|
|
view->start_display : view->edit_cursor;
|
|
view->dirty++;
|
|
}
|
|
|
|
/* Scroll left/right the view panel functions */
|
|
static void
|
|
move_right (WView *view)
|
|
{
|
|
if (view->wrap_mode && !view->hex_mode)
|
|
return;
|
|
if (view->hex_mode) {
|
|
view->last = view->first + ((LINES-2) * view->bytes_per_line);
|
|
|
|
if (view->hex_mode && view->view_side == view_side_left) {
|
|
view->nib_shift = 1 - view->nib_shift;
|
|
if (view->nib_shift == 1)
|
|
return;
|
|
}
|
|
view->edit_cursor = (++view->edit_cursor < view->last_byte) ?
|
|
view->edit_cursor : view->last_byte - 1;
|
|
if (view->edit_cursor >= view->last) {
|
|
view->edit_cursor -= view->bytes_per_line;
|
|
view_move_forward(view, 1);
|
|
}
|
|
} else
|
|
if (--view->start_col > 0)
|
|
view->start_col = 0;
|
|
view->dirty++;
|
|
}
|
|
|
|
static void
|
|
move_left (WView *view)
|
|
{
|
|
if (view->wrap_mode && !view->hex_mode)
|
|
return;
|
|
if (view->hex_mode) {
|
|
if (view->hex_mode && view->view_side == view_side_left) {
|
|
view->nib_shift = 1 - view->nib_shift;
|
|
if (view->nib_shift == 0)
|
|
return;
|
|
}
|
|
if (view->edit_cursor > view->first)
|
|
--view->edit_cursor;
|
|
if (view->edit_cursor < view->start_display) {
|
|
view->edit_cursor += view->bytes_per_line;
|
|
view_move_backward(view, 1);
|
|
}
|
|
} else
|
|
if (++view->start_col > 0)
|
|
view->start_col = 0;
|
|
view->dirty++;
|
|
}
|
|
|
|
/* }}} */
|
|
/* {{{ Search routines */
|
|
|
|
/* Case insensitive search of text in data */
|
|
static int
|
|
icase_search_p (WView *view, char *text, char *data, int nothing)
|
|
{
|
|
char *q;
|
|
int lng;
|
|
|
|
if ((q = _icase_search (text, data, &lng)) != 0) {
|
|
view->found_len = lng;
|
|
view->search_start = q - data - lng;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static char *
|
|
grow_string_buffer (char *text, int *size)
|
|
{
|
|
char *new;
|
|
|
|
/* The grow steps */
|
|
*size += 160;
|
|
new = g_realloc (text, *size);
|
|
if (!text){
|
|
*new = 0;
|
|
}
|
|
return new;
|
|
}
|
|
|
|
static char *
|
|
get_line_at (WView *view, unsigned long *p, unsigned long *skipped)
|
|
{
|
|
char *buffer = 0;
|
|
int buffer_size = 0;
|
|
int usable_size = 0;
|
|
int ch;
|
|
int direction = view->direction;
|
|
unsigned long pos = *p;
|
|
long i = 0;
|
|
int prev = 0;
|
|
|
|
/* skip over all the possible zeros in the file */
|
|
while ((ch = get_byte (view, pos)) == 0) {
|
|
pos += direction; i++;
|
|
}
|
|
*skipped = i;
|
|
|
|
if (pos) {
|
|
prev = get_byte (view, pos - 1);
|
|
if ((prev == -1) || (prev == '\n'))
|
|
prev = 0;
|
|
}
|
|
|
|
for (i = 0; ch != -1; ch = get_byte (view, pos)){
|
|
|
|
if (i == usable_size){
|
|
buffer = grow_string_buffer (buffer, &buffer_size);
|
|
usable_size = buffer_size - 2;
|
|
}
|
|
|
|
pos += direction; i++;
|
|
|
|
if (ch == '\n' || !ch){
|
|
break;
|
|
}
|
|
buffer [i] = ch;
|
|
}
|
|
if (buffer){
|
|
buffer [0] = prev;
|
|
buffer [i] = 0;
|
|
|
|
/* If we are searching backwards, reverse the string */
|
|
if (direction < 0) {
|
|
reverse_string (buffer + 1);
|
|
}
|
|
}
|
|
|
|
*p = pos;
|
|
return buffer;
|
|
}
|
|
|
|
/** Search status optmizations **/
|
|
|
|
/* The number of bytes between percent increments */
|
|
int update_steps;
|
|
|
|
/* Last point where we updated the status */
|
|
long update_activate;
|
|
|
|
static void
|
|
search_update_steps (WView *view)
|
|
{
|
|
if (view->s.st_size)
|
|
update_steps = 40000;
|
|
else
|
|
update_steps = view->last_byte / 100;
|
|
|
|
/* Do not update the percent display but every 20 ks */
|
|
if (update_steps < 20000)
|
|
update_steps = 20000;
|
|
}
|
|
|
|
#ifdef HAVE_GNOME
|
|
static void
|
|
cancel_pressed (GtkWidget *widget, int *abort)
|
|
{
|
|
*abort = 1;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
search (WView *view, char *text, int (*search)(WView *, char *, char *, int))
|
|
{
|
|
int w = view->widget.cols - view->have_frame + 1;
|
|
|
|
char *s = NULL; /* The line we read from the view buffer */
|
|
long p, beginning;
|
|
int found_len, search_start;
|
|
int search_status;
|
|
#ifdef HAVE_GNOME
|
|
char *msg;
|
|
int abort;
|
|
GtkWidget *gd;
|
|
#else
|
|
Dlg_head *d = 0;
|
|
#endif
|
|
|
|
/* 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 */
|
|
long forward_line_start;
|
|
long reverse_line_start;
|
|
long t;
|
|
/* Clear interrupt status */
|
|
got_interrupt ();
|
|
|
|
#ifdef HAVE_GNOME
|
|
abort = 0;
|
|
msg = g_strdup_printf (_("Searching for `%s'"), text);
|
|
gd = gnome_message_box_new (msg,
|
|
GNOME_MESSAGE_BOX_INFO,
|
|
GNOME_STOCK_BUTTON_CANCEL,
|
|
NULL);
|
|
g_free (msg);
|
|
gnome_dialog_button_connect (GNOME_DIALOG (gd), 0, GTK_SIGNAL_FUNC (cancel_pressed), &abort);
|
|
gtk_widget_show (gd);
|
|
#else
|
|
if (verbose){
|
|
d = message (D_INSERT, _(" Search "), _("Searching %s"), text);
|
|
mc_refresh ();
|
|
}
|
|
#endif
|
|
|
|
found_len = view->found_len;
|
|
search_start = view->search_start;
|
|
|
|
if (view->direction == 1){
|
|
p = found_len ? search_start + 1 : search_start;
|
|
} else {
|
|
p = (found_len ? search_start : view->last) - 1;
|
|
}
|
|
beginning = p;
|
|
|
|
/* Compute the percent steps */
|
|
search_update_steps (view);
|
|
update_activate = 0;
|
|
|
|
for (; ; g_free (s)){
|
|
#ifdef PORT_HAS_FLUSH_EVENTS
|
|
static int count;
|
|
|
|
if ((count++ % 32) == 0)
|
|
x_flush_events ();
|
|
#ifdef HAVE_GNOME
|
|
if (abort)
|
|
break;
|
|
#else
|
|
if (verbose && !d->running)
|
|
break;
|
|
#endif
|
|
#endif
|
|
if (p >= update_activate){
|
|
update_activate += update_steps;
|
|
if (verbose){
|
|
view_percent (view, p, w, TRUE);
|
|
mc_refresh ();
|
|
}
|
|
if (got_interrupt ())
|
|
break;
|
|
}
|
|
forward_line_start = p;
|
|
disable_interrupt_key ();
|
|
s = get_line_at (view, &p, &t);
|
|
reverse_line_start = p;
|
|
enable_interrupt_key ();
|
|
|
|
if (!s)
|
|
break;
|
|
|
|
search_status = (*search) (view, text, s + 1, match_normal);
|
|
if (search_status < 0){
|
|
g_free (s);
|
|
break;
|
|
}
|
|
|
|
if (search_status == 0)
|
|
continue;
|
|
|
|
/* We found the string */
|
|
|
|
if (*s && !view->search_start && (search == regexp_view_search) && (*text == '^')){
|
|
|
|
/* We do not want to match a
|
|
* ^ regexp when not at the real
|
|
* beginning of some line
|
|
*/
|
|
continue;
|
|
}
|
|
/* Record the position used to continue the search */
|
|
if (view->direction == 1)
|
|
t += forward_line_start;
|
|
else
|
|
t += reverse_line_start ? reverse_line_start + 3 : 0;
|
|
view->search_start += t;
|
|
|
|
if (t != beginning){
|
|
if (t > get_bottom_first (view, 0, 0))
|
|
view->start_display = view->bottom_first;
|
|
else
|
|
view->start_display = t;
|
|
}
|
|
|
|
g_free (s);
|
|
break;
|
|
}
|
|
disable_interrupt_key ();
|
|
#ifdef HAVE_GNOME
|
|
gtk_object_destroy (GTK_OBJECT (gd));
|
|
#else
|
|
if (verbose){
|
|
dlg_run_done (d);
|
|
destroy_dlg (d);
|
|
}
|
|
#endif
|
|
if (!s){
|
|
message (0, _(" Search "), _(" Search string not found "));
|
|
view->found_len = 0;
|
|
}
|
|
}
|
|
|
|
/* Search buffer (it's size is len) in the complete buffer */
|
|
/* returns the position where the block was found or -1 if not found */
|
|
static long
|
|
block_search (WView *view, char *buffer, int len)
|
|
{
|
|
int w = view->widget.cols - view->have_frame + 1;
|
|
|
|
char *d = buffer, b;
|
|
unsigned long e;
|
|
|
|
/* clear interrupt status */
|
|
got_interrupt ();
|
|
enable_interrupt_key ();
|
|
e = view->found_len ? view->search_start + 1 : view->search_start;
|
|
|
|
search_update_steps (view);
|
|
update_activate = 0;
|
|
|
|
while (e < view->last_byte){
|
|
if (e >= update_activate){
|
|
update_activate += update_steps;
|
|
if (verbose){
|
|
view_percent (view, e, w, TRUE);
|
|
mc_refresh ();
|
|
}
|
|
if (got_interrupt ())
|
|
break;
|
|
}
|
|
b = get_byte (view, e++);
|
|
|
|
if (*d == b){
|
|
d++;
|
|
if (d - buffer == len){
|
|
disable_interrupt_key ();
|
|
return e - len;
|
|
}
|
|
} else {
|
|
e -= d - buffer;
|
|
d = buffer;
|
|
}
|
|
}
|
|
disable_interrupt_key ();
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
static void
|
|
hex_search (WView *view, char *text)
|
|
{
|
|
char *buffer; /* Where we hold the information */
|
|
long pos; /* Where did we found the string */
|
|
int block_len = 0;
|
|
int parse_error = 0;
|
|
|
|
if (!*text) {
|
|
view->found_len = 0;
|
|
return;
|
|
}
|
|
|
|
/* buffer will never be longer that text */
|
|
buffer = g_new (char, strlen (text));
|
|
|
|
/* First convert the string to a stream of bytes */
|
|
while (*text) {
|
|
int val;
|
|
int ptr;
|
|
|
|
/* %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;
|
|
break;
|
|
}
|
|
|
|
buffer [block_len++] = (char) val;
|
|
text += ptr;
|
|
continue;
|
|
}
|
|
|
|
/* Try quoted string, strip quotes */
|
|
if (sscanf (text, "\"%[^\"]\"%n", buffer + block_len, &ptr) > 0) {
|
|
text += ptr;
|
|
block_len += ptr - 2;
|
|
continue;
|
|
}
|
|
|
|
parse_error = 1;
|
|
break;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* Then start the search */
|
|
pos = block_search (view, buffer, block_len);
|
|
|
|
g_free (buffer);
|
|
|
|
if (pos == -1){
|
|
message (0, _(" Search "), _(" Search string not found "));
|
|
view->found_len = 0;
|
|
return;
|
|
}
|
|
|
|
view->search_start = pos;
|
|
view->found_len = block_len;
|
|
/* Set the edit cursor to the search position, left nibble */
|
|
view->edit_cursor = view->search_start;
|
|
view->nib_shift = 0;
|
|
|
|
/* Adjust the file offset */
|
|
view->start_display = (pos & (~(view->bytes_per_line-1)));
|
|
if (view->start_display > get_bottom_first (view, 0, 0))
|
|
view->start_display = view->bottom_first;
|
|
}
|
|
|
|
static int regexp_view_search (WView *view, char *pattern, char *string, int match_type)
|
|
{
|
|
static regex_t r;
|
|
static char *old_pattern = NULL;
|
|
static int old_type;
|
|
regmatch_t pmatch[1];
|
|
int i, flags = REG_ICASE;
|
|
|
|
if (!old_pattern || strcmp (old_pattern, pattern) || old_type != match_type){
|
|
if (old_pattern){
|
|
regfree (&r);
|
|
g_free (old_pattern);
|
|
old_pattern = 0;
|
|
}
|
|
for (i = 0; pattern[i] != 0; i++){
|
|
if (isupper ((unsigned char) pattern[i])){
|
|
flags = 0;
|
|
break;
|
|
}
|
|
}
|
|
flags |= REG_EXTENDED;
|
|
if (regcomp (&r, pattern, flags)){
|
|
message (1, MSG_ERROR, _(" Invalid regular expression "));
|
|
return -1;
|
|
}
|
|
old_pattern = g_strdup (pattern);
|
|
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;
|
|
}
|
|
|
|
static void do_regexp_search (void *xview, char *regexp)
|
|
{
|
|
WView *view = (WView *) xview;
|
|
|
|
view->search_exp = regexp;
|
|
search (view, regexp, regexp_view_search);
|
|
/* Had a refresh here */
|
|
view->dirty++;
|
|
view_update (view, TRUE);
|
|
}
|
|
|
|
static void do_normal_search (void *xview, char *text)
|
|
{
|
|
WView *view = (WView *) xview;
|
|
|
|
view->search_exp = text;
|
|
if (view->hex_mode)
|
|
hex_search (view, text);
|
|
else
|
|
search (view, text, icase_search_p);
|
|
/* Had a refresh here */
|
|
view->dirty++;
|
|
view_update (view, TRUE);
|
|
}
|
|
|
|
/* }}} */
|
|
/* {{{ Mouse and keyboard handling */
|
|
|
|
/* Real view only */
|
|
static void view_help_cmd (void)
|
|
{
|
|
interactive_display (NULL, "[Internal File Viewer]");
|
|
/*
|
|
view_refresh (0);
|
|
*/
|
|
}
|
|
|
|
/* Both views */
|
|
void toggle_wrap_mode (WView *view)
|
|
{
|
|
if (view->hex_mode) {
|
|
if (view->growing_buffer != 0) {
|
|
return;
|
|
}
|
|
get_bottom_first (view, 1, 1);
|
|
if (view->hexedit_mode) {
|
|
view->view_side = 1 - view->view_side;
|
|
} else {
|
|
view->hexedit_mode = 1 - view->hexedit_mode;
|
|
}
|
|
view_labels (view);
|
|
view->dirty++;
|
|
view_update (view, TRUE);
|
|
return;
|
|
}
|
|
view->wrap_mode = 1 - view->wrap_mode;
|
|
get_bottom_first (view, 1, 1);
|
|
if (view->wrap_mode)
|
|
view->start_col = 0;
|
|
else {
|
|
if (have_fast_cpu){
|
|
if (view->bottom_first < view->start_display)
|
|
view->search_start = view->start_display = view->bottom_first;
|
|
view->found_len = 0;
|
|
}
|
|
}
|
|
view_labels (view);
|
|
view->dirty++;
|
|
view_update (view, TRUE);
|
|
}
|
|
|
|
/* Both views */
|
|
void
|
|
toggle_hex_mode (WView *view)
|
|
{
|
|
view->hex_mode = 1 - view->hex_mode;
|
|
|
|
if (view->hex_mode){
|
|
/* Shift the line start to 0x____0 on entry, restore it for Ascii */
|
|
view->start_save = view->start_display;
|
|
view->start_display -= view->start_display % view->bytes_per_line;
|
|
view->edit_cursor = view->start_display;
|
|
view->widget.options |= W_WANT_CURSOR;
|
|
view->widget.parent->raw = 1;
|
|
} else {
|
|
view->start_display = view->start_save;
|
|
view->widget.parent->raw = 0;
|
|
view->widget.options &= ~W_WANT_CURSOR;
|
|
}
|
|
altered_hex_mode = 1;
|
|
get_bottom_first (view, 1, 1);
|
|
view_labels (view);
|
|
view->dirty++;
|
|
view_update (view, TRUE);
|
|
}
|
|
|
|
/* Ascii view */
|
|
void
|
|
goto_line (WView *view)
|
|
{
|
|
char *line, prompt [BUF_SMALL];
|
|
int oldline = 1;
|
|
int saved_wrap_mode = view->wrap_mode;
|
|
unsigned long i;
|
|
|
|
view->wrap_mode = 0;
|
|
for (i = view->first; i < view->start_display; i++)
|
|
if (get_byte (view, i) == '\n')
|
|
oldline ++;
|
|
g_snprintf (prompt, sizeof (prompt), _(" The current line number is %d.\n"
|
|
" Enter the new line number:"), oldline);
|
|
line = input_dialog (_(" Goto line "), prompt, "");
|
|
if (line){
|
|
if (*line){
|
|
move_to_top (view);
|
|
view_move_forward (view, atol (line) - 1);
|
|
}
|
|
g_free (line);
|
|
}
|
|
view->dirty++;
|
|
view->wrap_mode = saved_wrap_mode;
|
|
view_update (view, TRUE);
|
|
}
|
|
|
|
/* Hex view */
|
|
static void
|
|
goto_addr (WView *view)
|
|
{
|
|
char *line, *error, prompt [BUF_SMALL];
|
|
unsigned long addr;
|
|
|
|
g_snprintf (prompt, sizeof (prompt), _(" The current address is 0x%lx.\n"
|
|
" Enter the new address:"), view->edit_cursor);
|
|
line = input_dialog (_(" Goto Address "), prompt, "");
|
|
if (line){
|
|
if (*line) {
|
|
addr = strtol (line, &error, 0);
|
|
if ((*error == '\0') && (addr <= view->last_byte)) {
|
|
move_to_top (view);
|
|
view_move_forward (view, addr/view->bytes_per_line);
|
|
view->edit_cursor = addr;
|
|
}
|
|
}
|
|
g_free (line);
|
|
}
|
|
view->dirty++;
|
|
view_update (view, TRUE);
|
|
}
|
|
|
|
/* Both views */
|
|
static void
|
|
regexp_search (WView *view, int direction)
|
|
{
|
|
char *regexp = "";
|
|
static char *old = 0;
|
|
|
|
/* This is really an F6 key handler */
|
|
if (view->hex_mode){
|
|
/* Save it without a confirmation prompt */
|
|
save_edit_changes(view);
|
|
return;
|
|
}
|
|
|
|
regexp = old ? old : regexp;
|
|
regexp = input_dialog (_(" Search "), _(" Enter regexp:"), regexp);
|
|
if ((!regexp)){
|
|
return;
|
|
}
|
|
if ((!*regexp)){
|
|
g_free (regexp);
|
|
return;
|
|
}
|
|
if (old)
|
|
g_free (old);
|
|
old = regexp;
|
|
#if 0
|
|
/* Mhm, do we really need to load all the file in the core? */
|
|
if (view->bytes_read < view->last_byte)
|
|
get_byte (view, view->last_byte-1);/* Get the whole file in to memory */
|
|
#endif
|
|
view->direction = direction;
|
|
do_regexp_search (view, regexp);
|
|
|
|
view->last_search = do_regexp_search;
|
|
}
|
|
|
|
void
|
|
regexp_search_cmd (WView *view)
|
|
{
|
|
regexp_search (view, 1);
|
|
}
|
|
|
|
/* Both views */
|
|
static void
|
|
normal_search (WView *view, int direction)
|
|
{
|
|
static char *old;
|
|
char *exp = "";
|
|
|
|
exp = old ? old : exp;
|
|
|
|
#ifdef HAVE_CHARSET
|
|
if ( *exp )
|
|
convert_to_display( exp );
|
|
#endif
|
|
|
|
exp = input_dialog (_(" Search "), _(" Enter search string:"), exp);
|
|
if ((!exp)){
|
|
return;
|
|
}
|
|
if ((!*exp)){
|
|
g_free (exp);
|
|
return;
|
|
}
|
|
if (old)
|
|
g_free (old);
|
|
old = exp;
|
|
|
|
#ifdef HAVE_CHARSET
|
|
convert_from_input( exp );
|
|
#endif
|
|
|
|
view->direction = direction;
|
|
do_normal_search (view, exp);
|
|
view->last_search = do_normal_search;
|
|
}
|
|
|
|
void
|
|
normal_search_cmd (WView *view)
|
|
{
|
|
normal_search (view, 1);
|
|
}
|
|
|
|
static void
|
|
change_viewer (WView *view)
|
|
{
|
|
char *s;
|
|
char *t;
|
|
|
|
|
|
if (*view->filename) {
|
|
altered_magic_flag = 1;
|
|
view->viewer_magic_flag = !view->viewer_magic_flag;
|
|
s = g_strdup (view->filename);
|
|
if (view->command)
|
|
t = g_strdup (view->command);
|
|
else
|
|
t = 0;
|
|
|
|
view_done (view);
|
|
view_init (view, t, s, 0);
|
|
g_free (s);
|
|
if (t)
|
|
g_free (t);
|
|
view_labels (view);
|
|
view->dirty++;
|
|
view_update (view, TRUE);
|
|
}
|
|
}
|
|
|
|
void
|
|
change_nroff (WView *view)
|
|
{
|
|
view->viewer_nroff_flag = !view->viewer_nroff_flag;
|
|
altered_nroff_flag = 1;
|
|
view_labels (view);
|
|
view->dirty++;
|
|
view_update (view, TRUE);
|
|
}
|
|
|
|
/* Real view only */
|
|
static void
|
|
view_quit_cmd (WView *view)
|
|
{
|
|
if (view_ok_to_quit (view))
|
|
dlg_stop (view->widget.parent);
|
|
}
|
|
|
|
/* Both views */
|
|
void
|
|
view_labels (WView *view)
|
|
{
|
|
Dlg_head *h = view->widget.parent;
|
|
|
|
define_label (h, (Widget *) view, 1, _("Help"), view_help_cmd);
|
|
|
|
my_define (h, 10, _("Quit"), view_quit_cmd, view);
|
|
my_define (h, 4, view->hex_mode ? _("Ascii"): _("Hex"), toggle_hex_mode, view);
|
|
my_define (h, 5, view->hex_mode ? _("Goto") : _("Line"),
|
|
view->hex_mode ? goto_addr : goto_line,
|
|
view);
|
|
my_define (h, 6, view->hex_mode ? _("Save") : _("RxSrch"), regexp_search_cmd, view);
|
|
|
|
my_define (h, 2, view->hex_mode ? view->hexedit_mode ?
|
|
view->view_side == view_side_left ? _("EdText") : _("EdHex") :
|
|
view->growing_buffer ? "" : _("Edit") :
|
|
view->wrap_mode ? _("UnWrap") : _("Wrap"),
|
|
toggle_wrap_mode, view);
|
|
|
|
my_define (h, 7, view->hex_mode ? _("HxSrch") : _("Search"),
|
|
normal_search_cmd, view);
|
|
|
|
my_define (h, 8, view->viewer_magic_flag ? _("Raw") : _("Parse"),
|
|
change_viewer, view);
|
|
|
|
if (!view->have_frame){
|
|
my_define (h, 9, view->viewer_nroff_flag ? _("Unform") : _("Format"),
|
|
change_nroff, view);
|
|
my_define (h, 3, _("Quit"), view_quit_cmd, view);
|
|
}
|
|
|
|
redraw_labels (h, (Widget *) view);
|
|
}
|
|
|
|
/* Both views */
|
|
static int
|
|
check_left_right_keys (WView *view, int c)
|
|
{
|
|
if (c == KEY_LEFT)
|
|
move_left (view);
|
|
else if (c == KEY_RIGHT)
|
|
move_right (view);
|
|
else return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
set_monitor (WView *view, int set_on)
|
|
{
|
|
int old = view->monitor;
|
|
|
|
view->monitor = set_on;
|
|
|
|
if (view->monitor){
|
|
move_to_bottom (view);
|
|
view->bottom_first = -1;
|
|
set_idle_proc (view->widget.parent, 1);
|
|
} else {
|
|
if (old)
|
|
set_idle_proc (view->widget.parent, 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
continue_search (WView *view)
|
|
{
|
|
if (view->last_search){
|
|
(*view->last_search)(view, view->search_exp);
|
|
} else {
|
|
/* if not... then ask for an expression */
|
|
normal_search (view, 1);
|
|
}
|
|
}
|
|
|
|
/* Both views */
|
|
static int
|
|
view_handle_key (WView *view, int c)
|
|
{
|
|
int prev_monitor = view->monitor;
|
|
|
|
set_monitor (view, off);
|
|
|
|
#ifdef HAVE_CHARSET
|
|
if (c >= 128 && c <= 255) {
|
|
int ch = conv_input[ c & 0xFF ];
|
|
c = (c & 0xFF00) | ch;
|
|
}
|
|
#endif
|
|
|
|
if (view->hex_mode) {
|
|
switch (c) {
|
|
case 0x09: /* Tab key */
|
|
view->view_side = 1 - view->view_side;
|
|
view->dirty++;
|
|
return 1;
|
|
|
|
case XCTRL('a'): /* Beginning of line */
|
|
view->edit_cursor -= view->edit_cursor % view->bytes_per_line;
|
|
view->dirty++;
|
|
return 1;
|
|
|
|
case XCTRL('b'): /* Character back */
|
|
move_left(view);
|
|
return 1;
|
|
|
|
case XCTRL('e'): /* End of line */
|
|
view->edit_cursor -= view->edit_cursor % view->bytes_per_line;
|
|
view->edit_cursor += view->bytes_per_line - 1;
|
|
view->dirty++;
|
|
return 1;
|
|
|
|
case XCTRL('f'): /* Character forward */
|
|
move_right(view);
|
|
return 1;
|
|
}
|
|
|
|
/* Trap 0-9,A-F,a-f for left side data entry (hex editing) */
|
|
if (view->view_side == view_side_left){
|
|
if ((c >= '0' && c <= '9') ||
|
|
(c >= 'A' && c <= 'F') ||
|
|
(c >= 'a' && c <= 'f')){
|
|
|
|
put_editkey (view, c);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Trap all printable characters for right side data entry */
|
|
/* Also enter the value of the Enter key */
|
|
if (view->view_side == view_side_right){
|
|
if (c < 256 && (is_printable (c) || (c == '\n'))){
|
|
put_editkey(view, c);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (check_left_right_keys (view, c))
|
|
return 1;
|
|
|
|
if (check_movement_keys (c, 1, vheight, view, (movefn) view_move_backward, (movefn) view_move_forward,
|
|
(movefn) move_to_top, (movefn) move_to_bottom)){
|
|
return 1;
|
|
}
|
|
switch (c){
|
|
|
|
case '?':
|
|
regexp_search (view, -1);
|
|
return 1;
|
|
|
|
case '/':
|
|
regexp_search (view, 1);
|
|
return 1;
|
|
|
|
/* Continue search */
|
|
case XCTRL('s'):
|
|
case 'n':
|
|
case KEY_F(17):
|
|
continue_search (view);
|
|
return 1;
|
|
|
|
case XCTRL('r'):
|
|
if (view->last_search){
|
|
(*view->last_search)(view, view->search_exp);
|
|
} else {
|
|
normal_search (view, -1);
|
|
}
|
|
return 1;
|
|
|
|
/* toggle ruler */
|
|
case ALT('r'):
|
|
switch (ruler){
|
|
case 0:
|
|
ruler = 1; break;
|
|
case 1:
|
|
ruler = 2; break;
|
|
default:
|
|
ruler = 0; break;
|
|
}
|
|
view->dirty++;
|
|
return 1;
|
|
|
|
case 'h':
|
|
move_left (view);
|
|
return 1;
|
|
|
|
case 'j':
|
|
case '\n':
|
|
case 'e':
|
|
view_move_forward (view, 1);
|
|
return 1;
|
|
|
|
case 'd':
|
|
view_move_forward (view, vheight / 2);
|
|
return 1;
|
|
|
|
case 'u':
|
|
view_move_backward (view, vheight / 2);
|
|
return 1;
|
|
|
|
case 'k':
|
|
case 'y':
|
|
view_move_backward (view, 1);
|
|
return 1;
|
|
|
|
case 'l':
|
|
move_right (view);
|
|
return 1;
|
|
|
|
case ' ':
|
|
case 'f':
|
|
view_move_forward (view, vheight - 1);
|
|
return 1;
|
|
|
|
#ifndef HAVE_X
|
|
case XCTRL('o'):
|
|
view_other_cmd ();
|
|
return 1;
|
|
|
|
/* Unlike Ctrl-O, run a new shell if the subshell is not running. */
|
|
case '!':
|
|
exec_shell ();
|
|
return 1;
|
|
#endif /* !HAVE_X */
|
|
|
|
case 'F':
|
|
set_monitor (view, on);
|
|
return 1;
|
|
|
|
case 'b':
|
|
view_move_backward (view, vheight - 1);
|
|
return 1;
|
|
|
|
case KEY_IC:
|
|
view_move_backward (view, 2);
|
|
return 1;
|
|
|
|
case KEY_DC:
|
|
view_move_forward (view, 2);
|
|
return 1;
|
|
|
|
case 'm':
|
|
view->marks [view->marker] = view->start_display;
|
|
return 1;
|
|
|
|
case 'r':
|
|
view->start_display = view->marks [view->marker];
|
|
view->dirty++;
|
|
return 1;
|
|
|
|
/* Use to indicate parent that we want to see the next/previous file */
|
|
/* Only works on full screen mode */
|
|
case XCTRL('f'):
|
|
case XCTRL('b'):
|
|
if (!view->have_frame)
|
|
view->move_dir = c == XCTRL('f') ? 1 : -1;
|
|
/* fall */
|
|
|
|
case 'q':
|
|
case XCTRL('g'):
|
|
case ESC_CHAR:
|
|
if (view_ok_to_quit (view))
|
|
view->view_quit = 1;
|
|
return 1;
|
|
|
|
#ifdef HAVE_CHARSET
|
|
case XCTRL('t'):
|
|
do_select_codepage();
|
|
view->dirty++;
|
|
view_update( view, TRUE );
|
|
return 1;
|
|
#endif
|
|
|
|
}
|
|
if (c >= '0' && c <= '9')
|
|
view->marker = c - '0';
|
|
|
|
/* Restore the monitor status */
|
|
set_monitor (view, prev_monitor);
|
|
|
|
/* Key not used */
|
|
return 0;
|
|
}
|
|
|
|
/* Both views */
|
|
int
|
|
view_event (WView *view, Gpm_Event *event, int *result)
|
|
{
|
|
*result = MOU_NORMAL;
|
|
if (event->type & (GPM_DOWN|GPM_DRAG)){
|
|
if (!view->wrap_mode){
|
|
if (event->x < view->widget.cols / 4){
|
|
move_left (view);
|
|
*result = MOU_REPEAT;
|
|
return 1;
|
|
}
|
|
if (event->x > 3 * vwidth / 4){
|
|
move_right (view);
|
|
*result = MOU_REPEAT;
|
|
return 1;
|
|
}
|
|
}
|
|
if (event->y < view->widget.lines / 3){
|
|
if (mouse_move_pages_viewer)
|
|
view_move_backward (view, view->widget.lines / 2 - 1);
|
|
else
|
|
view_move_backward (view, 1);
|
|
*result = MOU_REPEAT;
|
|
return 1;
|
|
}
|
|
else if (event->y > 2 * vheight /3){
|
|
if (mouse_move_pages_viewer)
|
|
view_move_forward (view, vheight / 2 - 1);
|
|
else
|
|
view_move_forward (view, 1);
|
|
*result = MOU_REPEAT;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Real view only */
|
|
static int
|
|
real_view_event (Gpm_Event *event, void *x)
|
|
{
|
|
int result;
|
|
|
|
if (view_event ((WView *) x, event, &result))
|
|
view_update ((WView *) x, TRUE);
|
|
return result;
|
|
}
|
|
|
|
/* }}} */
|
|
/* {{{ Window creation, destruction and a driver stub for real view */
|
|
|
|
#ifndef PORT_WANTS_VIEW
|
|
static int
|
|
view_mode_callback (struct Dlg_head *h, int id, int msg)
|
|
{
|
|
return default_dlg_callback (h, id, msg);
|
|
}
|
|
|
|
void
|
|
view_adjust_size (Dlg_head *h)
|
|
{
|
|
WView *view;
|
|
WButtonBar *bar;
|
|
|
|
/* Look up the viewer and the buttonbar, we assume only two widgets here */
|
|
view = (WView *) find_widget_type (h, (callback_fn) view_callback);
|
|
bar = (WButtonBar *) view->widget.parent->current->next->widget;
|
|
widget_set_size (&view->widget, 0, 0, LINES-1, COLS);
|
|
widget_set_size (&bar->widget, LINES-1, 0, 1, COLS);
|
|
|
|
view_update_bytes_per_line(view);
|
|
}
|
|
|
|
/* Only the text mode edition uses this */
|
|
Dlg_head *view_dlg;
|
|
|
|
/* Real view only */
|
|
int
|
|
view (char *_command, const char *_file, int *move_dir_p, int start_line)
|
|
{
|
|
int midnight_colors [4];
|
|
int error;
|
|
WView *wview;
|
|
WButtonBar *bar;
|
|
Dlg_head *our_dlg;
|
|
|
|
/* Create dialog and widgets, put them on the dialog */
|
|
our_dlg = create_dlg (0, 0, LINES, COLS, midnight_colors,
|
|
view_mode_callback, "[Internal File Viewer]",
|
|
"view",
|
|
DLG_NONE);
|
|
|
|
#ifndef HAVE_X
|
|
view_dlg = our_dlg;
|
|
#endif
|
|
wview = view_new (0, 0, COLS, LINES-1, 0);
|
|
|
|
bar = buttonbar_new (1);
|
|
|
|
add_widget (our_dlg, wview);
|
|
add_widget (our_dlg, bar);
|
|
|
|
error = view_init (wview, _command, _file, start_line);
|
|
if (move_dir_p)
|
|
*move_dir_p = 0;
|
|
|
|
/* Please note that if you add another widget,
|
|
* you have to modify view_adjust_size to
|
|
* be aware of it
|
|
*/
|
|
if (!error){
|
|
run_dlg (our_dlg);
|
|
if (move_dir_p)
|
|
*move_dir_p = wview->move_dir;
|
|
}
|
|
destroy_dlg (our_dlg);
|
|
|
|
return !error;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
view_hook (void *v)
|
|
{
|
|
WView *view = (WView *) v;
|
|
WPanel *panel;
|
|
struct stat s;
|
|
|
|
/* If the user is busy typing, wait until he finishes to update the
|
|
screen */
|
|
if (!is_idle ()){
|
|
if (!hook_present (idle_hook, view_hook))
|
|
add_hook (&idle_hook, view_hook, v);
|
|
return;
|
|
}
|
|
|
|
delete_hook (&idle_hook, view_hook);
|
|
|
|
if (get_current_type () == view_listing)
|
|
panel = cpanel;
|
|
else if (get_other_type () == view_listing)
|
|
panel = other_panel;
|
|
else
|
|
return;
|
|
|
|
if (S_ISLNK (panel->dir.list [panel->selected].buf.st_mode)){
|
|
if (mc_stat (panel->dir.list [panel->selected].fname, &s) != 0)
|
|
return;
|
|
if (!S_ISREG (s.st_mode))
|
|
return;
|
|
} else {
|
|
if (!S_ISREG (panel->dir.list [panel->selected].buf.st_mode))
|
|
return;
|
|
}
|
|
|
|
view_init (view, 0, panel->dir.list [panel->selected].fname, 0);
|
|
display (view);
|
|
view_status (view, TRUE);
|
|
}
|
|
|
|
static int
|
|
view_callback (Dlg_head *h, WView *view, int msg, int par)
|
|
{
|
|
int i;
|
|
|
|
switch (msg){
|
|
case WIDGET_INIT:
|
|
x_create_viewer (view);
|
|
if (view->have_frame)
|
|
add_hook (&select_file_hook, view_hook, view);
|
|
else
|
|
view_labels (view);
|
|
break;
|
|
|
|
case WIDGET_DRAW:
|
|
display (view);
|
|
view_status (view, TRUE);
|
|
break;
|
|
|
|
case WIDGET_CURSOR:
|
|
if (view->hex_mode)
|
|
view_place_cursor (view);
|
|
break;
|
|
|
|
case WIDGET_KEY:
|
|
i = view_handle_key ((WView *)view, par);
|
|
if (view->view_quit)
|
|
dlg_stop (h);
|
|
else {
|
|
view_update (view, TRUE);
|
|
}
|
|
return i;
|
|
|
|
case WIDGET_IDLE:
|
|
/* This event is generated when the user is using the 'F' flag */
|
|
view->bottom_first = -1;
|
|
move_to_bottom (view);
|
|
display (view);
|
|
view_status (view, TRUE);
|
|
sleep (1);
|
|
return 1;
|
|
|
|
case WIDGET_FOCUS:
|
|
x_focus_view (view);
|
|
view_labels (view);
|
|
return 1;
|
|
|
|
}
|
|
return default_proc (h, msg, par);
|
|
}
|
|
|
|
WView *
|
|
view_new (int y, int x, int cols, int lines, int is_panel)
|
|
{
|
|
WView *view = g_new0 (WView, 1);
|
|
|
|
init_widget (&view->widget, y, x, lines, cols,
|
|
(callback_fn) view_callback,
|
|
(destroy_fn) view_destroy,
|
|
(mouse_h) real_view_event, NULL);
|
|
|
|
view->hex_mode = default_hex_mode;
|
|
view->hexedit_mode = default_hexedit_mode;
|
|
view->viewer_magic_flag = default_magic_flag;
|
|
view->viewer_nroff_flag = default_nroff_flag;
|
|
view->have_frame = is_panel;
|
|
view->last_byte = -1;
|
|
view->wrap_mode = global_wrap_mode;
|
|
|
|
x_init_view (view);
|
|
|
|
widget_want_cursor (view->widget, 0);
|
|
|
|
return view;
|
|
}
|
|
|
|
/* }}} */
|
|
/* {{{ Emacs local variables */
|
|
/*
|
|
Cause emacs to enter folding mode for this file:
|
|
Local variables:
|
|
end:
|
|
*/
|
|
/* }}} */
|