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
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
26 #include <sys/types.h>
33 # include <sys/mman.h>
35 #include <ctype.h> /* For toupper() */
41 #include "cmd.h" /* For view_other_cmd */
42 #include "dlg.h" /* Needed by widget.h */
43 #include "widget.h" /* Needed for buttonbar_new */
48 #include "key.h" /* For mi_getch() */
51 #include "wtools.h" /* For query_set_sel() */
52 #include "../vfs/vfs.h"
54 #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
72 # define IFAIX(x) case (x):
77 #define vwidth (view->widget.cols - (view->have_frame ? 2 : 0))
78 #define vheight (view->widget.lines - (view->have_frame ? 2 : 0))
80 /* The growing buffers data types */
81 typedef struct block_ptr_t
{
85 /* A node for building a change list on change_list */
86 struct hexedit_change_node
{
87 struct hexedit_change_node
*next
;
100 char *filename
; /* Name of the file */
101 char *command
; /* Command used to pipe data in */
106 unsigned char *data
; /* Memory area for the file to be viewed */
108 /* File information */
109 int file
; /* File descriptor (for mmap and munmap) */
110 FILE *stdfile
; /* Stdio struct for reading file in parts */
111 int reading_pipe
; /* Flag: Reading from pipe(use popen/pclose) */
112 unsigned long bytes_read
; /* How much of file is read */
113 int mmapping
; /* Did we use mmap on the file? */
115 /* Display information */
116 unsigned long last
; /* Last byte shown */
117 unsigned long last_byte
; /* Last byte of file */
118 long first
; /* First byte in file */
119 long bottom_first
; /* First byte shown when very last page is displayed */
120 /* For the case of WINCH we should reset it to -1 */
121 unsigned long start_display
;/* First char displayed */
122 int start_col
; /* First displayed column, negative */
123 unsigned long edit_cursor
; /* HexEdit cursor position in file */
124 char hexedit_mode
; /* Hexidecimal editing mode flag */
125 char nib_shift
; /* A flag for inserting nibbles into bytes */
126 enum ViewSide view_side
; /* A flag for the active editing panel */
127 int file_dirty
; /* Number of changes */
128 int start_save
; /* Line start shift between Ascii and Hex */
129 int cursor_col
; /* Cursor column */
130 int cursor_row
; /* Cursor row */
131 struct hexedit_change_node
*change_list
; /* Linked list of changes */
133 int dirty
; /* Number of skipped updates */
134 int wrap_mode
; /* wrap_mode */
137 int hex_mode
; /* Hexadecimal mode flag */
138 int bytes_per_line
; /* Number of bytes per line in hex mode */
139 int viewer_magic_flag
; /* Selected viewer */
140 int viewer_nroff_flag
; /* Do we do nroff style highlighting? */
142 /* Growing buffers information */
143 int growing_buffer
; /* Use the growing buffers? */
144 struct block_ptr_t
*block_ptr
; /* Pointer to the block pointers */
145 int blocks
; /* The number of blocks in *block_ptr */
148 /* Search variables */
149 int search_start
; /* First character to start searching from */
150 int found_len
; /* Length of found string or 0 if none was found */
151 char *search_exp
; /* The search expression */
152 int direction
; /* 1= forward; -1 backward */
153 void (*last_search
)(void *, char *);
154 /* Pointer to the last search command */
155 int view_quit
; /* Quit flag */
157 int monitor
; /* Monitor file growth (like tail -f) */
159 int marker
; /* mark to use */
160 int marks
[10]; /* 10 marks: 0..9 */
163 int move_dir
; /* return value from widget:
165 * -1 view previous file
168 struct stat s
; /* stat for file */
172 /* Maxlimit for skipping updates */
180 extern Hook
*idle_hook
;
182 /* If set, show a ruler */
183 static int ruler
= 0;
185 /* Scrolling is done in pages or line increments */
186 int mouse_move_pages_viewer
= 1;
188 /* Used to compute the bottom first variable */
189 int have_fast_cpu
= 0;
191 /* wrap mode default */
192 int global_wrap_mode
= 1;
194 int default_hex_mode
= 0;
195 static int default_hexedit_mode
= 0;
196 int default_magic_flag
= 1;
197 int default_nroff_flag
= 1;
198 int altered_hex_mode
= 0;
199 int altered_magic_flag
= 0;
200 int altered_nroff_flag
= 0;
202 static const char hex_char
[] = "0123456789ABCDEF";
205 static int view_callback (WView
*view
, int msg
, int par
);
207 static int regexp_view_search (WView
* view
, char *pattern
, char *string
,
209 static void view_move_forward (WView
* view
, int i
);
210 static void view_labels (WView
* view
);
211 static void set_monitor (WView
* view
, int set_on
);
212 static void view_update (WView
* view
, gboolean update_gui
);
216 close_view_file (WView
*view
)
218 if (view
->file
!= -1) {
219 mc_close (view
->file
);
225 free_file (WView
*view
)
230 if (view
->mmapping
) {
231 mc_munmap (view
->data
, view
->s
.st_size
);
232 close_view_file (view
);
234 #endif /* HAVE_MMAP */
236 if (view
->reading_pipe
) {
237 /* Check error messages */
238 if (!view
->have_frame
)
242 pclose (view
->stdfile
);
243 view
->stdfile
= NULL
;
245 /* Ignore errors because we don't want to hear about broken pipe */
246 close_error_pipe (-1, NULL
);
248 close_view_file (view
);
250 /* Block_ptr may be zero if the file was a file with 0 bytes */
251 if (view
->growing_buffer
&& view
->block_ptr
) {
252 for (i
= 0; i
< view
->blocks
; i
++) {
253 g_free (view
->block_ptr
[i
].data
);
255 g_free (view
->block_ptr
);
259 /* Valid parameters for second parameter to set_monitor */
264 view_done (WView
*view
)
266 set_monitor (view
, off
);
268 /* alex: release core, used to replace mmap */
269 if (!view
->mmapping
&& !view
->growing_buffer
&& view
->data
!= NULL
) {
274 if (view
->view_active
) {
276 mc_ungetlocalcopy (view
->filename
, view
->localcopy
, 0);
278 g_free (view
->filename
);
280 g_free (view
->command
);
282 view
->view_active
= 0;
283 default_hex_mode
= view
->hex_mode
;
284 default_nroff_flag
= view
->viewer_nroff_flag
;
285 default_magic_flag
= view
->viewer_magic_flag
;
286 global_wrap_mode
= view
->wrap_mode
;
289 static void view_hook (void *);
292 view_destroy (WView
*view
)
295 if (view
->have_frame
)
296 delete_hook (&select_file_hook
, view_hook
);
300 get_byte (WView
*view
, unsigned int byte_index
)
302 int page
= byte_index
/ VIEW_PAGE_SIZE
+ 1;
303 int offset
= byte_index
% VIEW_PAGE_SIZE
;
306 if (view
->growing_buffer
) {
307 if (page
> view
->blocks
) {
308 view
->block_ptr
= g_realloc (view
->block_ptr
,
309 sizeof (block_ptr_t
) * page
);
310 for (i
= view
->blocks
; i
< page
; i
++) {
311 char *p
= g_malloc (VIEW_PAGE_SIZE
);
312 view
->block_ptr
[i
].data
= p
;
315 if (view
->stdfile
!= NULL
)
316 n
= fread (p
, 1, VIEW_PAGE_SIZE
, view
->stdfile
);
318 n
= mc_read (view
->file
, p
, VIEW_PAGE_SIZE
);
320 * FIXME: Errors are ignored at this point
321 * Also should report preliminary EOF
324 view
->bytes_read
+= n
;
325 if (view
->s
.st_size
< view
->bytes_read
) {
326 view
->bottom_first
= -1; /* Invalidate cache */
327 view
->s
.st_size
= view
->bytes_read
;
328 view
->last_byte
= view
->bytes_read
;
329 if (view
->reading_pipe
)
330 view
->last_byte
= view
->first
+ view
->bytes_read
;
332 /* To force loading the next page */
333 if (n
== VIEW_PAGE_SIZE
&& view
->reading_pipe
) {
339 if (byte_index
> view
->bytes_read
) {
342 return view
->block_ptr
[page
- 1].data
[offset
];
344 if (byte_index
>= view
->last_byte
)
347 return view
->data
[byte_index
];
352 enqueue_change (struct hexedit_change_node
**head
,
353 struct hexedit_change_node
*node
)
355 struct hexedit_change_node
*curr
= *head
;
358 if (node
->offset
< curr
->offset
) {
363 head
= (struct hexedit_change_node
**) curr
;
370 static void move_right (WView
*);
373 put_editkey (WView
*view
, unsigned char key
)
375 struct hexedit_change_node
*node
;
376 unsigned char byte_val
;
378 if (!view
->hexedit_mode
|| view
->growing_buffer
!= 0)
381 /* Has there been a change at this position ? */
382 node
= view
->change_list
;
383 while (node
&& (node
->offset
!= view
->edit_cursor
)) {
387 if (view
->view_side
== view_side_left
) {
390 if (key
>= '0' && key
<= '9')
392 else if (key
>= 'A' && key
<= 'F')
394 else if (key
>= 'a' && key
<= 'f')
400 byte_val
= node
->value
;
402 byte_val
= get_byte (view
, view
->edit_cursor
);
404 if (view
->nib_shift
== 0) {
405 byte_val
= (byte_val
& 0x0f) | (key
<< 4);
407 byte_val
= (byte_val
& 0xf0) | (key
);
414 node
= (struct hexedit_change_node
*)
415 g_new (struct hexedit_change_node
, 1);
419 /* alex@bcs.zaporizhzhe.ua: here we are using file copy
420 * completely loaded into memory, so we can replace bytes in
421 * view->data array to allow changes to be reflected when
422 * user switches back to ascii mode */
423 view
->data
[view
->edit_cursor
] = byte_val
;
424 #endif /* !HAVE_MMAP */
425 node
->offset
= view
->edit_cursor
;
426 node
->value
= byte_val
;
427 enqueue_change (&view
->change_list
, node
);
430 node
->value
= byte_val
;
433 view_update (view
, TRUE
);
438 free_change_list (WView
*view
)
440 struct hexedit_change_node
*n
= view
->change_list
;
443 view
->change_list
= n
->next
;
445 n
= view
->change_list
;
447 view
->file_dirty
= 0;
452 save_edit_changes (WView
*view
)
454 struct hexedit_change_node
*node
= view
->change_list
;
458 fp
= open (view
->filename
, O_WRONLY
);
461 if (lseek (fp
, node
->offset
, SEEK_SET
) == -1 ||
462 write (fp
, &node
->value
, 1) != 1) {
474 fp
= query_dialog (_(" Save file "),
475 _(" Cannot save file. "),
476 2, 2, _("&Retry"), _("&Cancel")) - 1;
480 free_change_list (view
);
484 view_ok_to_quit (WView
*view
)
488 if (!view
->change_list
)
491 r
= query_dialog (_("Quit"),
492 _(" File was modified, Save with exit? "), 2, 3,
493 _("Cancel quit"), _("&Yes"), _("&No"));
497 save_edit_changes (view
);
500 free_change_list (view
);
508 set_view_init_error (WView
*view
, const char *msg
)
510 view
->growing_buffer
= 0;
511 view
->reading_pipe
= 0;
515 view
->bytes_read
= strlen (msg
);
516 return g_strdup (msg
);
521 /* return values: NULL for success, else points to error message */
523 init_growing_view (WView
*view
, char *name
, char *filename
)
525 char *err_msg
= NULL
;
527 view
->growing_buffer
= 1;
530 view
->reading_pipe
= 1;
534 if ((view
->stdfile
= popen (name
, "r")) == NULL
) {
535 /* Avoid two messages. Message from stderr has priority. */
536 if (!close_error_pipe (view
->have_frame
? -1 : 1, view
->data
))
537 err_msg
= _(" Cannot spawn child program ");
538 return set_view_init_error (view
, err_msg
);
541 /* First, check if filter produced any output */
543 if (view
->bytes_read
<= 0) {
544 pclose (view
->stdfile
);
545 view
->stdfile
= NULL
;
546 /* Avoid two messages. Message from stderr has priority. */
547 if (!close_error_pipe (view
->have_frame
? -1 : 1, view
->data
))
548 err_msg
= _("Empty output from child filter");
549 return set_view_init_error (view
, err_msg
);
552 view
->stdfile
= NULL
;
553 if ((view
->file
= mc_open (filename
, O_RDONLY
)) == -1)
554 return set_view_init_error (view
, _(" Cannot open file "));
559 /* Load filename into core */
562 if (have_frame), we return success, but data points to a
563 error message instead of the file buffer (quick_view feature).
566 load_view_file (WView
*view
, int fd
)
570 if (view
->s
.st_size
== 0) {
571 /* Must be one of those nice files that grow (/proc) */
572 close_view_file (view
);
573 return init_growing_view (view
, 0, view
->filename
);
577 mc_mmap (0, view
->s
.st_size
, PROT_READ
, MAP_FILE
| MAP_SHARED
,
579 if ((caddr_t
) view
->data
!= (caddr_t
) - 1) {
582 view
->bytes_read
= view
->s
.st_size
;
586 #endif /* HAVE_MMAP */
588 /* For the OSes that don't provide mmap call, try to load all the
589 * file into memory (alex@bcs.zaporizhzhe.ua). Also, mmap can fail
590 * for any reason, so we use this as fallback (pavel@ucw.cz) */
592 /* Make sure view->s.st_size is not truncated when passed to g_malloc */
593 if ((gulong
) view
->s
.st_size
== view
->s
.st_size
)
594 view
->data
= (unsigned char *) g_malloc ((gulong
) view
->s
.st_size
);
598 if (view
->data
== NULL
|| mc_lseek (view
->file
, 0, SEEK_SET
) != 0
599 || mc_read (view
->file
, view
->data
,
600 view
->s
.st_size
) != view
->s
.st_size
) {
602 close_view_file (view
);
603 return init_growing_view (view
, 0, view
->filename
);
606 view
->bytes_read
= view
->s
.st_size
;
610 /* Return zero on success, -1 on failure */
612 do_view_init (WView
*view
, char *_command
, const char *_file
,
618 char tmp
[BUF_MEDIUM
];
620 if (view
->view_active
)
623 /* Set up the state */
626 view
->growing_buffer
= 0;
627 view
->reading_pipe
= 0;
631 view
->first
= view
->bytes_read
= 0;
633 view
->filename
= g_strdup (_file
);
636 view
->last
= view
->first
+ ((LINES
- 2) * view
->bytes_per_line
);
638 /* Clear the markers */
640 for (i
= 0; i
< 10; i
++)
643 if (!view
->have_frame
) {
647 if (_command
&& (view
->viewer_magic_flag
|| _file
[0] == '\0')) {
648 error
= init_growing_view (view
, _command
, view
->filename
);
649 } else if (_file
[0]) {
653 if ((fd
= mc_open (_file
, O_RDONLY
| O_NONBLOCK
)) == -1) {
654 g_snprintf (tmp
, sizeof (tmp
), _(" Cannot open \"%s\"\n %s "),
655 _file
, unix_error_string (errno
));
656 error
= set_view_init_error (view
, tmp
);
660 /* Make sure we are working with a regular file */
661 if (mc_fstat (fd
, &view
->s
) == -1) {
663 g_snprintf (tmp
, sizeof (tmp
), _(" Cannot stat \"%s\"\n %s "),
664 _file
, unix_error_string (errno
));
665 error
= set_view_init_error (view
, tmp
);
669 if (!S_ISREG (view
->s
.st_mode
)) {
671 g_snprintf (tmp
, sizeof (tmp
),
672 _(" Cannot view: not a regular file "));
673 error
= set_view_init_error (view
, tmp
);
677 /* We don't need O_NONBLOCK after opening the file, unset it */
678 cntlflags
= fcntl (fd
, F_GETFL
, 0);
679 if (cntlflags
!= -1) {
680 cntlflags
&= ~O_NONBLOCK
;
681 fcntl (fd
, F_SETFL
, cntlflags
);
684 type
= get_compression_type (fd
);
686 if (view
->viewer_magic_flag
&& (type
!= COMPRESSION_NONE
)) {
687 g_free (view
->filename
);
689 g_strconcat (_file
, decompress_extension (type
), NULL
);
692 error
= load_view_file (view
, fd
);
697 if (!view
->have_frame
) {
698 message (1, MSG_ERROR
, "%s", error
);
704 view
->view_active
= 1;
706 view
->command
= g_strdup (_command
);
709 view
->search_start
= view
->start_display
= view
->start_save
=
713 view
->last_search
= 0; /* Start a new search */
715 /* Special case: The data points to the error message */
719 view
->s
.st_size
= view
->bytes_read
= strlen (view
->data
);
721 view
->last_byte
= view
->first
+ view
->s
.st_size
;
723 if (start_line
> 1 && !error
) {
724 int saved_wrap_mode
= view
->wrap_mode
;
728 view_move_forward (view
, start_line
- 1);
729 view
->wrap_mode
= saved_wrap_mode
;
731 view
->edit_cursor
= view
->first
;
732 view
->file_dirty
= 0;
734 view
->view_side
= view_side_left
;
735 view
->change_list
= NULL
;
741 view_update_bytes_per_line (WView
*view
)
745 if (view
->have_frame
)
746 cols
= view
->widget
.cols
- 2;
748 cols
= view
->widget
.cols
;
750 view
->bottom_first
= -1;
752 view
->bytes_per_line
= ((cols
- 8) / 17) * 4;
754 view
->bytes_per_line
= ((cols
- 8) / 18) * 4;
756 if (view
->bytes_per_line
== 0)
757 view
->bytes_per_line
++; /* To avoid division by 0 */
759 view
->dirty
= max_dirt_limit
+ 1; /* To force refresh */
763 /* Return zero on success, -1 on failure */
765 view_init (WView
*view
, char *_command
, const char *_file
, int start_line
)
767 if (!view
->view_active
|| strcmp (_file
, view
->filename
)
768 || altered_magic_flag
)
769 return do_view_init (view
, _command
, _file
, start_line
);
775 view_percent (WView
*view
, int p
, int w
, gboolean update_gui
)
779 percent
= (view
->s
.st_size
== 0
780 || view
->last_byte
== view
->last
) ? 100 : (p
>
789 percent
= view
->s
.st_size
== 0 ? 100 :
790 (view
->last_byte
== view
->last
? 100 :
791 (p
) * 100 / view
->s
.st_size
);
794 widget_move (view
, view
->have_frame
, w
- 5);
795 printw ("%3d%%", percent
);
799 view_status (WView
*view
, gboolean update_gui
)
801 static int i18n_adjust
= 0;
802 static char *file_label
;
804 int w
= view
->widget
.cols
- (view
->have_frame
* 2);
807 attrset (SELECTED_COLOR
);
808 widget_move (view
, view
->have_frame
, view
->have_frame
);
812 file_label
= _("File: %s");
813 i18n_adjust
= strlen (file_label
) - 2;
816 if (w
< i18n_adjust
+ 6)
817 addstr (name_trunc (view
->filename
? view
->filename
:
818 view
->command
? view
->command
: "", w
));
820 i
= (w
> 22 ? 22 : w
) - i18n_adjust
;
821 printw (file_label
, name_trunc (view
->filename
? view
->filename
:
822 view
->command
? view
->command
: "",
825 widget_move (view
, view
->have_frame
, 24 + view
->have_frame
);
827 printw (_("Offset 0x%08x"), view
->edit_cursor
);
829 printw (_("Col %d"), -view
->start_col
);
832 widget_move (view
, view
->have_frame
, 43 + view
->have_frame
);
833 printw (_("%s bytes"), size_trunc (view
->s
.st_size
));
837 if (view
->growing_buffer
)
838 addstr (_(" [grow]"));
842 view
->hex_mode
? view
->edit_cursor
: view
->
844 view
->widget
.cols
- view
->have_frame
+ 1,
848 attrset (SELECTED_COLOR
);
852 view_display_clean (WView
*view
, int height
, int width
)
854 /* FIXME: Should I use widget_erase only and repaint the box? */
855 if (view
->have_frame
) {
858 draw_double_box (view
->widget
.parent
, view
->widget
.y
,
859 view
->widget
.x
, view
->widget
.lines
,
861 for (i
= 1; i
< height
; i
++) {
862 widget_move (view
, i
, 1);
863 printw ("%*s", width
- 1, "");
866 widget_erase ((Widget
*) view
);
869 #define view_add_character(view,c) addch (c)
870 #define view_add_one_vline() one_vline()
871 #define view_add_string(view,s) addstr (s)
872 #define view_gotoyx(v,r,c) widget_move (v,r,c)
874 #define view_freeze(view)
875 #define view_thaw(view)
877 #define STATUS_LINES 1
886 /* Shows the file pointed to by *start_display on view_win */
888 display (WView
*view
)
890 const int frame_shift
= view
->have_frame
;
891 int col
= 0 + frame_shift
;
892 int row
= STATUS_LINES
+ frame_shift
;
896 mark_t boldflag
= MARK_NORMAL
;
897 struct hexedit_change_node
*curr
= view
->change_list
;
899 height
= view
->widget
.lines
- frame_shift
;
900 width
= view
->widget
.cols
- frame_shift
;
901 from
= view
->start_display
;
902 attrset (NORMAL_COLOR
);
905 view_display_clean (view
, height
, width
);
907 /* Optionally, display a ruler */
908 if ((!view
->hex_mode
) && (ruler
)) {
912 attrset (MARKED_COLOR
);
913 for (c
= frame_shift
; c
< width
; c
++) {
914 cl
= c
- view
->start_col
;
916 view_gotoyx (view
, row
, c
);
918 view_gotoyx (view
, row
+ height
- 2, c
);
922 else if ((cl
% 5) == 0)
924 view_add_character (view
, r_buff
[0]);
925 if ((cl
!= 0) && (cl
% 10) == 0) {
926 g_snprintf (r_buff
, sizeof (r_buff
), "%03d", cl
);
928 widget_move (view
, row
+ 1, c
- 1);
930 widget_move (view
, row
+ height
- 3, c
- 1);
932 view_add_string (view
, r_buff
);
935 attrset (NORMAL_COLOR
);
942 /* Find the first displayable changed byte */
943 while (curr
&& (curr
->offset
< from
)) {
946 if (view
->hex_mode
) {
947 char hex_buff
[10]; /* A temporary buffer for sprintf and mvwaddstr */
948 int bytes
; /* Number of bytes already printed on the line */
950 /* Start of text column */
951 int text_start
= width
- view
->bytes_per_line
- 1 + frame_shift
;
953 for (; row
< height
&& from
< view
->last_byte
; row
++) {
954 /* Print the hex offset */
955 attrset (MARKED_COLOR
);
956 g_snprintf (hex_buff
, sizeof (hex_buff
), "%08X",
957 (int) (from
- view
->first
));
958 view_gotoyx (view
, row
, frame_shift
);
959 view_add_string (view
, hex_buff
);
960 attrset (NORMAL_COLOR
);
962 /* Hex dump starts from column nine */
963 if (view
->have_frame
)
968 /* Each hex number is two digits */
971 bytes
< view
->bytes_per_line
&& from
< view
->last_byte
;
973 /* Display and mark changed bytes */
974 if (curr
&& from
== curr
->offset
) {
977 boldflag
= MARK_CHANGED
;
978 attrset (VIEW_UNDERLINED_COLOR
);
980 c
= (unsigned char) get_byte (view
, from
);
982 if (view
->found_len
&& from
>= view
->search_start
983 && from
< view
->search_start
+ view
->found_len
) {
984 boldflag
= MARK_SELECTED
;
985 attrset (MARKED_COLOR
);
987 /* Display the navigation cursor */
988 if (from
== view
->edit_cursor
) {
989 if (view
->view_side
== view_side_left
) {
990 view
->cursor_row
= row
;
991 view
->cursor_col
= col
;
993 boldflag
= MARK_CURSOR
;
994 attrset (view
->view_side
==
995 view_side_left
? VIEW_UNDERLINED_COLOR
:
996 MARKED_SELECTED_COLOR
);
999 /* Print a hex number (sprintf is too slow) */
1000 hex_buff
[0] = hex_char
[(c
>> 4)];
1001 hex_buff
[1] = hex_char
[c
& 15];
1002 view_gotoyx (view
, row
, col
);
1003 view_add_string (view
, hex_buff
);
1005 /* Turn off the cursor or changed byte highlighting here */
1006 if (boldflag
== MARK_CURSOR
|| boldflag
== MARK_CHANGED
)
1007 attrset (NORMAL_COLOR
);
1008 if ((bytes
& 3) == 3 && bytes
+ 1 < view
->bytes_per_line
) {
1009 /* Turn off the search highlighting */
1010 if (boldflag
== MARK_SELECTED
1012 view
->search_start
+ view
->found_len
- 1)
1013 attrset (NORMAL_COLOR
);
1015 /* Hex numbers are printed in the groups of four */
1016 /* Groups are separated by a vline */
1018 view_gotoyx (view
, row
, col
- 1);
1019 view_add_character (view
, ' ');
1020 view_gotoyx (view
, row
, col
);
1021 if ((view
->have_frame
&& view
->widget
.cols
< 82) ||
1022 view
->widget
.cols
< 80)
1025 view_add_one_vline ();
1029 if (boldflag
!= MARK_NORMAL
1031 view
->search_start
+ view
->found_len
- 1)
1032 attrset (MARKED_COLOR
);
1035 if (boldflag
!= MARK_NORMAL
1036 && from
< view
->search_start
+ view
->found_len
- 1
1037 && bytes
!= view
->bytes_per_line
- 1) {
1038 view_gotoyx (view
, row
, col
);
1039 view_add_character (view
, ' ');
1042 /* Print the corresponding ascii character */
1043 view_gotoyx (view
, row
, text_start
+ bytes
);
1045 c
= convert_to_display_c (c
);
1047 if (!is_printable (c
))
1053 attrset (MARKED_COLOR
);
1056 if (view
->view_side
== view_side_right
) {
1057 /* Our side is active */
1058 view
->cursor_col
= text_start
+ bytes
;
1059 view
->cursor_row
= row
;
1060 attrset (VIEW_UNDERLINED_COLOR
);
1062 /* Other side is active */
1063 attrset (MARKED_SELECTED_COLOR
);
1067 attrset (VIEW_UNDERLINED_COLOR
);
1070 view_add_character (view
, c
);
1072 if (boldflag
!= MARK_NORMAL
) {
1073 boldflag
= MARK_NORMAL
;
1074 attrset (NORMAL_COLOR
);
1079 if (view
->growing_buffer
&& from
== view
->last_byte
)
1080 get_byte (view
, from
);
1081 for (; row
< height
&& from
< view
->last_byte
; from
++) {
1082 c
= get_byte (view
, from
);
1083 if ((c
== '\n') || (col
>= width
&& view
->wrap_mode
)) {
1086 if (c
== '\n' || row
>= height
)
1092 col
= ((col
- frame_shift
) / 8) * 8 + 8 + frame_shift
;
1095 if (view
->viewer_nroff_flag
&& c
== '\b') {
1099 if (from
+ 1 < view
->last_byte
1100 && is_printable ((c_next
= get_byte (view
, from
+ 1)))
1101 && from
> view
->first
1102 && is_printable ((c_prev
= get_byte (view
, from
- 1)))
1103 && (c_prev
== c_next
|| c_prev
== '_')) {
1104 if (col
<= frame_shift
) {
1105 /* So it has to be wrap_mode - do not need to check for it */
1106 if (row
== 1 + frame_shift
) {
1108 continue; /* There had to be a bold character on the rightmost position
1109 of the previous undisplayed line */
1115 boldflag
= MARK_SELECTED
;
1116 if (c_prev
== '_' && c_next
!= '_')
1117 attrset (VIEW_UNDERLINED_COLOR
);
1119 attrset (MARKED_COLOR
);
1123 if (view
->found_len
&& from
>= view
->search_start
1124 && from
< view
->search_start
+ view
->found_len
) {
1125 boldflag
= MARK_SELECTED
;
1126 attrset (SELECTED_COLOR
);
1128 if (col
>= frame_shift
- view
->start_col
1129 && col
< width
- view
->start_col
) {
1130 view_gotoyx (view
, row
, col
+ view
->start_col
);
1132 c
= convert_to_display_c (c
);
1134 if (!is_printable (c
))
1137 view_add_character (view
, c
);
1140 if (boldflag
!= MARK_NORMAL
) {
1141 boldflag
= MARK_NORMAL
;
1142 attrset (NORMAL_COLOR
);
1145 /* Very last thing */
1146 if (view
->growing_buffer
&& from
+ 1 == view
->last_byte
)
1147 get_byte (view
, from
+ 1);
1156 view_place_cursor (WView
*view
)
1160 if (view
->view_side
== view_side_left
)
1161 shift
= view
->nib_shift
;
1165 widget_move (&view
->widget
, view
->cursor_row
,
1166 view
->cursor_col
+ shift
);
1170 view_update (WView
*view
, gboolean update_gui
)
1172 static int dirt_limit
= 1;
1174 if (view
->dirty
> dirt_limit
) {
1175 /* Too many updates skipped -> force a update */
1177 view_status (view
, update_gui
);
1179 /* Raise the update skipping limit */
1181 if (dirt_limit
> max_dirt_limit
)
1182 dirt_limit
= max_dirt_limit
;
1186 /* We have time to update the screen properly */
1188 view_status (view
, update_gui
);
1193 /* We are busy -> skipping full update,
1194 only the status line is updated */
1195 view_status (view
, update_gui
);
1197 /* Here we had a refresh, if fast scrolling does not work
1198 restore the refresh, although this should not happen */
1203 my_define (Dlg_head
*h
, int idx
, char *text
, void (*fn
) (WView
*),
1206 define_label_data (h
, idx
, text
, (buttonbarfn
) fn
, view
);
1209 /* If the last parameter is nonzero, it means we want get the count of lines
1210 from current up to the the upto position inclusive */
1212 move_forward2 (WView
*view
, long current
, int lines
, long upto
)
1218 if (view
->hex_mode
) {
1219 p
= current
+ lines
* view
->bytes_per_line
;
1220 p
= (p
>= view
->last_byte
) ? current
: p
;
1222 q
= view
->edit_cursor
+ view
->bytes_per_line
;
1223 line
= q
/ view
->bytes_per_line
;
1224 col
= (view
->last_byte
- 1) / view
->bytes_per_line
;
1225 view
->edit_cursor
= (line
> col
) ? view
->edit_cursor
: q
;
1226 view
->edit_cursor
= (view
->edit_cursor
< view
->last_byte
) ?
1227 view
->edit_cursor
: view
->last_byte
- 1;
1228 q
= current
+ ((LINES
- 2) * view
->bytes_per_line
);
1229 p
= (view
->edit_cursor
< q
) ? current
: p
;
1231 view
->edit_cursor
= (view
->edit_cursor
< p
) ?
1232 p
: view
->edit_cursor
;
1240 q
= view
->last_byte
;
1241 if (get_byte (view
, q
) != '\n')
1243 for (line
= col
= 0, p
= current
; p
< q
; p
++) {
1246 if (lines
!= -1 && line
>= lines
)
1249 c
= get_byte (view
, p
);
1251 if (view
->wrap_mode
) {
1253 continue; /* This characters is never displayed */
1256 ((col
- view
->have_frame
) / 8) * 8 + 8 +
1260 if (view
->viewer_nroff_flag
&& c
== '\b') {
1261 if (p
+ 1 < view
->last_byte
1262 && is_printable (get_byte (view
, p
+ 1))
1264 && is_printable (get_byte (view
, p
- 1)))
1266 } else if (col
== vwidth
) {
1267 /* FIXME: the c in is_printable was a p, that is a bug,
1268 I suspect I got that fix from Jakub, same applies
1270 int d
= get_byte (view
, p
+ 2);
1272 if (p
+ 2 >= view
->last_byte
|| !is_printable (c
) ||
1273 !view
->viewer_nroff_flag
1274 || get_byte (view
, p
+ 1) != '\b'
1275 || !is_printable (d
)) {
1278 if (c
== '\n' || get_byte (view
, p
+ 1) != '\n')
1281 } else if (c
== '\n') {
1285 } else if (c
== '\n')
1294 /* returns the new current pointer */
1295 /* Cause even the forward routine became very complex, we in the wrap_mode
1296 just find the nearest '\n', use move_forward2(p, 0, q) to get the count
1297 of lines up to there and then use move_forward2(p, something, 0), which we
1300 move_backward2 (WView
*view
, unsigned long current
, int lines
)
1305 if (!view
->hex_mode
&& current
== view
->first
)
1308 if (view
->hex_mode
) {
1309 p
= current
- lines
* view
->bytes_per_line
;
1310 p
= (p
< view
->first
) ? view
->first
: p
;
1312 q
= view
->edit_cursor
- view
->bytes_per_line
;
1313 view
->edit_cursor
= (q
< view
->first
) ? view
->edit_cursor
: q
;
1314 p
= (view
->edit_cursor
>= current
) ? current
: p
;
1316 q
= p
+ ((LINES
- 2) * view
->bytes_per_line
);
1317 view
->edit_cursor
= (view
->edit_cursor
>= q
) ?
1318 p
: view
->edit_cursor
;
1322 if (current
== view
->last_byte
1323 && get_byte (view
, current
- 1) != '\n')
1324 /* There is one virtual '\n' at the end,
1325 so that the last line is shown */
1329 for (q
= p
= current
- 1; p
>= view
->first
; p
--)
1330 if (get_byte (view
, p
) == '\n' || p
== view
->first
) {
1331 pm
= p
> view
->first
? p
+ 1 : view
->first
;
1332 if (!view
->wrap_mode
) {
1337 line
+= move_forward2 (view
, pm
, 0, q
);
1338 if (line
>= lines
) {
1342 return move_forward2 (view
, pm
, line
- lines
,
1349 return p
> view
->first
? p
: view
->first
;
1353 view_move_backward (WView
*view
, int i
)
1355 view
->search_start
= view
->start_display
=
1356 move_backward2 (view
, view
->start_display
, i
);
1357 view
->found_len
= 0;
1358 view
->last
= view
->first
+ ((LINES
- 2) * view
->bytes_per_line
);
1363 get_bottom_first (WView
*view
, int do_not_cache
, int really
)
1367 if (!have_fast_cpu
&& !really
)
1370 if (!do_not_cache
&& view
->bottom_first
!= -1)
1371 return view
->bottom_first
;
1374 if (view
->growing_buffer
) {
1378 while (old_last_byte
!= view
->last_byte
) {
1379 old_last_byte
= view
->last_byte
;
1380 get_byte (view
, view
->last_byte
+ VIEW_PAGE_SIZE
);
1384 bottom_first
= move_backward2 (view
, view
->last_byte
, vheight
- 1);
1387 bottom_first
= (bottom_first
+ view
->bytes_per_line
- 1)
1388 / view
->bytes_per_line
* view
->bytes_per_line
;
1389 view
->bottom_first
= bottom_first
;
1391 return view
->bottom_first
;
1395 view_move_forward (WView
*view
, int i
)
1397 view
->start_display
= move_forward2 (view
, view
->start_display
, i
, 0);
1398 if (!view
->reading_pipe
1399 && view
->start_display
> get_bottom_first (view
, 0, 0))
1400 view
->start_display
= view
->bottom_first
;
1401 view
->search_start
= view
->start_display
;
1402 view
->found_len
= 0;
1403 view
->last
= view
->first
+ ((LINES
- 2) * view
->bytes_per_line
);
1409 move_to_top (WView
*view
)
1411 view
->search_start
= view
->start_display
= view
->first
;
1412 view
->found_len
= 0;
1413 view
->last
= view
->first
+ ((LINES
- 2) * view
->bytes_per_line
);
1414 view
->nib_shift
= 0;
1415 view
->edit_cursor
= view
->start_display
;
1420 move_to_bottom (WView
*view
)
1422 view
->search_start
= view
->start_display
=
1423 get_bottom_first (view
, 0, 1);
1424 view
->found_len
= 0;
1425 view
->last
= view
->first
+ ((LINES
- 2) * view
->bytes_per_line
);
1426 view
->edit_cursor
= (view
->edit_cursor
< view
->start_display
) ?
1427 view
->start_display
: view
->edit_cursor
;
1431 /* Scroll left/right the view panel functions */
1433 move_right (WView
*view
)
1435 if (view
->wrap_mode
&& !view
->hex_mode
)
1437 if (view
->hex_mode
) {
1438 view
->last
= view
->first
+ ((LINES
- 2) * view
->bytes_per_line
);
1440 if (view
->hex_mode
&& view
->view_side
== view_side_left
) {
1441 view
->nib_shift
= 1 - view
->nib_shift
;
1442 if (view
->nib_shift
== 1)
1445 view
->edit_cursor
= (++view
->edit_cursor
< view
->last_byte
) ?
1446 view
->edit_cursor
: view
->last_byte
- 1;
1447 if (view
->edit_cursor
>= view
->last
) {
1448 view
->edit_cursor
-= view
->bytes_per_line
;
1449 view_move_forward (view
, 1);
1451 } else if (--view
->start_col
> 0)
1452 view
->start_col
= 0;
1457 move_left (WView
*view
)
1459 if (view
->wrap_mode
&& !view
->hex_mode
)
1461 if (view
->hex_mode
) {
1462 if (view
->hex_mode
&& view
->view_side
== view_side_left
) {
1463 view
->nib_shift
= 1 - view
->nib_shift
;
1464 if (view
->nib_shift
== 0)
1467 if (view
->edit_cursor
> view
->first
)
1468 --view
->edit_cursor
;
1469 if (view
->edit_cursor
< view
->start_display
) {
1470 view
->edit_cursor
+= view
->bytes_per_line
;
1471 view_move_backward (view
, 1);
1473 } else if (++view
->start_col
> 0)
1474 view
->start_col
= 0;
1478 /* Case insensitive search of text in data */
1480 icase_search_p (WView
*view
, char *text
, char *data
, int nothing
)
1484 int direction
= view
->direction
;
1486 /* If we are searching backwards, reverse the string */
1487 if (direction
== -1) {
1488 g_strreverse (text
);
1489 g_strreverse (data
);
1492 q
= _icase_search (text
, data
, &lng
);
1494 if (direction
== -1) {
1495 g_strreverse (text
);
1496 g_strreverse (data
);
1501 view
->search_start
= q
- data
- lng
;
1503 view
->search_start
= strlen (data
) - (q
- data
);
1504 view
->found_len
= lng
;
1511 grow_string_buffer (char *text
, int *size
)
1515 /* The grow steps */
1517 new = g_realloc (text
, *size
);
1525 get_line_at (WView
*view
, unsigned long *p
, unsigned long *skipped
)
1528 int buffer_size
= 0;
1529 int usable_size
= 0;
1531 int direction
= view
->direction
;
1532 unsigned long pos
= *p
;
1536 if (!pos
&& direction
== -1)
1539 /* skip over all the possible zeros in the file */
1540 while ((ch
= get_byte (view
, pos
)) == 0) {
1541 if (!pos
&& direction
== -1)
1548 if (!i
&& (pos
|| direction
== -1)) {
1549 prev
= get_byte (view
, pos
- direction
);
1550 if ((prev
== -1) || (prev
== '\n'))
1554 for (i
= 1; ch
!= -1; ch
= get_byte (view
, pos
)) {
1556 if (i
>= usable_size
) {
1557 buffer
= grow_string_buffer (buffer
, &buffer_size
);
1558 usable_size
= buffer_size
- 2; /* prev & null terminator */
1563 if (!pos
&& direction
== -1)
1568 if (ch
== '\n' || !ch
) {
1569 i
--; /* Strip newline/zero */
1578 /* If we are searching backwards, reverse the string */
1579 if (direction
< 0) {
1580 g_strreverse (buffer
+ 1);
1588 /** Search status optmizations **/
1590 /* The number of bytes between percent increments */
1591 static int update_steps
;
1593 /* Last point where we updated the status */
1594 static long update_activate
;
1597 search_update_steps (WView
*view
)
1599 if (view
->s
.st_size
)
1600 update_steps
= 40000;
1602 update_steps
= view
->last_byte
/ 100;
1604 /* Do not update the percent display but every 20 ks */
1605 if (update_steps
< 20000)
1606 update_steps
= 20000;
1610 search (WView
*view
, char *text
,
1611 int (*search
) (WView
*, char *, char *, int))
1613 int w
= view
->widget
.cols
- view
->have_frame
+ 1;
1615 char *s
= NULL
; /* The line we read from the view buffer */
1617 int found_len
, search_start
;
1621 /* Used to keep track of where the line starts, when looking forward */
1622 /* is the index before transfering the line; the reverse case uses */
1623 /* the position returned after the line has been read */
1624 long forward_line_start
;
1625 long reverse_line_start
;
1627 /* Clear interrupt status */
1631 d
= message (D_INSERT
, _("Search"), _("Searching %s"), text
);
1635 found_len
= view
->found_len
;
1636 search_start
= view
->search_start
;
1638 if (view
->direction
== 1) {
1639 p
= found_len
? search_start
+ 1 : search_start
;
1641 p
= (found_len
&& search_start
) ? search_start
- 1 : search_start
;
1645 /* Compute the percent steps */
1646 search_update_steps (view
);
1647 update_activate
= 0;
1649 for (;; g_free (s
)) {
1650 if (p
>= update_activate
) {
1651 update_activate
+= update_steps
;
1653 view_percent (view
, p
, w
, TRUE
);
1656 if (got_interrupt ())
1659 forward_line_start
= p
;
1660 disable_interrupt_key ();
1661 s
= get_line_at (view
, &p
, &t
);
1662 reverse_line_start
= p
;
1663 enable_interrupt_key ();
1668 search_status
= (*search
) (view
, text
, s
+ 1, match_normal
);
1669 if (search_status
< 0) {
1674 if (search_status
== 0)
1677 /* We found the string */
1679 /* Handle ^ and $ when regexp search starts at the middle of the line */
1680 if (*s
&& !view
->search_start
&& (search
== regexp_view_search
)) {
1681 if ((*text
== '^' && view
->direction
== 1)
1682 || (view
->direction
== -1 && text
[strlen (text
) - 1] == '$')
1687 /* Record the position used to continue the search */
1688 if (view
->direction
== 1)
1689 t
+= forward_line_start
;
1691 t
= reverse_line_start
? reverse_line_start
+ 3 : 0;
1692 view
->search_start
+= t
;
1694 if (t
!= beginning
) {
1695 if (t
> get_bottom_first (view
, 0, 0))
1696 view
->start_display
= view
->bottom_first
;
1698 view
->start_display
= t
;
1704 disable_interrupt_key ();
1710 message (0, _("Search"), _(" Search string not found "));
1711 view
->found_len
= 0;
1715 /* Search buffer (it's size is len) in the complete buffer */
1716 /* returns the position where the block was found or -1 if not found */
1718 block_search (WView
*view
, char *buffer
, int len
)
1720 int w
= view
->widget
.cols
- view
->have_frame
+ 1;
1721 int direction
= view
->direction
;
1722 char *d
= buffer
, b
;
1725 /* clear interrupt status */
1727 enable_interrupt_key ();
1729 e
= view
->found_len
? view
->search_start
+ 1 : view
->search_start
;
1731 e
= (view
->found_len
1732 && view
->search_start
) ? view
->search_start
- 1
1733 : view
->search_start
;
1735 search_update_steps (view
);
1736 update_activate
= 0;
1738 if (direction
== -1) {
1739 for (d
+= len
- 1;; e
--) {
1740 if (e
<= update_activate
) {
1741 update_activate
-= update_steps
;
1743 view_percent (view
, e
, w
, TRUE
);
1746 if (got_interrupt ())
1749 b
= get_byte (view
, e
);
1753 disable_interrupt_key ();
1758 e
+= buffer
+ len
- 1 - d
;
1759 d
= buffer
+ len
- 1;
1765 while (e
< view
->last_byte
) {
1766 if (e
>= update_activate
) {
1767 update_activate
+= update_steps
;
1769 view_percent (view
, e
, w
, TRUE
);
1772 if (got_interrupt ())
1775 b
= get_byte (view
, e
++);
1779 if (d
- buffer
== len
) {
1780 disable_interrupt_key ();
1789 disable_interrupt_key ();
1794 * Search in the hex mode. Supported input:
1795 * - numbers (oct, dec, hex). Each of them matches one byte.
1796 * - strings in double quotes. Matches exactly without quotes.
1799 hex_search (WView
*view
, char *text
)
1801 char *buffer
; /* Parsed search string */
1802 char *cur
; /* Current position in it */
1803 int block_len
; /* Length of the search string */
1804 long pos
; /* Position of the string in the file */
1805 int parse_error
= 0;
1808 view
->found_len
= 0;
1812 /* buffer will never be longer that text */
1813 buffer
= g_new (char, strlen (text
));
1816 /* First convert the string to a stream of bytes */
1821 /* Skip leading spaces */
1822 if (*text
== ' ' || *text
== '\t') {
1827 /* %i matches octal, decimal, and hexadecimal numbers */
1828 if (sscanf (text
, "%i%n", &val
, &ptr
) > 0) {
1829 /* Allow signed and unsigned char in the user input */
1830 if (val
< -128 || val
> 255) {
1835 *cur
++ = (char) val
;
1840 /* Try quoted string, strip quotes */
1845 next_quote
= strchr (text
, '"');
1847 memcpy (cur
, text
, next_quote
- text
);
1848 cur
+= next_quote
- text
;
1849 text
= next_quote
+ 1;
1859 block_len
= cur
- buffer
;
1861 /* No valid bytes in the user input */
1862 if (block_len
<= 0 || parse_error
) {
1863 message (0, _("Search"), _("Invalid hex search expression"));
1865 view
->found_len
= 0;
1869 /* Then start the search */
1870 pos
= block_search (view
, buffer
, block_len
);
1875 message (0, _("Search"), _(" Search string not found "));
1876 view
->found_len
= 0;
1880 view
->search_start
= pos
;
1881 view
->found_len
= block_len
;
1882 /* Set the edit cursor to the search position, left nibble */
1883 view
->edit_cursor
= view
->search_start
;
1884 view
->nib_shift
= 0;
1886 /* Adjust the file offset */
1887 view
->start_display
= (pos
& (~(view
->bytes_per_line
- 1)));
1888 if (view
->start_display
> get_bottom_first (view
, 0, 0))
1889 view
->start_display
= view
->bottom_first
;
1893 regexp_view_search (WView
*view
, char *pattern
, char *string
,
1897 static char *old_pattern
= NULL
;
1898 static int old_type
;
1899 regmatch_t pmatch
[1];
1900 int i
, flags
= REG_ICASE
;
1902 if (!old_pattern
|| strcmp (old_pattern
, pattern
)
1903 || old_type
!= match_type
) {
1906 g_free (old_pattern
);
1909 for (i
= 0; pattern
[i
] != 0; i
++) {
1910 if (isupper ((unsigned char) pattern
[i
])) {
1915 flags
|= REG_EXTENDED
;
1916 if (regcomp (&r
, pattern
, flags
)) {
1917 message (1, MSG_ERROR
, _(" Invalid regular expression "));
1920 old_pattern
= g_strdup (pattern
);
1921 old_type
= match_type
;
1923 if (regexec (&r
, string
, 1, pmatch
, 0) != 0)
1925 view
->found_len
= pmatch
[0].rm_eo
- pmatch
[0].rm_so
;
1926 view
->search_start
= pmatch
[0].rm_so
;
1931 do_regexp_search (void *xview
, char *regexp
)
1933 WView
*view
= (WView
*) xview
;
1935 view
->search_exp
= regexp
;
1936 search (view
, regexp
, regexp_view_search
);
1937 /* Had a refresh here */
1939 view_update (view
, TRUE
);
1943 do_normal_search (void *xview
, char *text
)
1945 WView
*view
= (WView
*) xview
;
1947 view
->search_exp
= text
;
1949 hex_search (view
, text
);
1951 search (view
, text
, icase_search_p
);
1952 /* Had a refresh here */
1954 view_update (view
, TRUE
);
1957 /* Real view only */
1959 view_help_cmd (void)
1961 interactive_display (NULL
, "[Internal File Viewer]");
1969 toggle_wrap_mode (WView
*view
)
1971 if (view
->hex_mode
) {
1972 if (view
->growing_buffer
!= 0) {
1975 get_bottom_first (view
, 1, 1);
1976 if (view
->hexedit_mode
) {
1977 view
->view_side
= 1 - view
->view_side
;
1979 view
->hexedit_mode
= 1 - view
->hexedit_mode
;
1983 view_update (view
, TRUE
);
1986 view
->wrap_mode
= 1 - view
->wrap_mode
;
1987 get_bottom_first (view
, 1, 1);
1988 if (view
->wrap_mode
)
1989 view
->start_col
= 0;
1991 if (have_fast_cpu
) {
1992 if (view
->bottom_first
< view
->start_display
)
1993 view
->search_start
= view
->start_display
=
1995 view
->found_len
= 0;
2000 view_update (view
, TRUE
);
2005 toggle_hex_mode (WView
*view
)
2007 view
->hex_mode
= 1 - view
->hex_mode
;
2009 if (view
->hex_mode
) {
2010 /* Shift the line start to 0x____0 on entry, restore it for Ascii */
2011 view
->start_save
= view
->start_display
;
2012 view
->start_display
-= view
->start_display
% view
->bytes_per_line
;
2013 view
->edit_cursor
= view
->start_display
;
2014 view
->widget
.options
|= W_WANT_CURSOR
;
2015 view
->widget
.parent
->flags
|= DLG_WANT_TAB
;
2017 view
->start_display
= view
->start_save
;
2018 view
->widget
.parent
->flags
&= ~DLG_WANT_TAB
;
2019 view
->widget
.options
&= ~W_WANT_CURSOR
;
2021 altered_hex_mode
= 1;
2022 get_bottom_first (view
, 1, 1);
2025 view_update (view
, TRUE
);
2030 goto_line (WView
*view
)
2032 char *line
, prompt
[BUF_SMALL
];
2034 int saved_wrap_mode
= view
->wrap_mode
;
2037 view
->wrap_mode
= 0;
2038 for (i
= view
->first
; i
< view
->start_display
; i
++)
2039 if (get_byte (view
, i
) == '\n')
2041 g_snprintf (prompt
, sizeof (prompt
),
2042 _(" The current line number is %d.\n"
2043 " Enter the new line number:"), oldline
);
2044 line
= input_dialog (_(" Goto line "), prompt
, "");
2048 view_move_forward (view
, atol (line
) - 1);
2053 view
->wrap_mode
= saved_wrap_mode
;
2054 view_update (view
, TRUE
);
2059 goto_addr (WView
*view
)
2061 char *line
, *error
, prompt
[BUF_SMALL
];
2064 g_snprintf (prompt
, sizeof (prompt
),
2065 _(" The current address is 0x%lx.\n"
2066 " Enter the new address:"), view
->edit_cursor
);
2067 line
= input_dialog (_(" Goto Address "), prompt
, "");
2070 addr
= strtol (line
, &error
, 0);
2071 if ((*error
== '\0') && (addr
<= view
->last_byte
)) {
2073 view_move_forward (view
, addr
/ view
->bytes_per_line
);
2074 view
->edit_cursor
= addr
;
2080 view_update (view
, TRUE
);
2085 regexp_search (WView
*view
, int direction
)
2088 static char *old
= 0;
2090 /* This is really an F6 key handler */
2091 if (view
->hex_mode
) {
2092 /* Save it without a confirmation prompt */
2093 if (view
->change_list
)
2094 save_edit_changes (view
);
2098 regexp
= old
? old
: regexp
;
2099 regexp
= input_dialog (_("Search"), _(" Enter regexp:"), regexp
);
2111 /* Mhm, do we really need to load all the file in the core? */
2112 if (view
->bytes_read
< view
->last_byte
)
2113 get_byte (view
, view
->last_byte
- 1); /* Get the whole file in to memory */
2115 view
->direction
= direction
;
2116 do_regexp_search (view
, regexp
);
2118 view
->last_search
= do_regexp_search
;
2122 regexp_search_cmd (WView
*view
)
2124 regexp_search (view
, 1);
2129 normal_search (WView
*view
, int direction
)
2132 char *exp
= old
? old
: "";
2135 SEARCH_DLG_HEIGHT
= 8,
2136 SEARCH_DLG_WIDTH
= 58
2139 static int replace_backwards
;
2140 int treplace_backwards
= replace_backwards
;
2142 static QuickWidget quick_widgets
[] = {
2143 {quick_button
, 6, 10, 5, SEARCH_DLG_HEIGHT
, N_("&Cancel"), 0,
2146 {quick_button
, 2, 10, 5, SEARCH_DLG_HEIGHT
, N_("&OK"), 0, B_ENTER
,
2148 {quick_checkbox
, 3, SEARCH_DLG_WIDTH
, 4, SEARCH_DLG_HEIGHT
,
2149 N_("&Backwards"), 0, 0,
2151 {quick_input
, 3, SEARCH_DLG_WIDTH
, 3, SEARCH_DLG_HEIGHT
, "", 52, 0,
2152 0, 0, N_("Search")},
2153 {quick_label
, 2, SEARCH_DLG_WIDTH
, 2, SEARCH_DLG_HEIGHT
,
2154 N_(" Enter search string:"), 0, 0,
2158 static QuickDialog Quick_input
= {
2159 SEARCH_DLG_WIDTH
, SEARCH_DLG_HEIGHT
, -1, 0, N_("Search"),
2160 "[Input Line Keys]", quick_widgets
, 0
2162 convert_to_display (old
);
2164 quick_widgets
[2].result
= &treplace_backwards
;
2165 quick_widgets
[3].str_result
= &exp
;
2166 quick_widgets
[3].text
= exp
;
2168 if (quick_dialog (&Quick_input
) == B_CANCEL
) {
2169 convert_from_input (old
);
2172 replace_backwards
= treplace_backwards
;
2174 convert_from_input (old
);
2188 convert_from_input (exp
);
2190 view
->direction
= replace_backwards
? -1 : 1;
2191 do_normal_search (view
, exp
);
2192 view
->last_search
= do_normal_search
;
2196 normal_search_cmd (WView
*view
)
2198 normal_search (view
, 1);
2202 change_viewer (WView
*view
)
2208 if (*view
->filename
) {
2209 altered_magic_flag
= 1;
2210 view
->viewer_magic_flag
= !view
->viewer_magic_flag
;
2211 s
= g_strdup (view
->filename
);
2213 t
= g_strdup (view
->command
);
2218 view_init (view
, t
, s
, 0);
2224 view_update (view
, TRUE
);
2229 change_nroff (WView
*view
)
2231 view
->viewer_nroff_flag
= !view
->viewer_nroff_flag
;
2232 altered_nroff_flag
= 1;
2235 view_update (view
, TRUE
);
2238 /* Real view only */
2240 view_quit_cmd (WView
*view
)
2242 if (view_ok_to_quit (view
))
2243 dlg_stop (view
->widget
.parent
);
2248 view_labels (WView
*view
)
2250 Dlg_head
*h
= view
->widget
.parent
;
2252 define_label (h
, 1, _("Help"), view_help_cmd
);
2254 my_define (h
, 10, _("Quit"), view_quit_cmd
, view
);
2255 my_define (h
, 4, view
->hex_mode
? _("Ascii") : _("Hex"),
2256 toggle_hex_mode
, view
);
2257 my_define (h
, 5, view
->hex_mode
? _("Goto") : _("Line"),
2258 view
->hex_mode
? goto_addr
: goto_line
, view
);
2259 my_define (h
, 6, view
->hex_mode
? _("Save") : _("RxSrch"),
2260 regexp_search_cmd
, view
);
2262 my_define (h
, 2, view
->hex_mode
? view
->hexedit_mode
?
2264 view_side_left
? _("EdText") : _("EdHex") : view
->
2265 growing_buffer
? "" : _("Edit") : view
->
2266 wrap_mode
? _("UnWrap") : _("Wrap"), toggle_wrap_mode
,
2269 my_define (h
, 7, view
->hex_mode
? _("HxSrch") : _("Search"),
2270 normal_search_cmd
, view
);
2272 my_define (h
, 8, view
->viewer_magic_flag
? _("Raw") : _("Parse"),
2273 change_viewer
, view
);
2275 if (!view
->have_frame
) {
2277 view
->viewer_nroff_flag
? _("Unform") : _("Format"),
2278 change_nroff
, view
);
2279 my_define (h
, 3, _("Quit"), view_quit_cmd
, view
);
2287 check_left_right_keys (WView
*view
, int c
)
2291 else if (c
== KEY_RIGHT
)
2300 set_monitor (WView
*view
, int set_on
)
2302 int old
= view
->monitor
;
2304 view
->monitor
= set_on
;
2306 if (view
->monitor
) {
2307 move_to_bottom (view
);
2308 view
->bottom_first
= -1;
2309 set_idle_proc (view
->widget
.parent
, 1);
2312 set_idle_proc (view
->widget
.parent
, 0);
2317 continue_search (WView
*view
)
2319 if (view
->last_search
) {
2320 (*view
->last_search
) (view
, view
->search_exp
);
2322 /* if not... then ask for an expression */
2323 normal_search (view
, 1);
2329 view_handle_key (WView
*view
, int c
)
2331 int prev_monitor
= view
->monitor
;
2333 set_monitor (view
, off
);
2335 c
= convert_from_input_c (c
);
2337 if (view
->hex_mode
) {
2339 case 0x09: /* Tab key */
2340 view
->view_side
= 1 - view
->view_side
;
2344 case XCTRL ('a'): /* Beginning of line */
2345 view
->edit_cursor
-= view
->edit_cursor
% view
->bytes_per_line
;
2349 case XCTRL ('b'): /* Character back */
2353 case XCTRL ('e'): /* End of line */
2354 view
->edit_cursor
-= view
->edit_cursor
% view
->bytes_per_line
;
2355 view
->edit_cursor
+= view
->bytes_per_line
- 1;
2359 case XCTRL ('f'): /* Character forward */
2364 /* Trap 0-9,A-F,a-f for left side data entry (hex editing) */
2365 if (view
->view_side
== view_side_left
) {
2366 if ((c
>= '0' && c
<= '9') ||
2367 (c
>= 'A' && c
<= 'F') || (c
>= 'a' && c
<= 'f')) {
2369 put_editkey (view
, c
);
2374 /* Trap all printable characters for right side data entry */
2375 /* Also enter the value of the Enter key */
2376 if (view
->view_side
== view_side_right
) {
2377 if (c
< 256 && (is_printable (c
) || (c
== '\n'))) {
2378 put_editkey (view
, c
);
2384 if (check_left_right_keys (view
, c
))
2387 if (check_movement_keys
2388 (c
, 1, vheight
, view
, (movefn
) view_move_backward
,
2389 (movefn
) view_move_forward
, (movefn
) move_to_top
,
2390 (movefn
) move_to_bottom
)) {
2396 regexp_search (view
, -1);
2400 regexp_search (view
, 1);
2403 /* Continue search */
2407 continue_search (view
);
2411 if (view
->last_search
) {
2412 (*view
->last_search
) (view
, view
->search_exp
);
2414 normal_search (view
, -1);
2441 view_move_forward (view
, 1);
2445 view_move_forward (view
, vheight
/ 2);
2449 view_move_backward (view
, vheight
/ 2);
2454 view_move_backward (view
, 1);
2463 view_move_forward (view
, vheight
- 1);
2470 /* Unlike Ctrl-O, run a new shell if the subshell is not running. */
2476 set_monitor (view
, on
);
2480 view_move_backward (view
, vheight
- 1);
2484 view_move_backward (view
, 2);
2488 view_move_forward (view
, 2);
2492 view
->marks
[view
->marker
] = view
->start_display
;
2496 view
->start_display
= view
->marks
[view
->marker
];
2500 /* Use to indicate parent that we want to see the next/previous file */
2501 /* Only works on full screen mode */
2504 if (!view
->have_frame
)
2505 view
->move_dir
= c
== XCTRL ('f') ? 1 : -1;
2511 if (view_ok_to_quit (view
))
2512 view
->view_quit
= 1;
2517 do_select_codepage ();
2519 view_update (view
, TRUE
);
2521 #endif /* HAVE_CHARSET */
2524 if (c
>= '0' && c
<= '9')
2525 view
->marker
= c
- '0';
2527 /* Restore the monitor status */
2528 set_monitor (view
, prev_monitor
);
2536 view_event (WView
*view
, Gpm_Event
*event
, int *result
)
2538 *result
= MOU_NORMAL
;
2540 /* We are not interested in the release events */
2541 if (!(event
->type
& (GPM_DOWN
| GPM_DRAG
)))
2545 if ((event
->buttons
& GPM_B_UP
) && (event
->type
& GPM_DOWN
)) {
2546 view_move_backward (view
, 2);
2549 if ((event
->buttons
& GPM_B_DOWN
) && (event
->type
& GPM_DOWN
)) {
2550 view_move_forward (view
, 2);
2554 /* Scrolling left and right */
2555 if (!view
->wrap_mode
) {
2556 if (event
->x
< view
->widget
.cols
/ 4) {
2560 if (event
->x
> 3 * vwidth
/ 4) {
2566 /* Scrolling up and down */
2567 if (event
->y
< view
->widget
.lines
/ 3) {
2568 if (mouse_move_pages_viewer
)
2569 view_move_backward (view
, view
->widget
.lines
/ 2 - 1);
2571 view_move_backward (view
, 1);
2573 } else if (event
->y
> 2 * vheight
/ 3) {
2574 if (mouse_move_pages_viewer
)
2575 view_move_forward (view
, vheight
/ 2 - 1);
2577 view_move_forward (view
, 1);
2584 *result
= MOU_REPEAT
;
2588 /* Real view only */
2590 real_view_event (Gpm_Event
*event
, void *x
)
2594 if (view_event ((WView
*) x
, event
, &result
))
2595 view_update ((WView
*) x
, TRUE
);
2600 view_adjust_size (Dlg_head
*h
)
2605 /* Look up the viewer and the buttonbar, we assume only two widgets here */
2606 view
= (WView
*) find_widget_type (h
, (callback_fn
) view_callback
);
2607 bar
= find_buttonbar (h
);
2608 widget_set_size (&view
->widget
, 0, 0, LINES
- 1, COLS
);
2609 widget_set_size (&bar
->widget
, LINES
- 1, 0, 1, COLS
);
2611 view_update_bytes_per_line (view
);
2614 /* Callback for the view dialog */
2616 view_dialog_callback (Dlg_head
*h
, int id
, int msg
)
2620 view_adjust_size (h
);
2623 return default_dlg_callback (h
, id
, msg
);
2626 /* Real view only */
2628 view (char *_command
, const char *_file
, int *move_dir_p
, int start_line
)
2635 /* Create dialog and widgets, put them on the dialog */
2637 create_dlg (0, 0, LINES
, COLS
, NULL
, view_dialog_callback
,
2638 "[Internal File Viewer]", NULL
, DLG_NONE
);
2640 wview
= view_new (0, 0, COLS
, LINES
- 1, 0);
2642 bar
= buttonbar_new (1);
2644 add_widget (view_dlg
, wview
);
2645 add_widget (view_dlg
, bar
);
2647 error
= view_init (wview
, _command
, _file
, start_line
);
2651 /* Please note that if you add another widget,
2652 * you have to modify view_adjust_size to
2658 *move_dir_p
= wview
->move_dir
;
2660 destroy_dlg (view_dlg
);
2668 WView
*view
= (WView
*) v
;
2671 /* If the user is busy typing, wait until he finishes to update the
2674 if (!hook_present (idle_hook
, view_hook
))
2675 add_hook (&idle_hook
, view_hook
, v
);
2679 delete_hook (&idle_hook
, view_hook
);
2681 if (get_current_type () == view_listing
)
2683 else if (get_other_type () == view_listing
)
2684 panel
= other_panel
;
2688 view_init (view
, 0, panel
->dir
.list
[panel
->selected
].fname
, 0);
2690 view_status (view
, TRUE
);
2694 view_callback (WView
*view
, int msg
, int par
)
2697 Dlg_head
*h
= view
->widget
.parent
;
2701 view_update_bytes_per_line (view
);
2702 if (view
->have_frame
)
2703 add_hook (&select_file_hook
, view_hook
, view
);
2710 view_status (view
, TRUE
);
2715 view_place_cursor (view
);
2719 i
= view_handle_key ((WView
*) view
, par
);
2720 if (view
->view_quit
)
2723 view_update (view
, TRUE
);
2728 /* This event is generated when the user is using the 'F' flag */
2729 view
->bottom_first
= -1;
2730 move_to_bottom (view
);
2732 view_status (view
, TRUE
);
2741 return default_proc (msg
, par
);
2745 view_new (int y
, int x
, int cols
, int lines
, int is_panel
)
2747 WView
*view
= g_new0 (WView
, 1);
2749 init_widget (&view
->widget
, y
, x
, lines
, cols
,
2750 (callback_fn
) view_callback
,
2751 (destroy_fn
) view_destroy
,
2752 (mouse_h
) real_view_event
, NULL
);
2754 view
->hex_mode
= default_hex_mode
;
2755 view
->hexedit_mode
= default_hexedit_mode
;
2756 view
->viewer_magic_flag
= default_magic_flag
;
2757 view
->viewer_nroff_flag
= default_nroff_flag
;
2758 view
->have_frame
= is_panel
;
2759 view
->last_byte
= -1;
2760 view
->wrap_mode
= global_wrap_mode
;
2762 widget_want_cursor (view
->widget
, 0);