mc/src/view.c
Pavel Roskin 2da73069b9 * view.c: Warning fixes. Include "cmd.h". Rename help_cmd()
to view_help_cmd().
(view_handle_key) [HAVE_X]: Disable '!' and Ctrl-O.
2001-08-07 16:53:55 +00:00

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:
*/
/* }}} */