1 /* View file module for the Midnight Commander
2 Copyright (C) 1994, 1995, 1996 The Free Software Foundation
3 Written by: 1994, 1995, 1998 Miguel de Icaza
4 1994, 1995 Janne Kukonlehto
9 2004 Roland Illig <roland.illig@gmx.de>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
27 #include <sys/types.h>
34 # include <sys/mman.h>
36 #include <ctype.h> /* For toupper() */
42 #include "cmd.h" /* For view_other_cmd */
43 #include "dialog.h" /* Needed by widget.h */
44 #include "widget.h" /* Needed for buttonbar_new */
48 #include "key.h" /* For mi_getch() */
51 #include "wtools.h" /* For query_set_sel() */
53 #include "panel.h" /* Needed for current_panel and other_panel */
56 #include "main.h" /* For the externs */
61 #include "selcodepage.h"
67 /* Block size for reading files in parts */
68 #define READ_BLOCK 8192
69 #define VIEW_PAGE_SIZE 8192
71 #define vwidth (view->widget.cols - (view->have_frame ? 2 : 0))
72 #define vheight (view->widget.lines - (view->have_frame ? 2 : 0))
74 /* Offset in bytes into a file */
75 typedef unsigned long offset_type
;
76 #define INVALID_OFFSET ((offset_type) -1)
77 #define OFFSETTYPE_MAX (~((offset_type) 0))
79 /* A width or height on the screen */
80 typedef unsigned int screen_dimen
;
82 /* A node for building a change list on change_list */
83 struct hexedit_change_node
{
84 struct hexedit_change_node
*next
;
92 char *filename
; /* Name of the file */
93 char *command
; /* Command used to pipe data in */
97 unsigned char *data
; /* Memory area for the file to be viewed */
98 size_t datasize
; /* Number of bytes in the data */
99 /* view_update_last_byte() must be called after assignment to datasize */
101 /* File information */
102 int file
; /* File descriptor (for mmap and munmap) */
103 FILE *stdfile
; /* Stdio struct for reading file in parts */
104 int reading_pipe
; /* Flag: Reading from pipe(use popen/pclose) */
105 int mmapping
; /* Did we use mmap on the file? */
106 size_t mmappedsize
; /* Number of bytes that are mmapped; used only for munmap() */
108 /* Display information */
109 offset_type last
; /* Last byte shown */
110 offset_type last_byte
; /* Last byte of file */
111 offset_type first
; /* First byte in file */
112 offset_type bottom_first
; /* First byte shown when very last page is displayed */
113 /* For the case of WINCH we should reset it to -1 */
114 offset_type start_display
; /* First char displayed */
115 int start_col
; /* First displayed column, negative */
116 offset_type edit_cursor
; /* HexEdit cursor position in file */
117 int hexedit_mode
:1; /* Hexadecimal editing mode flag */
118 int nib_shift
:1; /* Set if editing the least significant nibble */
119 int hexedit_text
:1; /* Set if hexedit is in the text mode */
120 screen_dimen start_save
; /* Line start shift between text and hex */
121 screen_dimen cursor_col
; /* Cursor column */
122 screen_dimen cursor_row
; /* Cursor row */
123 struct hexedit_change_node
*change_list
; /* Linked list of changes */
125 int dirty
; /* Number of skipped updates */
126 int wrap_mode
:1; /* wrap_mode */
129 int hex_mode
:1; /* Hexadecimal mode flag */
130 int bytes_per_line
; /* Number of bytes per line in hex mode */
131 int viewer_magic_flag
:1; /* Selected viewer */
132 int viewer_nroff_flag
:1; /* Do we do nroff style highlighting? */
134 /* Growing buffers information */
135 int growing_buffer
; /* Use the growing buffers? */
136 char **block_ptr
; /* Pointer to the block pointers */
137 int blocks
; /* The number of blocks in *block_ptr */
138 size_t growbuf_lastindex
; /* Number of bytes in the last page of the
140 /* view_update_last_byte() must be called after assignment to
141 growing_buffer, blocks, growbuf_lastindex */
143 /* Search variables */
144 offset_type search_start
; /* First character to start searching from */
145 offset_type found_len
; /* Length of found string or 0 if none was found */
146 char *search_exp
; /* The search expression */
147 int direction
; /* 1= forward; -1 backward */
148 void (*last_search
)(void *, char *);
149 /* Pointer to the last search command */
150 int view_quit
:1; /* Quit flag */
152 int monitor
; /* Monitor file growth (like tail -f) */
154 int marker
; /* mark to use */
155 offset_type marks
[10]; /* 10 marks: 0..9 */
157 int move_dir
; /* return value from widget:
159 * -1 view previous file
163 offset_type update_steps
; /* The number of bytes between percent
165 offset_type update_activate
;/* Last point where we updated the status */
168 static void view_update_last_byte (WView
*view
)
170 if (view
->growing_buffer
) {
171 if (view
->blocks
== 0)
174 view
->last_byte
= ((offset_type
) view
->blocks
- 1)
175 * VIEW_PAGE_SIZE
+ view
->growbuf_lastindex
;
177 view
->last_byte
= view
->datasize
;
178 view
->bottom_first
= INVALID_OFFSET
;
181 static void view_move_cursor_to_eol(WView
*view
)
183 offset_type last_line
= (view
->last_byte
- 1) / view
->bytes_per_line
;
184 offset_type current_line
= view
->edit_cursor
/ view
->bytes_per_line
;
186 if (current_line
== last_line
) {
187 view
->edit_cursor
= view
->last_byte
- 1;
189 view
->edit_cursor
= (1 + current_line
) * view
->bytes_per_line
- 1;
194 /* Maxlimit for skipping updates */
195 int max_dirt_limit
= 10;
197 /* If set, show a ruler */
198 static int ruler
= 0;
200 /* Scrolling is done in pages or line increments */
201 int mouse_move_pages_viewer
= 1;
203 /* Used to compute the bottom first variable */
204 int have_fast_cpu
= 0;
206 /* wrap mode default */
207 int global_wrap_mode
= 1;
209 int default_hex_mode
= 0;
210 static int default_hexedit_mode
= 0;
211 int default_magic_flag
= 1;
212 int default_nroff_flag
= 1;
213 int altered_hex_mode
= 0;
214 int altered_magic_flag
= 0;
215 int altered_nroff_flag
= 0;
217 static const char hex_char
[] = "0123456789ABCDEF";
219 /* Our widget callback */
220 static cb_ret_t
view_callback (WView
*view
, widget_msg_t msg
, int parm
);
222 static int regexp_view_search (WView
* view
, char *pattern
, char *string
,
224 static void view_move_forward (WView
* view
, int i
);
225 static void view_labels (WView
* view
);
226 static void set_monitor (WView
* view
, int set_on
);
227 static void view_update (WView
* view
, gboolean update_gui
);
231 close_view_file (WView
*view
)
233 if (view
->file
!= -1) {
234 mc_close (view
->file
);
240 free_file (WView
*view
)
245 if (view
->mmapping
) {
246 mc_munmap (view
->data
, view
->mmappedsize
);
247 close_view_file (view
);
249 #endif /* HAVE_MMAP */
251 if (view
->reading_pipe
) {
253 pclose (view
->stdfile
);
254 view
->stdfile
= NULL
;
256 /* Close error pipe and show warnings if any */
257 close_error_pipe (0, NULL
);
259 close_view_file (view
);
261 /* Block_ptr may be zero if the file was a file with 0 bytes */
262 if (view
->growing_buffer
&& view
->block_ptr
) {
263 for (i
= 0; i
< view
->blocks
; i
++) {
264 g_free (view
->block_ptr
[i
]);
266 g_free (view
->block_ptr
);
270 /* Valid parameters for second parameter to set_monitor */
275 view_done (WView
*view
)
277 set_monitor (view
, off
);
279 /* alex: release core, used to replace mmap */
280 if (!view
->mmapping
&& !view
->growing_buffer
&& view
->data
!= NULL
) {
285 if (view
->view_active
) {
287 g_free (view
->filename
);
288 g_free (view
->command
);
290 view
->view_active
= 0;
291 default_hex_mode
= view
->hex_mode
;
292 default_nroff_flag
= view
->viewer_nroff_flag
;
293 default_magic_flag
= view
->viewer_magic_flag
;
294 global_wrap_mode
= view
->wrap_mode
;
297 static void view_hook (void *);
299 /* Copies the output from the pipe to the growing buffer, until either
300 * the end-of-pipe is reached or the interval [0..ofs) of the growing
301 * buffer is completely filled. */
303 view_growbuf_read_until (WView
*view
, offset_type ofs
)
309 /* g_assert (view->growing_buffer, NULL); */
310 while (view
->last_byte
< ofs
) {
311 if (view
->blocks
== 0 || view
->growbuf_lastindex
== VIEW_PAGE_SIZE
) {
312 char *newblock
= g_try_malloc (VIEW_PAGE_SIZE
);
313 char **newblocks
= g_try_malloc (sizeof (char *) * (view
->blocks
+ 1));
314 if (!newblock
|| !newblocks
) {
318 memcpy (newblocks
, view
->block_ptr
, sizeof (char *) * view
->blocks
);
319 g_free (view
->block_ptr
);
320 view
->block_ptr
= newblocks
;
321 view
->block_ptr
[view
->blocks
++] = newblock
;
322 view
->growbuf_lastindex
= 0;
323 view_update_last_byte (view
);
325 p
= view
->block_ptr
[view
->blocks
- 1] + view
->growbuf_lastindex
;
326 bytesfree
= VIEW_PAGE_SIZE
- view
->growbuf_lastindex
;
327 if (view
->stdfile
!= NULL
)
328 nread
= fread (p
, 1, bytesfree
, view
->stdfile
);
330 nread
= mc_read (view
->file
, p
, bytesfree
);
332 if (nread
== -1 || nread
== 0)
334 view
->growbuf_lastindex
+= nread
;
335 view_update_last_byte (view
);
340 get_byte (WView
*view
, unsigned int byte_index
)
342 if (view
->growing_buffer
) {
343 size_t pageno
= byte_index
/ VIEW_PAGE_SIZE
;
344 size_t pageindex
= byte_index
% VIEW_PAGE_SIZE
;
346 view_growbuf_read_until (view
, byte_index
+ 1);
347 if (view
->blocks
== 0)
349 if (pageno
< view
->blocks
- 1)
350 return view
->block_ptr
[pageno
][pageindex
];
351 if (pageno
== view
->blocks
- 1 && pageindex
< view
->growbuf_lastindex
)
352 return view
->block_ptr
[pageno
][pageindex
];
356 /* g_assert (view->data != NULL); */
357 if (byte_index
< view
->datasize
)
358 return view
->data
[byte_index
];
363 enqueue_change (struct hexedit_change_node
**head
,
364 struct hexedit_change_node
*node
)
366 struct hexedit_change_node
*curr
= *head
;
369 if (node
->offset
< curr
->offset
) {
374 head
= (struct hexedit_change_node
**) curr
;
381 static void move_right (WView
*);
384 view_handle_editkey (WView
*view
, int key
)
386 struct hexedit_change_node
*node
;
387 unsigned char byte_val
;
389 /* Has there been a change at this position ? */
390 node
= view
->change_list
;
391 while (node
&& (node
->offset
!= view
->edit_cursor
))
394 if (!view
->hexedit_text
) {
396 unsigned int hexvalue
= 0;
398 if (key
>= '0' && key
<= '9')
399 hexvalue
= 0 + (key
- '0');
400 else if (key
>= 'A' && key
<= 'F')
401 hexvalue
= 10 + (key
- 'A');
402 else if (key
>= 'a' && key
<= 'f')
403 hexvalue
= 10 + (key
- 'a');
405 return MSG_NOT_HANDLED
;
408 byte_val
= node
->value
;
410 byte_val
= get_byte (view
, view
->edit_cursor
);
412 if (!view
->nib_shift
) {
413 byte_val
= (byte_val
& 0x0f) | (hexvalue
<< 4);
415 byte_val
= (byte_val
& 0xf0) | (hexvalue
);
419 if (key
< 256 && (is_printable (key
) || (key
== '\n')))
422 return MSG_NOT_HANDLED
;
425 node
= (struct hexedit_change_node
*)
426 g_new (struct hexedit_change_node
, 1);
430 /* alex@bcs.zaporizhzhe.ua: here we are using file copy
431 * completely loaded into memory, so we can replace bytes in
432 * view->data array to allow changes to be reflected when
433 * user switches back to text mode */
434 view
->data
[view
->edit_cursor
] = byte_val
;
435 #endif /* !HAVE_MMAP */
436 node
->offset
= view
->edit_cursor
;
437 node
->value
= byte_val
;
438 enqueue_change (&view
->change_list
, node
);
441 node
->value
= byte_val
;
444 view_update (view
, TRUE
);
450 free_change_list (WView
*view
)
452 struct hexedit_change_node
*n
= view
->change_list
;
455 view
->change_list
= n
->next
;
457 n
= view
->change_list
;
463 save_edit_changes (WView
*view
)
465 struct hexedit_change_node
*node
= view
->change_list
;
469 fp
= open (view
->filename
, O_WRONLY
);
472 if (lseek (fp
, node
->offset
, SEEK_SET
) == -1 ||
473 write (fp
, &node
->value
, 1) != 1) {
485 fp
= query_dialog (_(" Save file "),
486 _(" Cannot save file. "),
487 2, 2, _("&Retry"), _("&Cancel")) - 1;
491 free_change_list (view
);
495 view_ok_to_quit (WView
*view
)
499 if (!view
->change_list
)
502 r
= query_dialog (_("Quit"),
503 _(" File was modified, Save with exit? "), 2, 3,
504 _("&Cancel quit"), _("&Yes"), _("&No"));
508 save_edit_changes (view
);
511 free_change_list (view
);
519 set_view_init_error (WView
*view
, const char *msg
)
521 view
->growing_buffer
= 0;
522 view
->reading_pipe
= 0;
526 view
->datasize
= strlen (msg
);
527 view_update_last_byte (view
);
528 return g_strdup (msg
);
533 /* return values: NULL for success, else points to error message */
535 init_growing_view (WView
*view
, const char *name
, const char *filename
)
537 const char *err_msg
= NULL
;
539 view
->growing_buffer
= 1;
540 view
->block_ptr
= NULL
;
542 view
->growbuf_lastindex
= 0; /* unused */
543 view_update_last_byte (view
);
546 view
->reading_pipe
= 1;
549 if ((view
->stdfile
= popen (name
, "r")) == NULL
) {
550 /* Avoid two messages. Message from stderr has priority. */
551 if (!close_error_pipe (view
->have_frame
? -1 : 1, view
->data
))
552 err_msg
= _(" Cannot spawn child program ");
553 return set_view_init_error (view
, err_msg
);
556 /* First, check if filter produced any output */
557 view_growbuf_read_until (view
, 1);
558 if (view
->last_byte
== 0) {
559 pclose (view
->stdfile
);
560 view
->stdfile
= NULL
;
561 /* Avoid two messages. Message from stderr has priority. */
562 if (!close_error_pipe (view
->have_frame
? -1 : 1, view
->data
))
563 err_msg
= _("Empty output from child filter");
564 return set_view_init_error (view
, err_msg
);
567 view
->stdfile
= NULL
;
568 if ((view
->file
= mc_open (filename
, O_RDONLY
)) == -1)
569 return set_view_init_error (view
, _(" Cannot open file "));
574 /* Load filename into core */
577 if (have_frame), we return success, but data points to a
578 error message instead of the file buffer (quick_view feature).
581 load_view_file (WView
*view
, int fd
, const struct stat
*st
)
585 if (st
->st_size
== 0) {
586 /* Must be one of those nice files that grow (/proc) */
587 close_view_file (view
);
588 return init_growing_view (view
, 0, view
->filename
);
591 if ((size_t) st
->st_size
== st
->st_size
)
592 view
->data
= mc_mmap (0, st
->st_size
, PROT_READ
,
593 MAP_FILE
| MAP_SHARED
, view
->file
, 0);
595 view
->data
= (caddr_t
) -1;
596 if ((caddr_t
) view
->data
!= (caddr_t
) - 1) {
599 view
->mmappedsize
= st
->st_size
;
600 view
->datasize
= st
->st_size
;
602 view_update_last_byte (view
);
605 #endif /* HAVE_MMAP */
607 /* For the OSes that don't provide mmap call, try to load all the
608 * file into memory (alex@bcs.zaporizhzhe.ua). Also, mmap can fail
609 * for any reason, so we use this as fallback (pavel@ucw.cz) */
611 /* Make sure view->s.st_size is not truncated when passed to g_malloc */
612 if ((gulong
) st
->st_size
== st
->st_size
)
613 view
->data
= (unsigned char *) g_try_malloc ((gulong
) st
->st_size
);
617 if (view
->data
== NULL
|| mc_lseek (view
->file
, 0, SEEK_SET
) != 0
618 || mc_read (view
->file
, view
->data
,
619 st
->st_size
) != st
->st_size
) {
621 close_view_file (view
);
622 return init_growing_view (view
, 0, view
->filename
);
625 view
->datasize
= st
->st_size
;
626 view_update_last_byte (view
);
630 /* Return zero on success, -1 on failure */
632 do_view_init (WView
*view
, const char *_command
, const char *_file
,
638 char tmp
[BUF_MEDIUM
];
641 if (view
->view_active
)
644 /* Set up the state */
647 view
->growing_buffer
= 0;
648 view
->reading_pipe
= 0;
651 view
->block_ptr
= NULL
;
653 view
->filename
= g_strdup (_file
);
655 view
->last
= view
->first
+ ((LINES
- 2) * view
->bytes_per_line
);
656 view_update_last_byte (view
);
658 /* Clear the markers */
660 for (i
= 0; i
< 10; i
++)
663 if (!view
->have_frame
) {
667 if (_command
&& (view
->viewer_magic_flag
|| _file
[0] == '\0')) {
668 error
= init_growing_view (view
, _command
, view
->filename
);
669 } else if (_file
[0]) {
673 if ((fd
= mc_open (_file
, O_RDONLY
| O_NONBLOCK
)) == -1) {
674 g_snprintf (tmp
, sizeof (tmp
), _(" Cannot open \"%s\"\n %s "),
675 _file
, unix_error_string (errno
));
676 error
= set_view_init_error (view
, tmp
);
680 /* Make sure we are working with a regular file */
681 if (mc_fstat (fd
, &st
) == -1) {
683 g_snprintf (tmp
, sizeof (tmp
), _(" Cannot stat \"%s\"\n %s "),
684 _file
, unix_error_string (errno
));
685 error
= set_view_init_error (view
, tmp
);
689 if (!S_ISREG (st
.st_mode
)) {
691 g_snprintf (tmp
, sizeof (tmp
),
692 _(" Cannot view: not a regular file "));
693 error
= set_view_init_error (view
, tmp
);
697 /* We don't need O_NONBLOCK after opening the file, unset it */
698 cntlflags
= fcntl (fd
, F_GETFL
, 0);
699 if (cntlflags
!= -1) {
700 cntlflags
&= ~O_NONBLOCK
;
701 fcntl (fd
, F_SETFL
, cntlflags
);
704 type
= get_compression_type (fd
);
706 if (view
->viewer_magic_flag
&& (type
!= COMPRESSION_NONE
)) {
707 g_free (view
->filename
);
709 g_strconcat (_file
, decompress_extension (type
), (char *) NULL
);
712 error
= load_view_file (view
, fd
, &st
);
717 if (!view
->have_frame
) {
718 message (1, MSG_ERROR
, "%s", error
);
724 view
->view_active
= 1;
726 view
->command
= g_strdup (_command
);
729 view
->search_start
= view
->start_display
= view
->start_save
=
733 view
->last_search
= 0; /* Start a new search */
735 /* Special case: The data points to the error message */
738 view
->datasize
= strlen (error
);
739 view_update_last_byte (view
);
743 if (start_line
> 1 && !error
) {
744 int saved_wrap_mode
= view
->wrap_mode
;
748 view_move_forward (view
, start_line
- 1);
749 view
->wrap_mode
= saved_wrap_mode
;
751 view
->edit_cursor
= view
->first
;
753 view
->hexedit_text
= 0;
754 view
->change_list
= NULL
;
760 view_update_bytes_per_line (WView
*view
)
764 if (view
->have_frame
)
765 cols
= view
->widget
.cols
- 2;
767 cols
= view
->widget
.cols
;
769 view
->bottom_first
= INVALID_OFFSET
;
771 view
->bytes_per_line
= ((cols
- 8) / 17) * 4;
773 view
->bytes_per_line
= ((cols
- 8) / 18) * 4;
775 if (view
->bytes_per_line
== 0)
776 view
->bytes_per_line
++; /* To avoid division by 0 */
778 view
->dirty
= max_dirt_limit
+ 1; /* To force refresh */
782 /* Return zero on success, -1 on failure */
784 view_init (WView
*view
, const char *_command
, const char *_file
, int start_line
)
786 if (!view
->view_active
|| strcmp (_file
, view
->filename
)
787 || altered_magic_flag
)
788 return do_view_init (view
, _command
, _file
, start_line
);
794 view_percent (WView
*view
, int p
, int w
, gboolean update_gui
)
798 if (view
->last_byte
== 0 || view
->last_byte
== view
->last
)
800 else if (p
> (INT_MAX
/ 100))
801 percent
= p
/ (view
->last_byte
/ 100);
803 percent
= p
* 100 / view
->last_byte
;
805 widget_move (view
, view
->have_frame
, w
- 5);
806 printw ("%3d%%", percent
);
810 view_status (WView
*view
, gboolean update_gui
)
812 static int i18n_adjust
= 0;
813 static const char *file_label
;
815 int w
= view
->widget
.cols
- (view
->have_frame
* 2);
818 attrset (SELECTED_COLOR
);
819 widget_move (view
, view
->have_frame
, view
->have_frame
);
823 file_label
= _("File: %s");
824 i18n_adjust
= strlen (file_label
) - 2;
827 if (w
< i18n_adjust
+ 6)
828 addstr ((char *) name_trunc (view
->filename
? view
->filename
:
829 view
->command
? view
->command
: "", w
));
831 i
= (w
> 22 ? 22 : w
) - i18n_adjust
;
832 printw (const_cast(char *, file_label
), name_trunc (view
->filename
? view
->filename
:
833 view
->command
? view
->command
: "",
836 widget_move (view
, view
->have_frame
, 24 + view
->have_frame
);
838 printw (const_cast(char *, _("Offset 0x%08lx")), view
->edit_cursor
);
840 printw (const_cast(char *, _("Col %d")), -view
->start_col
);
843 widget_move (view
, view
->have_frame
, 43 + view
->have_frame
);
844 printw (const_cast(char *, _("%s bytes")), size_trunc (view
->last_byte
));
848 if (view
->growing_buffer
)
849 addstr (_(" [grow]"));
853 view
->hex_mode
? view
->edit_cursor
: view
->
855 view
->widget
.cols
- view
->have_frame
+ 1,
859 attrset (SELECTED_COLOR
);
863 view_display_clean (WView
*view
, int height
, int width
)
865 /* FIXME: Should I use widget_erase only and repaint the box? */
866 if (view
->have_frame
) {
869 draw_double_box (view
->widget
.parent
, view
->widget
.y
,
870 view
->widget
.x
, view
->widget
.lines
,
872 for (i
= 1; i
< height
; i
++) {
873 widget_move (view
, i
, 1);
874 printw ("%*s", width
- 1, "");
877 widget_erase ((Widget
*) view
);
880 #define view_add_character(view,c) addch (c)
881 #define view_add_one_vline() one_vline()
882 #define view_add_string(view,s) addstr (s)
883 #define view_gotoyx(v,r,c) widget_move (v,r,c)
885 #define view_freeze(view)
886 #define view_thaw(view)
888 #define STATUS_LINES 1
897 static inline int view_count_backspaces (WView
*view
, off_t offset
)
900 while (get_byte (view
, offset
- 2 * backspaces
) == '\b')
905 /* Shows the file pointed to by *start_display on view_win */
907 display (WView
*view
)
909 const int frame_shift
= view
->have_frame
;
910 int col
= 0 + frame_shift
;
911 int row
= STATUS_LINES
+ frame_shift
;
915 mark_t boldflag
= MARK_NORMAL
;
916 struct hexedit_change_node
*curr
= view
->change_list
;
918 height
= view
->widget
.lines
- frame_shift
;
919 width
= view
->widget
.cols
- frame_shift
;
920 from
= view
->start_display
;
921 attrset (NORMAL_COLOR
);
924 view_display_clean (view
, height
, width
);
926 /* Optionally, display a ruler */
927 if ((!view
->hex_mode
) && (ruler
)) {
931 attrset (MARKED_COLOR
);
932 for (c
= frame_shift
; c
< width
; c
++) {
933 cl
= c
- view
->start_col
;
935 view_gotoyx (view
, row
, c
);
937 view_gotoyx (view
, row
+ height
- 2, c
);
941 else if ((cl
% 5) == 0)
943 view_add_character (view
, r_buff
[0]);
944 if ((cl
!= 0) && (cl
% 10) == 0) {
945 g_snprintf (r_buff
, sizeof (r_buff
), "%03d", cl
);
947 widget_move (view
, row
+ 1, c
- 1);
949 widget_move (view
, row
+ height
- 3, c
- 1);
951 view_add_string (view
, r_buff
);
954 attrset (NORMAL_COLOR
);
961 /* Find the first displayable changed byte */
962 while (curr
&& (curr
->offset
< from
)) {
965 if (view
->hex_mode
) {
966 char hex_buff
[10]; /* A temporary buffer for sprintf and mvwaddstr */
967 int bytes
; /* Number of bytes already printed on the line */
969 /* Start of text column */
970 int text_start
= width
- view
->bytes_per_line
- 1 + frame_shift
;
972 for (; (void) get_byte (view
, from
+ view
->bytes_per_line
),
973 row
< height
&& from
< view
->last_byte
; row
++) {
974 /* Print the hex offset */
975 attrset (MARKED_COLOR
);
976 g_snprintf (hex_buff
, sizeof (hex_buff
), "%08X",
977 (int) (from
- view
->first
));
978 view_gotoyx (view
, row
, frame_shift
);
979 view_add_string (view
, hex_buff
);
980 attrset (NORMAL_COLOR
);
982 /* Hex dump starts from column nine */
983 if (view
->have_frame
)
988 /* Each hex number is two digits */
991 bytes
< view
->bytes_per_line
&& from
< view
->last_byte
;
993 /* Display and mark changed bytes */
994 if (curr
&& from
== curr
->offset
) {
997 boldflag
= MARK_CHANGED
;
998 attrset (VIEW_UNDERLINED_COLOR
);
1000 c
= (unsigned char) get_byte (view
, from
);
1002 if (view
->found_len
&& from
>= view
->search_start
1003 && from
< view
->search_start
+ view
->found_len
) {
1004 boldflag
= MARK_SELECTED
;
1005 attrset (MARKED_COLOR
);
1007 /* Display the navigation cursor */
1008 if (from
== view
->edit_cursor
) {
1009 if (!view
->hexedit_text
) {
1010 view
->cursor_row
= row
;
1011 view
->cursor_col
= col
;
1013 boldflag
= MARK_CURSOR
;
1015 hexedit_text
? MARKED_SELECTED_COLOR
:
1016 VIEW_UNDERLINED_COLOR
);
1019 /* Print a hex number (sprintf is too slow) */
1020 hex_buff
[0] = hex_char
[(c
>> 4)];
1021 hex_buff
[1] = hex_char
[c
& 15];
1022 view_gotoyx (view
, row
, col
);
1023 view_add_string (view
, hex_buff
);
1025 /* Turn off the cursor or changed byte highlighting here */
1026 if (boldflag
== MARK_CURSOR
|| boldflag
== MARK_CHANGED
)
1027 attrset (NORMAL_COLOR
);
1028 if ((bytes
& 3) == 3 && bytes
+ 1 < view
->bytes_per_line
) {
1029 /* Turn off the search highlighting */
1030 if (boldflag
== MARK_SELECTED
1032 view
->search_start
+ view
->found_len
- 1)
1033 attrset (NORMAL_COLOR
);
1035 /* Hex numbers are printed in the groups of four */
1036 /* Groups are separated by a vline */
1038 view_gotoyx (view
, row
, col
- 1);
1039 view_add_character (view
, ' ');
1040 view_gotoyx (view
, row
, col
);
1041 if ((view
->have_frame
&& view
->widget
.cols
< 82)
1042 || view
->widget
.cols
< 80)
1045 view_add_one_vline ();
1049 if (boldflag
!= MARK_NORMAL
1051 view
->search_start
+ view
->found_len
- 1)
1052 attrset (MARKED_COLOR
);
1055 if (boldflag
!= MARK_NORMAL
1056 && from
< view
->search_start
+ view
->found_len
- 1
1057 && bytes
!= view
->bytes_per_line
- 1) {
1058 view_gotoyx (view
, row
, col
);
1059 view_add_character (view
, ' ');
1062 /* Print corresponding character on the text side */
1063 view_gotoyx (view
, row
, text_start
+ bytes
);
1065 c
= convert_to_display_c (c
);
1067 if (!is_printable (c
))
1073 attrset (MARKED_COLOR
);
1076 if (view
->hexedit_text
) {
1077 /* Our side is active */
1078 view
->cursor_col
= text_start
+ bytes
;
1079 view
->cursor_row
= row
;
1080 attrset (VIEW_UNDERLINED_COLOR
);
1082 /* Other side is active */
1083 attrset (MARKED_SELECTED_COLOR
);
1087 attrset (VIEW_UNDERLINED_COLOR
);
1090 view_add_character (view
, c
);
1092 if (boldflag
!= MARK_NORMAL
) {
1093 boldflag
= MARK_NORMAL
;
1094 attrset (NORMAL_COLOR
);
1099 /* the get_byte() call below might modify view->last_byte */
1100 for (; row
< height
&& ((void) (c
= get_byte (view
, from
)), from
< view
->last_byte
); from
++) {
1101 if ((c
== '\n') || (col
>= width
&& view
->wrap_mode
)) {
1104 if (c
== '\n' || row
>= height
)
1110 col
= ((col
- frame_shift
) / 8) * 8 + 8 + frame_shift
;
1113 if (view
->viewer_nroff_flag
&& c
== '\b') {
1117 if (from
+ 1 < view
->last_byte
1118 && is_printable ((c_next
= get_byte (view
, from
+ 1)))
1119 && from
> view
->first
1120 && is_printable ((c_prev
= get_byte (view
, from
- 1)))
1121 && (c_prev
== c_next
|| c_prev
== '_'
1122 || (c_prev
== '+' && c_next
== 'o'))) {
1123 if (col
<= frame_shift
) {
1124 /* So it has to be wrap_mode - do not need to check for it */
1125 if (row
== 1 + frame_shift
) {
1127 continue; /* There had to be a bold character on the rightmost position
1128 of the previous undisplayed line */
1134 boldflag
= MARK_SELECTED
;
1135 if (c_prev
== '_' && (c_next
!= '_' || view_count_backspaces (view
, from
) == 1))
1136 attrset (VIEW_UNDERLINED_COLOR
);
1138 attrset (MARKED_COLOR
);
1142 if (view
->found_len
&& from
>= view
->search_start
1143 && from
< view
->search_start
+ view
->found_len
) {
1144 boldflag
= MARK_SELECTED
;
1145 attrset (SELECTED_COLOR
);
1147 if (col
>= frame_shift
- view
->start_col
1148 && col
< width
- view
->start_col
) {
1149 view_gotoyx (view
, row
, col
+ view
->start_col
);
1151 c
= convert_to_display_c (c
);
1153 if (!is_printable (c
))
1156 view_add_character (view
, c
);
1159 if (boldflag
!= MARK_NORMAL
) {
1160 boldflag
= MARK_NORMAL
;
1161 attrset (NORMAL_COLOR
);
1164 /* Very last thing */
1165 if (view
->growing_buffer
&& from
+ 1 == view
->last_byte
)
1166 get_byte (view
, from
+ 1);
1175 view_place_cursor (WView
*view
)
1179 if (!view
->hexedit_text
&& view
->nib_shift
)
1184 widget_move (&view
->widget
, view
->cursor_row
,
1185 view
->cursor_col
+ shift
);
1189 view_update (WView
*view
, gboolean update_gui
)
1191 static int dirt_limit
= 1;
1193 if (view
->dirty
> dirt_limit
) {
1194 /* Too many updates skipped -> force a update */
1196 view_status (view
, update_gui
);
1198 /* Raise the update skipping limit */
1200 if (dirt_limit
> max_dirt_limit
)
1201 dirt_limit
= max_dirt_limit
;
1205 /* We have time to update the screen properly */
1207 view_status (view
, update_gui
);
1212 /* We are busy -> skipping full update,
1213 only the status line is updated */
1214 view_status (view
, update_gui
);
1216 /* Here we had a refresh, if fast scrolling does not work
1217 restore the refresh, although this should not happen */
1222 my_define (Dlg_head
*h
, int idx
, const char *text
, void (*fn
) (WView
*),
1225 define_label_data (h
, idx
, text
, (buttonbarfn
) fn
, view
);
1228 /* If the last parameter is nonzero, it means we want get the count of lines
1229 from current up to the the upto position inclusive */
1231 move_forward2 (WView
*view
, offset_type current
, int lines
, offset_type upto
)
1237 if (view
->hex_mode
) {
1238 p
= current
+ lines
* view
->bytes_per_line
;
1239 p
= (p
>= view
->last_byte
) ? current
: p
;
1241 q
= view
->edit_cursor
+ view
->bytes_per_line
;
1242 line
= q
/ view
->bytes_per_line
;
1243 col
= (view
->last_byte
- 1) / view
->bytes_per_line
;
1244 view
->edit_cursor
= (line
> col
) ? view
->edit_cursor
: q
;
1245 view
->edit_cursor
= (view
->edit_cursor
< view
->last_byte
) ?
1246 view
->edit_cursor
: view
->last_byte
- 1;
1247 q
= current
+ ((LINES
- 2) * view
->bytes_per_line
);
1248 p
= (view
->edit_cursor
< q
) ? current
: p
;
1250 view
->edit_cursor
= (view
->edit_cursor
< p
) ?
1251 p
: view
->edit_cursor
;
1259 q
= view
->last_byte
;
1260 if (get_byte (view
, q
) != '\n')
1262 for (line
= col
= 0, p
= current
; p
< q
; p
++) {
1265 if (lines
!= -1 && line
>= lines
)
1268 c
= get_byte (view
, p
);
1270 if (view
->wrap_mode
) {
1272 continue; /* This characters is never displayed */
1275 ((col
- view
->have_frame
) / 8) * 8 + 8 +
1279 if (view
->viewer_nroff_flag
&& c
== '\b') {
1280 if (p
+ 1 < view
->last_byte
1281 && is_printable (get_byte (view
, p
+ 1))
1283 && is_printable (get_byte (view
, p
- 1)))
1285 } else if (col
== vwidth
) {
1286 /* FIXME: the c in is_printable was a p, that is a bug,
1287 I suspect I got that fix from Jakub, same applies
1289 int d
= get_byte (view
, p
+ 2);
1291 if (p
+ 2 >= view
->last_byte
|| !is_printable (c
) ||
1292 !view
->viewer_nroff_flag
1293 || get_byte (view
, p
+ 1) != '\b'
1294 || !is_printable (d
)) {
1297 if (c
== '\n' || get_byte (view
, p
+ 1) != '\n')
1300 } else if (c
== '\n') {
1304 } else if (c
== '\n')
1313 /* special case for text (non-hex) mode with line wrapping. */
1315 move_backward2_textmode_wrap (WView
* view
, offset_type current
, int lines
)
1317 offset_type p
, q
, pm
;
1320 if (current
== view
->last_byte
&& get_byte (view
, current
- 1) != '\n')
1321 /* There is one virtual '\n' at the end,
1322 so that the last line is shown */
1327 for (q
= p
= current
- 1; p
> view
->first
; p
--) {
1328 if (get_byte (view
, p
) == '\n' || p
== view
->first
) {
1329 pm
= p
> view
->first
? p
+ 1 : view
->first
;
1330 line
+= move_forward2 (view
, pm
, 0, q
);
1331 if (line
>= lines
) {
1335 return move_forward2 (view
, pm
, line
- lines
, 0);
1340 return p
> view
->first
? p
: view
->first
;
1343 /* returns the new current pointer */
1344 /* Cause even the forward routine became very complex, we in the wrap_mode
1345 just find the nearest '\n', use move_forward2(p, 0, q) to get the count
1346 of lines up to there and then use move_forward2(p, something, 0), which we
1349 move_backward2 (WView
*view
, offset_type current
, int lines
)
1351 if (view
->hex_mode
) {
1352 if (view
->edit_cursor
>= lines
* view
->bytes_per_line
) {
1353 view
->edit_cursor
-= lines
* view
->bytes_per_line
;
1355 view
->edit_cursor
%= view
->bytes_per_line
;
1357 if (current
> view
->edit_cursor
) {
1358 /* cursor is out-of-view -- adjust the view */
1359 if (current
>= lines
* view
->bytes_per_line
) {
1360 current
-= lines
* view
->bytes_per_line
;
1362 current
%= view
->bytes_per_line
;
1367 if (current
== view
->first
)
1370 if (view
->wrap_mode
)
1371 return move_backward2_textmode_wrap (view
, current
, lines
);
1373 /* There is one virtual '\n' at the end,
1374 * so that the last line is shown */
1375 if (current
== view
->last_byte
&& get_byte (view
, current
- 1) != '\n')
1377 while (current
> view
->first
&& get_byte(view
, current
- 1) != '\n')
1380 if (current
> view
->first
)
1383 while (current
> view
->first
&& get_byte(view
, current
- 1) != '\n')
1388 return current
; /* unreached */
1392 view_move_backward (WView
*view
, int i
)
1394 view
->search_start
= view
->start_display
=
1395 move_backward2 (view
, view
->start_display
, i
);
1396 view
->found_len
= 0;
1397 view
->last
= view
->first
+ ((LINES
- 2) * view
->bytes_per_line
);
1402 get_bottom_first (WView
*view
, int do_not_cache
, int really
)
1404 offset_type bottom_first
;
1406 if (!have_fast_cpu
&& !really
)
1409 if (!do_not_cache
&& view
->bottom_first
!= INVALID_OFFSET
)
1410 return view
->bottom_first
;
1413 if (view
->growing_buffer
)
1414 view_growbuf_read_until (view
, OFFSETTYPE_MAX
);
1416 bottom_first
= move_backward2 (view
, view
->last_byte
, vheight
- 1);
1419 bottom_first
= (bottom_first
+ view
->bytes_per_line
- 1)
1420 / view
->bytes_per_line
* view
->bytes_per_line
;
1421 view
->bottom_first
= bottom_first
;
1423 return view
->bottom_first
;
1427 view_move_forward (WView
*view
, int i
)
1429 view
->start_display
= move_forward2 (view
, view
->start_display
, i
, 0);
1430 if (!view
->reading_pipe
1431 && view
->start_display
> get_bottom_first (view
, 0, 0))
1432 view
->start_display
= view
->bottom_first
;
1433 view
->search_start
= view
->start_display
;
1434 view
->found_len
= 0;
1435 view
->last
= view
->first
+ ((LINES
- 2) * view
->bytes_per_line
);
1441 move_to_top (WView
*view
)
1443 view
->search_start
= view
->start_display
= view
->first
;
1444 view
->found_len
= 0;
1445 view
->last
= view
->first
+ ((LINES
- 2) * view
->bytes_per_line
);
1446 view
->nib_shift
= 0;
1447 view
->edit_cursor
= view
->start_display
;
1452 move_to_bottom (WView
*view
)
1454 view
->search_start
= view
->start_display
=
1455 get_bottom_first (view
, 0, 1);
1456 view
->found_len
= 0;
1457 view
->last
= view
->first
+ ((LINES
- 2) * view
->bytes_per_line
);
1458 view
->edit_cursor
= (view
->edit_cursor
< view
->start_display
) ?
1459 view
->start_display
: view
->edit_cursor
;
1463 /* Scroll left/right the view panel functions */
1465 move_right (WView
*view
)
1467 if (view
->wrap_mode
&& !view
->hex_mode
)
1469 if (view
->hex_mode
) {
1470 view
->last
= view
->first
+ ((LINES
- 2) * view
->bytes_per_line
);
1472 if (view
->hex_mode
&& !view
->hexedit_text
) {
1473 view
->nib_shift
= !view
->nib_shift
;
1474 if (view
->nib_shift
)
1477 view
->edit_cursor
= (++view
->edit_cursor
< view
->last_byte
) ?
1478 view
->edit_cursor
: view
->last_byte
- 1;
1479 if (view
->edit_cursor
>= view
->last
) {
1480 view
->edit_cursor
-= view
->bytes_per_line
;
1481 view_move_forward (view
, 1);
1483 } else if (--view
->start_col
> 0)
1484 view
->start_col
= 0;
1489 move_left (WView
*view
)
1491 if (view
->wrap_mode
&& !view
->hex_mode
)
1493 if (view
->hex_mode
) {
1494 if (view
->hex_mode
&& !view
->hexedit_text
) {
1495 view
->nib_shift
= !view
->nib_shift
;
1496 if (!view
->nib_shift
)
1499 if (view
->edit_cursor
> view
->first
)
1500 --view
->edit_cursor
;
1501 if (view
->edit_cursor
< view
->start_display
) {
1502 view
->edit_cursor
+= view
->bytes_per_line
;
1503 view_move_backward (view
, 1);
1505 } else if (++view
->start_col
> 0)
1506 view
->start_col
= 0;
1510 /* Case insensitive search of text in data */
1512 icase_search_p (WView
*view
, char *text
, char *data
, int nothing
)
1516 const int direction
= view
->direction
;
1518 /* If we are searching backwards, reverse the string */
1519 if (direction
== -1) {
1520 g_strreverse (text
);
1521 g_strreverse (data
);
1524 q
= _icase_search (text
, data
, &lng
);
1526 if (direction
== -1) {
1527 g_strreverse (text
);
1528 g_strreverse (data
);
1533 view
->search_start
= q
- data
- lng
;
1535 view
->search_start
= strlen (data
) - (q
- data
);
1536 view
->found_len
= lng
;
1543 grow_string_buffer (char *text
, int *size
)
1547 /* The grow steps */
1549 new = g_realloc (text
, *size
);
1557 get_line_at (WView
*view
, offset_type
*p
, offset_type
*skipped
)
1559 char *buffer
= NULL
;
1560 int buffer_size
= 0;
1561 offset_type usable_size
= 0;
1563 const int direction
= view
->direction
;
1564 offset_type pos
= *p
;
1568 if (!pos
&& direction
== -1)
1571 /* skip over all the possible zeros in the file */
1572 while ((ch
= get_byte (view
, pos
)) == 0) {
1573 if (!pos
&& direction
== -1)
1580 if (!i
&& (pos
|| direction
== -1)) {
1581 prev
= get_byte (view
, pos
- direction
);
1582 if ((prev
== -1) || (prev
== '\n'))
1586 for (i
= 1; ch
!= -1; ch
= get_byte (view
, pos
)) {
1588 if (i
>= usable_size
) {
1589 buffer
= grow_string_buffer (buffer
, &buffer_size
);
1590 usable_size
= buffer_size
- 2; /* prev & null terminator */
1595 if (!pos
&& direction
== -1)
1600 if (ch
== '\n' || !ch
) {
1601 i
--; /* Strip newline/zero */
1610 /* If we are searching backwards, reverse the string */
1611 if (direction
< 0) {
1612 g_strreverse (buffer
+ 1);
1621 search_update_steps (WView
*view
)
1623 if (view
->last_byte
!= 0)
1624 view
->update_steps
= 40000;
1625 else /* viewing a data stream, not a file */
1626 view
->update_steps
= view
->last_byte
/ 100;
1628 /* Do not update the percent display but every 20 ks */
1629 if (view
->update_steps
< 20000)
1630 view
->update_steps
= 20000;
1634 search (WView
*view
, char *text
,
1635 int (*search
) (WView
*, char *, char *, int))
1637 const int w
= view
->widget
.cols
- view
->have_frame
+ 1;
1639 char *s
= NULL
; /* The line we read from the view buffer */
1640 offset_type p
, beginning
, search_start
;
1645 /* Used to keep track of where the line starts, when looking forward */
1646 /* is the index before transfering the line; the reverse case uses */
1647 /* the position returned after the line has been read */
1648 offset_type forward_line_start
;
1649 offset_type reverse_line_start
;
1651 /* Clear interrupt status */
1655 d
= create_message (D_NORMAL
, _("Search"), _("Searching %s"), text
);
1659 found_len
= view
->found_len
;
1660 search_start
= view
->search_start
;
1662 if (view
->direction
== 1) {
1663 p
= found_len
? search_start
+ 1 : search_start
;
1665 p
= (found_len
&& search_start
) ? search_start
- 1 : search_start
;
1669 /* Compute the percent steps */
1670 search_update_steps (view
);
1671 view
->update_activate
= 0;
1673 for (;; g_free (s
)) {
1674 if (p
>= view
->update_activate
) {
1675 view
->update_activate
+= view
->update_steps
;
1677 view_percent (view
, p
, w
, TRUE
);
1680 if (got_interrupt ())
1683 forward_line_start
= p
;
1684 disable_interrupt_key ();
1685 s
= get_line_at (view
, &p
, &t
);
1686 reverse_line_start
= p
;
1687 enable_interrupt_key ();
1692 search_status
= (*search
) (view
, text
, s
+ 1, match_normal
);
1693 if (search_status
< 0) {
1698 if (search_status
== 0)
1701 /* We found the string */
1703 /* Handle ^ and $ when regexp search starts at the middle of the line */
1704 if (*s
&& !view
->search_start
&& (search
== regexp_view_search
)) {
1705 if ((*text
== '^' && view
->direction
== 1)
1706 || (view
->direction
== -1 && text
[strlen (text
) - 1] == '$')
1711 /* Record the position used to continue the search */
1712 if (view
->direction
== 1)
1713 t
+= forward_line_start
;
1715 t
= reverse_line_start
? reverse_line_start
+ 3 : 0;
1716 view
->search_start
+= t
;
1718 if (t
!= beginning
) {
1719 if (t
> get_bottom_first (view
, 0, 0))
1720 view
->start_display
= view
->bottom_first
;
1722 view
->start_display
= t
;
1728 disable_interrupt_key ();
1734 message (0, _("Search"), _(" Search string not found "));
1735 view
->found_len
= 0;
1739 /* Search buffer (its size is len) in the complete buffer
1740 * returns the position where the block was found or INVALID_OFFSET
1743 block_search (WView
*view
, const char *buffer
, int len
)
1745 const int w
= view
->widget
.cols
- view
->have_frame
+ 1;
1746 int direction
= view
->direction
;
1747 const char *d
= buffer
;
1751 /* clear interrupt status */
1753 enable_interrupt_key ();
1755 e
= view
->found_len
? view
->search_start
+ 1 : view
->search_start
;
1757 e
= (view
->found_len
1758 && view
->search_start
) ? view
->search_start
- 1
1759 : view
->search_start
;
1761 search_update_steps (view
);
1762 view
->update_activate
= 0;
1764 if (direction
== -1) {
1765 for (d
+= len
- 1;; e
--) {
1766 if (e
<= view
->update_activate
) {
1767 view
->update_activate
-= view
->update_steps
;
1769 view_percent (view
, e
, w
, TRUE
);
1772 if (got_interrupt ())
1775 b
= get_byte (view
, e
);
1779 disable_interrupt_key ();
1784 e
+= buffer
+ len
- 1 - d
;
1785 d
= buffer
+ len
- 1;
1791 while (e
< view
->last_byte
) {
1792 if (e
>= view
->update_activate
) {
1793 view
->update_activate
+= view
->update_steps
;
1795 view_percent (view
, e
, w
, TRUE
);
1798 if (got_interrupt ())
1801 b
= get_byte (view
, e
++);
1805 if (d
- buffer
== len
) {
1806 disable_interrupt_key ();
1815 disable_interrupt_key ();
1816 return INVALID_OFFSET
;
1820 * Search in the hex mode. Supported input:
1821 * - numbers (oct, dec, hex). Each of them matches one byte.
1822 * - strings in double quotes. Matches exactly without quotes.
1825 hex_search (WView
*view
, const char *text
)
1827 char *buffer
; /* Parsed search string */
1828 char *cur
; /* Current position in it */
1829 int block_len
; /* Length of the search string */
1830 offset_type pos
; /* Position of the string in the file */
1831 int parse_error
= 0;
1834 view
->found_len
= 0;
1838 /* buffer will never be longer that text */
1839 buffer
= g_new (char, strlen (text
));
1842 /* First convert the string to a stream of bytes */
1847 /* Skip leading spaces */
1848 if (*text
== ' ' || *text
== '\t') {
1853 /* %i matches octal, decimal, and hexadecimal numbers */
1854 if (sscanf (text
, "%i%n", &val
, &ptr
) > 0) {
1855 /* Allow signed and unsigned char in the user input */
1856 if (val
< -128 || val
> 255) {
1861 *cur
++ = (char) val
;
1866 /* Try quoted string, strip quotes */
1868 const char *next_quote
;
1871 next_quote
= strchr (text
, '"');
1873 memcpy (cur
, text
, next_quote
- text
);
1874 cur
+= next_quote
- text
;
1875 text
= next_quote
+ 1;
1885 block_len
= cur
- buffer
;
1887 /* No valid bytes in the user input */
1888 if (block_len
<= 0 || parse_error
) {
1889 message (0, _("Search"), _("Invalid hex search expression"));
1891 view
->found_len
= 0;
1895 /* Then start the search */
1896 pos
= block_search (view
, buffer
, block_len
);
1900 if (pos
== INVALID_OFFSET
) {
1901 message (0, _("Search"), _(" Search string not found "));
1902 view
->found_len
= 0;
1906 view
->search_start
= pos
;
1907 view
->found_len
= block_len
;
1908 /* Set the edit cursor to the search position, left nibble */
1909 view
->edit_cursor
= view
->search_start
;
1910 view
->nib_shift
= 0;
1912 /* Adjust the file offset */
1913 view
->start_display
= (pos
& (~(view
->bytes_per_line
- 1)));
1914 if (view
->start_display
> get_bottom_first (view
, 0, 0))
1915 view
->start_display
= view
->bottom_first
;
1919 regexp_view_search (WView
*view
, char *pattern
, char *string
,
1923 static char *old_pattern
= NULL
;
1924 static int old_type
;
1925 regmatch_t pmatch
[1];
1926 int i
, flags
= REG_ICASE
;
1928 if (!old_pattern
|| strcmp (old_pattern
, pattern
)
1929 || old_type
!= match_type
) {
1932 g_free (old_pattern
);
1935 for (i
= 0; pattern
[i
] != 0; i
++) {
1936 if (isupper ((unsigned char) pattern
[i
])) {
1941 flags
|= REG_EXTENDED
;
1942 if (regcomp (&r
, pattern
, flags
)) {
1943 message (1, MSG_ERROR
, _(" Invalid regular expression "));
1946 old_pattern
= g_strdup (pattern
);
1947 old_type
= match_type
;
1949 if (regexec (&r
, string
, 1, pmatch
, 0) != 0)
1951 view
->found_len
= pmatch
[0].rm_eo
- pmatch
[0].rm_so
;
1952 view
->search_start
= pmatch
[0].rm_so
;
1957 do_regexp_search (void *xview
, char *regexp
)
1959 WView
*view
= (WView
*) xview
;
1961 view
->search_exp
= regexp
;
1962 search (view
, regexp
, regexp_view_search
);
1963 /* Had a refresh here */
1965 view_update (view
, TRUE
);
1969 do_normal_search (void *xview
, char *text
)
1971 WView
*view
= (WView
*) xview
;
1973 view
->search_exp
= text
;
1975 hex_search (view
, text
);
1977 search (view
, text
, icase_search_p
);
1978 /* Had a refresh here */
1980 view_update (view
, TRUE
);
1983 /* Real view only */
1985 view_help_cmd (void)
1987 interactive_display (NULL
, "[Internal File Viewer]");
1993 /* Toggle between text edit and hex edit */
1995 toggle_hexedit_mode (WView
*view
)
1997 get_bottom_first (view
, 1, 1);
1998 if (view
->hexedit_mode
) {
1999 view
->hexedit_text
= !view
->hexedit_text
;
2001 view
->hexedit_mode
= !view
->hexedit_mode
;
2005 view_update (view
, TRUE
);
2008 /* Toggle between wrapped and unwrapped view */
2010 toggle_wrap_mode (WView
*view
)
2012 view
->wrap_mode
= 1 - view
->wrap_mode
;
2013 get_bottom_first (view
, 1, 1);
2014 if (view
->wrap_mode
)
2015 view
->start_col
= 0;
2017 if (have_fast_cpu
) {
2018 if (view
->bottom_first
< view
->start_display
)
2019 view
->search_start
= view
->start_display
=
2021 view
->found_len
= 0;
2026 view_update (view
, TRUE
);
2029 /* Toggle between hex view and text view */
2031 toggle_hex_mode (WView
*view
)
2033 view
->hex_mode
= 1 - view
->hex_mode
;
2035 if (view
->hex_mode
) {
2036 /* Shift the line start to 0x____0 on entry, restore it for text */
2037 view
->start_save
= view
->start_display
;
2038 view
->start_display
-= view
->start_display
% view
->bytes_per_line
;
2039 view
->edit_cursor
= view
->start_display
;
2040 view
->widget
.options
|= W_WANT_CURSOR
;
2042 view
->start_display
= view
->start_save
;
2043 view
->widget
.options
&= ~W_WANT_CURSOR
;
2045 altered_hex_mode
= 1;
2046 get_bottom_first (view
, 1, 1);
2049 view_update (view
, TRUE
);
2054 goto_line (WView
*view
)
2056 char *line
, prompt
[BUF_SMALL
];
2058 int saved_wrap_mode
= view
->wrap_mode
;
2061 view
->wrap_mode
= 0;
2062 for (i
= view
->first
; i
< view
->start_display
; i
++)
2063 if (get_byte (view
, i
) == '\n')
2065 g_snprintf (prompt
, sizeof (prompt
),
2066 _(" The current line number is %d.\n"
2067 " Enter the new line number:"), oldline
);
2068 line
= input_dialog (_(" Goto line "), prompt
, "");
2072 view_move_forward (view
, atol (line
) - 1);
2077 view
->wrap_mode
= saved_wrap_mode
;
2078 view_update (view
, TRUE
);
2083 goto_addr (WView
*view
)
2085 char *line
, *error
, prompt
[BUF_SMALL
];
2088 g_snprintf (prompt
, sizeof (prompt
),
2089 _(" The current address is 0x%lx.\n"
2090 " Enter the new address:"), view
->edit_cursor
);
2091 line
= input_dialog (_(" Goto Address "), prompt
, "");
2094 addr
= strtoul (line
, &error
, 0);
2095 if ((*error
== '\0') && (addr
<= view
->last_byte
)) {
2097 view_move_forward (view
, addr
/ view
->bytes_per_line
);
2098 view
->edit_cursor
= addr
;
2104 view_update (view
, TRUE
);
2109 regexp_search (WView
*view
, int direction
)
2112 static char *old
= 0;
2114 /* This is really an F6 key handler */
2115 if (view
->hex_mode
) {
2116 /* Save it without a confirmation prompt */
2117 if (view
->change_list
)
2118 save_edit_changes (view
);
2122 regexp
= old
? old
: regexp
;
2123 regexp
= input_dialog (_("Search"), _(" Enter regexp:"), regexp
);
2134 /* Mhm, do we really need to load all the file in the core? */
2135 if (view
->bytes_read
< view
->last_byte
)
2136 get_byte (view
, view
->last_byte
- 1); /* Get the whole file in to memory */
2138 view
->direction
= direction
;
2139 do_regexp_search (view
, regexp
);
2141 view
->last_search
= do_regexp_search
;
2145 regexp_search_cmd (WView
*view
)
2147 regexp_search (view
, 1);
2152 normal_search (WView
*view
, int direction
)
2155 char *exp
= old
? old
: "";
2158 SEARCH_DLG_HEIGHT
= 8,
2159 SEARCH_DLG_WIDTH
= 58
2162 static int replace_backwards
;
2163 int treplace_backwards
= replace_backwards
;
2165 static QuickWidget quick_widgets
[] = {
2166 {quick_button
, 6, 10, 5, SEARCH_DLG_HEIGHT
, N_("&Cancel"), 0,
2169 {quick_button
, 2, 10, 5, SEARCH_DLG_HEIGHT
, N_("&OK"), 0, B_ENTER
,
2171 {quick_checkbox
, 3, SEARCH_DLG_WIDTH
, 4, SEARCH_DLG_HEIGHT
,
2172 N_("&Backwards"), 0, 0,
2174 {quick_input
, 3, SEARCH_DLG_WIDTH
, 3, SEARCH_DLG_HEIGHT
, "", 52, 0,
2175 0, 0, N_("Search")},
2176 {quick_label
, 2, SEARCH_DLG_WIDTH
, 2, SEARCH_DLG_HEIGHT
,
2177 N_(" Enter search string:"), 0, 0,
2181 static QuickDialog Quick_input
= {
2182 SEARCH_DLG_WIDTH
, SEARCH_DLG_HEIGHT
, -1, 0, N_("Search"),
2183 "[Input Line Keys]", quick_widgets
, 0
2185 convert_to_display (old
);
2187 quick_widgets
[2].result
= &treplace_backwards
;
2188 quick_widgets
[3].str_result
= &exp
;
2189 quick_widgets
[3].text
= exp
;
2191 if (quick_dialog (&Quick_input
) == B_CANCEL
) {
2192 convert_from_input (old
);
2195 replace_backwards
= treplace_backwards
;
2197 convert_from_input (old
);
2210 convert_from_input (exp
);
2212 view
->direction
= replace_backwards
? -1 : 1;
2213 do_normal_search (view
, exp
);
2214 view
->last_search
= do_normal_search
;
2218 normal_search_cmd (WView
*view
)
2220 normal_search (view
, 1);
2224 change_viewer (WView
*view
)
2230 if (*view
->filename
) {
2231 altered_magic_flag
= 1;
2232 view
->viewer_magic_flag
= !view
->viewer_magic_flag
;
2233 s
= g_strdup (view
->filename
);
2235 t
= g_strdup (view
->command
);
2240 view_init (view
, t
, s
, 0);
2245 view_update (view
, TRUE
);
2250 change_nroff (WView
*view
)
2252 view
->viewer_nroff_flag
= !view
->viewer_nroff_flag
;
2253 altered_nroff_flag
= 1;
2256 view_update (view
, TRUE
);
2259 /* Real view only */
2261 view_quit_cmd (WView
*view
)
2263 if (view_ok_to_quit (view
))
2264 dlg_stop (view
->widget
.parent
);
2267 /* Define labels and handlers for functional keys */
2269 view_labels (WView
*view
)
2271 Dlg_head
*h
= view
->widget
.parent
;
2273 define_label (h
, 1, _("Help"), view_help_cmd
);
2275 my_define (h
, 10, _("Quit"), view_quit_cmd
, view
);
2276 my_define (h
, 4, view
->hex_mode
? _("Ascii") : _("Hex"),
2277 toggle_hex_mode
, view
);
2278 my_define (h
, 5, view
->hex_mode
? _("Goto") : _("Line"),
2279 view
->hex_mode
? goto_addr
: goto_line
, view
);
2280 my_define (h
, 6, view
->hex_mode
? _("Save") : _("RxSrch"),
2281 regexp_search_cmd
, view
);
2284 if (view
->hexedit_mode
)
2285 my_define (h
, 2, view
->hexedit_text
? _("EdHex") : _("EdText"),
2286 toggle_hexedit_mode
, view
);
2288 if (view
->growing_buffer
|| view
->have_frame
)
2289 my_define (h
, 2, "", NULL
, view
);
2291 my_define (h
, 2, _("Edit"), toggle_hexedit_mode
, view
);
2293 my_define (h
, 2, view
->wrap_mode
? _("UnWrap") : _("Wrap"),
2294 toggle_wrap_mode
, view
);
2296 my_define (h
, 7, view
->hex_mode
? _("HxSrch") : _("Search"),
2297 normal_search_cmd
, view
);
2299 my_define (h
, 8, view
->viewer_magic_flag
? _("Raw") : _("Parse"),
2300 change_viewer
, view
);
2302 if (!view
->have_frame
) {
2304 view
->viewer_nroff_flag
? _("Unform") : _("Format"),
2305 change_nroff
, view
);
2306 my_define (h
, 3, _("Quit"), view_quit_cmd
, view
);
2312 /* Check for left and right arrows, possibly with modifiers */
2314 check_left_right_keys (WView
*view
, int c
)
2316 if (c
== KEY_LEFT
) {
2321 if (c
== KEY_RIGHT
) {
2326 /* Ctrl with arrows moves by 10 postions in the unwrap mode */
2327 if (view
->hex_mode
|| view
->wrap_mode
)
2328 return MSG_NOT_HANDLED
;
2330 if (c
== (KEY_M_CTRL
| KEY_LEFT
)) {
2331 view
->start_col
= view
->start_col
+ 10;
2332 if (view
->start_col
> 0)
2333 view
->start_col
= 0;
2338 if (c
== (KEY_M_CTRL
| KEY_RIGHT
)) {
2339 view
->start_col
= view
->start_col
- 10;
2344 return MSG_NOT_HANDLED
;
2348 set_monitor (WView
*view
, int set_on
)
2350 int old
= view
->monitor
;
2352 view
->monitor
= set_on
;
2354 if (view
->monitor
) {
2355 move_to_bottom (view
);
2356 view
->bottom_first
= INVALID_OFFSET
;
2357 set_idle_proc (view
->widget
.parent
, 1);
2360 set_idle_proc (view
->widget
.parent
, 0);
2365 continue_search (WView
*view
)
2367 if (view
->last_search
) {
2368 (*view
->last_search
) (view
, view
->search_exp
);
2370 /* if not... then ask for an expression */
2371 normal_search (view
, 1);
2377 view_handle_key (WView
*view
, int c
)
2379 int prev_monitor
= view
->monitor
;
2381 set_monitor (view
, off
);
2383 c
= convert_from_input_c (c
);
2385 if (view
->hex_mode
) {
2387 case 0x09: /* Tab key */
2388 view
->hexedit_text
= !view
->hexedit_text
;
2392 case XCTRL ('a'): /* Beginning of line */
2393 view
->edit_cursor
-= view
->edit_cursor
% view
->bytes_per_line
;
2397 case XCTRL ('b'): /* Character back */
2401 case XCTRL ('e'): /* End of line */
2402 view_move_cursor_to_eol (view
);
2405 case XCTRL ('f'): /* Character forward */
2410 if (view
->hexedit_mode
2411 && view_handle_editkey (view
, c
) == MSG_HANDLED
)
2415 if (check_left_right_keys (view
, c
))
2418 if (check_movement_keys
2419 (c
, vheight
, view
, (movefn
) view_move_backward
,
2420 (movefn
) view_move_forward
, (movefn
) move_to_top
,
2421 (movefn
) move_to_bottom
)) {
2427 regexp_search (view
, -1);
2431 regexp_search (view
, 1);
2434 /* Continue search */
2438 continue_search (view
);
2442 if (view
->last_search
) {
2443 (*view
->last_search
) (view
, view
->search_exp
);
2445 normal_search (view
, -1);
2472 view_move_forward (view
, 1);
2476 view_move_forward (view
, vheight
/ 2);
2480 view_move_backward (view
, vheight
/ 2);
2485 view_move_backward (view
, 1);
2494 view_move_forward (view
, vheight
- 1);
2501 /* Unlike Ctrl-O, run a new shell if the subshell is not running. */
2507 set_monitor (view
, on
);
2511 view_move_backward (view
, vheight
- 1);
2515 view_move_backward (view
, 2);
2519 view_move_forward (view
, 2);
2523 view
->marks
[view
->marker
] = view
->start_display
;
2527 view
->start_display
= view
->marks
[view
->marker
];
2531 /* Use to indicate parent that we want to see the next/previous file */
2532 /* Only works on full screen mode */
2535 if (!view
->have_frame
)
2536 view
->move_dir
= c
== XCTRL ('f') ? 1 : -1;
2542 if (view_ok_to_quit (view
))
2543 view
->view_quit
= 1;
2548 do_select_codepage ();
2550 view_update (view
, TRUE
);
2552 #endif /* HAVE_CHARSET */
2555 if (c
>= '0' && c
<= '9')
2556 view
->marker
= c
- '0';
2558 /* Restore the monitor status */
2559 set_monitor (view
, prev_monitor
);
2562 return MSG_NOT_HANDLED
;
2567 view_event (WView
*view
, Gpm_Event
*event
, int *result
)
2569 *result
= MOU_NORMAL
;
2571 /* We are not interested in the release events */
2572 if (!(event
->type
& (GPM_DOWN
| GPM_DRAG
)))
2576 if ((event
->buttons
& GPM_B_UP
) && (event
->type
& GPM_DOWN
)) {
2577 view_move_backward (view
, 2);
2580 if ((event
->buttons
& GPM_B_DOWN
) && (event
->type
& GPM_DOWN
)) {
2581 view_move_forward (view
, 2);
2585 /* Scrolling left and right */
2586 if (!view
->wrap_mode
) {
2587 if (event
->x
< view
->widget
.cols
/ 4) {
2591 if (event
->x
> 3 * vwidth
/ 4) {
2597 /* Scrolling up and down */
2598 if (event
->y
< view
->widget
.lines
/ 3) {
2599 if (mouse_move_pages_viewer
)
2600 view_move_backward (view
, view
->widget
.lines
/ 2 - 1);
2602 view_move_backward (view
, 1);
2604 } else if (event
->y
> 2 * vheight
/ 3) {
2605 if (mouse_move_pages_viewer
)
2606 view_move_forward (view
, vheight
/ 2 - 1);
2608 view_move_forward (view
, 1);
2615 *result
= MOU_REPEAT
;
2619 /* Real view only */
2621 real_view_event (Gpm_Event
*event
, void *x
)
2625 if (view_event ((WView
*) x
, event
, &result
))
2626 view_update ((WView
*) x
, TRUE
);
2631 view_adjust_size (Dlg_head
*h
)
2636 /* Look up the viewer and the buttonbar, we assume only two widgets here */
2637 view
= (WView
*) find_widget_type (h
, (callback_fn
) view_callback
);
2638 bar
= find_buttonbar (h
);
2639 widget_set_size (&view
->widget
, 0, 0, LINES
- 1, COLS
);
2640 widget_set_size (&bar
->widget
, LINES
- 1, 0, 1, COLS
);
2642 view_update_bytes_per_line (view
);
2645 /* Callback for the view dialog */
2647 view_dialog_callback (Dlg_head
*h
, dlg_msg_t msg
, int parm
)
2651 view_adjust_size (h
);
2655 return default_dlg_callback (h
, msg
, parm
);
2659 /* Real view only */
2661 view (const char *_command
, const char *_file
, int *move_dir_p
, int start_line
)
2668 /* Create dialog and widgets, put them on the dialog */
2670 create_dlg (0, 0, LINES
, COLS
, NULL
, view_dialog_callback
,
2671 "[Internal File Viewer]", NULL
, DLG_WANT_TAB
);
2673 wview
= view_new (0, 0, COLS
, LINES
- 1, 0);
2675 bar
= buttonbar_new (1);
2677 add_widget (view_dlg
, bar
);
2678 add_widget (view_dlg
, wview
);
2680 error
= view_init (wview
, _command
, _file
, start_line
);
2684 /* Please note that if you add another widget,
2685 * you have to modify view_adjust_size to
2691 *move_dir_p
= wview
->move_dir
;
2693 destroy_dlg (view_dlg
);
2701 WView
*view
= (WView
*) v
;
2704 /* If the user is busy typing, wait until he finishes to update the
2707 if (!hook_present (idle_hook
, view_hook
))
2708 add_hook (&idle_hook
, view_hook
, v
);
2712 delete_hook (&idle_hook
, view_hook
);
2714 if (get_current_type () == view_listing
)
2715 panel
= current_panel
;
2716 else if (get_other_type () == view_listing
)
2717 panel
= other_panel
;
2721 view_init (view
, 0, panel
->dir
.list
[panel
->selected
].fname
, 0);
2723 view_status (view
, TRUE
);
2727 view_callback (WView
*view
, widget_msg_t msg
, int parm
)
2730 Dlg_head
*h
= view
->widget
.parent
;
2732 view_update_bytes_per_line (view
);
2736 view_update_bytes_per_line (view
);
2737 if (view
->have_frame
)
2738 add_hook (&select_file_hook
, view_hook
, view
);
2745 view_status (view
, TRUE
);
2750 view_place_cursor (view
);
2754 i
= view_handle_key ((WView
*) view
, parm
);
2755 if (view
->view_quit
&& !view
->have_frame
)
2758 view_update (view
, TRUE
);
2763 /* This event is generated when the user is using the 'F' flag */
2764 view
->bottom_first
= INVALID_OFFSET
;
2765 move_to_bottom (view
);
2767 view_status (view
, TRUE
);
2775 case WIDGET_DESTROY
:
2777 if (view
->have_frame
)
2778 delete_hook (&select_file_hook
, view_hook
);
2781 case WIDGET_RESIZED
:
2782 view_update_bytes_per_line (view
);
2786 return default_proc (msg
, parm
);
2791 view_new (int y
, int x
, int cols
, int lines
, int is_panel
)
2793 WView
*view
= g_new0 (WView
, 1);
2795 init_widget (&view
->widget
, y
, x
, lines
, cols
,
2796 (callback_fn
) view_callback
,
2797 (mouse_h
) real_view_event
);
2799 view
->hex_mode
= default_hex_mode
;
2800 view
->hexedit_mode
= default_hexedit_mode
;
2801 view
->viewer_magic_flag
= default_magic_flag
;
2802 view
->viewer_nroff_flag
= default_nroff_flag
;
2803 view
->have_frame
= is_panel
;
2804 view
->wrap_mode
= global_wrap_mode
;
2806 widget_want_cursor (view
->widget
, 0);