2 Internal file viewer for the Midnight Commander
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
7 Written by: 1994, 1995, 1998 Miguel de Icaza
8 1994, 1995 Janne Kukonlehto
13 2004 Roland Illig <roland.illig@gmx.de>
14 2005 Roland Illig <roland.illig@gmx.de>
15 2009 Slava Zanko <slavazanko@google.com>
16 2009 Ilia Maslakov <il.smind@gmail.com>
18 This program is free software; you can redistribute it and/or modify
19 it under the terms of the GNU General Public License as published by
20 the Free Software Foundation; either version 2 of the License, or
21 (at your option) any later version.
23 This program is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program; if not, write to the Free Software
30 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
34 * \brief Source: internal file viewer
49 #include <sys/types.h>
55 #include "../src/tty/tty.h"
56 #include "../src/tty/color.h"
57 #include "../src/tty/key.h"
58 #include "../src/tty/mouse.h"
60 #include "cmd.h" /* For view_other_cmd */
61 #include "dialog.h" /* Needed by widget.h */
62 #include "widget.h" /* Needed for buttonbar_new */
66 #include "wtools.h" /* For query_set_sel() */
68 #include "panel.h" /* Needed for current_panel and other_panel */
70 #include "main.h" /* source_codepage */
72 #include "history.h" /* MC_HISTORY_SHARED_SEARCH */
74 #include "selcodepage.h"
76 #include "../src/search/search.h"
78 /* Block size for reading files in parts */
79 #define VIEW_PAGE_SIZE ((size_t) 8192)
80 #define VIEW_COORD_CACHE_GRANUL 1024
82 typedef unsigned char byte
;
84 /* Offset in bytes into a file */
85 typedef unsigned long offset_type
;
86 #define INVALID_OFFSET ((offset_type) -1)
87 #define OFFSETTYPE_MAX (~((offset_type) 0))
88 #define OFFSETTYPE_PRIX "lX"
89 #define OFFSETTYPE_PRId "lu"
91 /* A width or height on the screen */
92 typedef unsigned int screen_dimen
;
94 /* A cache entry for mapping offsets into line/column pairs and vice versa.
95 * cc_offset, cc_line, and cc_column are the 0-based values of the offset,
96 * line and column of that cache entry. cc_nroff_column is the column
97 * corresponding to cc_offset in nroff mode.
99 struct coord_cache_entry
{
100 offset_type cc_offset
;
102 offset_type cc_column
;
103 offset_type cc_nroff_column
;
106 /* A node for building a change list on change_list */
107 struct hexedit_change_node
{
108 struct hexedit_change_node
*next
;
113 /* data sources of the view */
115 DS_NONE
, /* No data available */
116 DS_STDIO_PIPE
, /* Data comes from a pipe using popen/pclose */
117 DS_VFS_PIPE
, /* Data comes from a piped-in VFS file */
118 DS_FILE
, /* Data comes from a VFS file */
119 DS_STRING
/* Data comes from a string in memory */
123 screen_dimen top
, left
;
124 screen_dimen height
, width
;
130 char *filename
; /* Name of the file */
131 char *command
; /* Command used to pipe data in */
133 enum view_ds datasource
; /* Where the displayed data comes from */
135 /* stdio pipe data source */
136 FILE *ds_stdio_pipe
; /* Output of a shell command */
138 /* vfs pipe data source */
139 int ds_vfs_pipe
; /* Non-seekable vfs file descriptor */
141 /* vfs file data source */
142 int ds_file_fd
; /* File with random access */
143 off_t ds_file_filesize
; /* Size of the file */
144 off_t ds_file_offset
; /* Offset of the currently loaded data */
145 byte
*ds_file_data
; /* Currently loaded data */
146 size_t ds_file_datalen
; /* Number of valid bytes in file_data */
147 size_t ds_file_datasize
; /* Number of allocated bytes in file_data */
149 /* string data source */
150 byte
*ds_string_data
; /* The characters of the string */
151 size_t ds_string_len
; /* The length of the string */
153 /* Growing buffers information */
154 gboolean growbuf_in_use
; /* Use the growing buffers? */
155 byte
**growbuf_blockptr
; /* Pointer to the block pointers */
156 size_t growbuf_blocks
; /* The number of blocks in *block_ptr */
157 size_t growbuf_lastindex
; /* Number of bytes in the last page of the
159 gboolean growbuf_finished
; /* TRUE when all data has been read. */
163 gboolean hex_mode
; /* Hexview or Hexedit */
164 gboolean hexedit_mode
; /* Hexedit */
165 gboolean hexview_in_text
; /* Is the hexview cursor in the text area? */
166 gboolean text_nroff_mode
; /* Nroff-style highlighting */
167 gboolean text_wrap_mode
; /* Wrap text lines to fit them on the screen */
168 gboolean magic_mode
; /* Preprocess the file using external programs */
169 gboolean utf8
; /* It's multibyte file codeset */
171 /* Additional editor state */
172 gboolean hexedit_lownibble
; /* Are we editing the last significant nibble? */
173 GArray
*coord_cache
; /* Cache for mapping offsets to cursor positions */
175 /* Display information */
176 screen_dimen dpy_frame_size
;/* Size of the frame surrounding the real viewer */
177 offset_type dpy_start
; /* Offset of the displayed data */
178 offset_type dpy_end
; /* Offset after the displayed data */
179 offset_type dpy_text_column
;/* Number of skipped columns in non-wrap
181 offset_type hex_cursor
; /* Hexview cursor position in file */
182 screen_dimen cursor_col
; /* Cursor column */
183 screen_dimen cursor_row
; /* Cursor row */
184 struct hexedit_change_node
*change_list
; /* Linked list of changes */
185 struct area status_area
; /* Where the status line is displayed */
186 struct area ruler_area
; /* Where the ruler is displayed */
187 struct area data_area
; /* Where the data is displayed */
189 int dirty
; /* Number of skipped updates */
190 gboolean dpy_bbar_dirty
; /* Does the button bar need to be updated? */
193 int bytes_per_line
; /* Number of bytes per line in hex mode */
195 /* Search variables */
196 offset_type search_start
; /* First character to start searching from */
197 offset_type search_end
; /* Length of found string or 0 if none was found */
199 /* Pointer to the last search command */
200 gboolean want_to_quit
; /* Prepare for cleanup ... */
203 int marker
; /* mark to use */
204 offset_type marks
[10]; /* 10 marks: 0..9 */
206 int move_dir
; /* return value from widget:
208 * -1 view previous file
212 offset_type update_steps
; /* The number of bytes between percent
214 offset_type update_activate
;/* Last point where we updated the status */
216 /* converter for translation of text */
219 /* handle of search engine */
221 gchar
*last_search_string
;
222 mc_search_type_t search_type
;
223 gboolean search_all_codepages
;
224 gboolean search_case
;
225 gboolean search_backwards
;
227 int search_numNeedSkipChar
;
231 /* {{{ Global Variables }}} */
233 /* Maxlimit for skipping updates */
234 int max_dirt_limit
= 10;
236 /* If set, show a ruler */
237 static enum ruler_type
{
241 } ruler
= RULER_NONE
;
243 /* Scrolling is done in pages or line increments */
244 int mouse_move_pages_viewer
= 1;
246 /* wrap mode default */
247 int global_wrap_mode
= 1;
249 int default_hex_mode
= 0;
250 int default_magic_flag
= 1;
251 int default_nroff_flag
= 1;
252 int altered_hex_mode
= 0;
253 int altered_magic_flag
= 0;
254 int altered_nroff_flag
= 0;
256 static const char hex_char
[] = "0123456789ABCDEF";
258 int mcview_remember_file_position
= FALSE
;
260 /* {{{ Function Prototypes }}} */
262 /* Our widget callback */
263 static cb_ret_t
view_callback (Widget
*, widget_msg_t
, int);
265 static void view_labels (WView
* view
);
267 static void view_init_growbuf (WView
*);
268 static void view_place_cursor (WView
*view
);
269 static void display (WView
*);
270 static void view_done (WView
*);
272 /* {{{ Helper Functions }}} */
274 /* difference or zero */
275 static inline screen_dimen
276 dimen_doz (screen_dimen a
, screen_dimen b
)
278 return (a
>= b
) ? a
- b
: 0;
281 static inline screen_dimen
282 dimen_min (screen_dimen a
, screen_dimen b
)
284 return (a
< b
) ? a
: b
;
287 static inline offset_type
288 offset_doz (offset_type a
, offset_type b
)
290 return (a
>= b
) ? a
- b
: 0;
293 static inline offset_type
294 offset_rounddown (offset_type a
, offset_type b
)
300 /* {{{ Simple Primitive Functions for WView }}} */
302 static inline gboolean
303 view_is_in_panel (WView
*view
)
305 return (view
->dpy_frame_size
!= 0);
309 view_compute_areas (WView
*view
)
311 struct area view_area
;
312 screen_dimen height
, rest
, y
;
314 /* The viewer is surrounded by a frame of size view->dpy_frame_size.
315 * Inside that frame, there are: The status line (at the top),
316 * the data area and an optional ruler, which is shown above or
317 * below the data area. */
319 view_area
.top
= view
->dpy_frame_size
;
320 view_area
.left
= view
->dpy_frame_size
;
321 view_area
.height
= dimen_doz(view
->widget
.lines
, 2 * view
->dpy_frame_size
);
322 view_area
.width
= dimen_doz(view
->widget
.cols
, 2 * view
->dpy_frame_size
);
324 /* Most coordinates of the areas equal those of the whole viewer */
325 view
->status_area
= view_area
;
326 view
->ruler_area
= view_area
;
327 view
->data_area
= view_area
;
329 /* Compute the heights of the areas */
330 rest
= view_area
.height
;
332 height
= dimen_min(rest
, 1);
333 view
->status_area
.height
= height
;
336 height
= dimen_min(rest
, (ruler
== RULER_NONE
|| view
->hex_mode
) ? 0 : 2);
337 view
->ruler_area
.height
= height
;
340 view
->data_area
.height
= rest
;
342 /* Compute the position of the areas */
345 view
->status_area
.top
= y
;
346 y
+= view
->status_area
.height
;
348 if (ruler
== RULER_TOP
) {
349 view
->ruler_area
.top
= y
;
350 y
+= view
->ruler_area
.height
;
353 view
->data_area
.top
= y
;
354 y
+= view
->data_area
.height
;
356 if (ruler
== RULER_BOTTOM
) {
357 view
->ruler_area
.top
= y
;
358 y
+= view
->ruler_area
.height
;
363 view_hexedit_free_change_list (WView
*view
)
365 struct hexedit_change_node
*curr
, *next
;
367 for (curr
= view
->change_list
; curr
!= NULL
; curr
= next
) {
371 view
->change_list
= NULL
;
375 /* {{{ Growing buffer }}} */
378 view_init_growbuf (WView
*view
)
380 view
->growbuf_in_use
= TRUE
;
381 view
->growbuf_blockptr
= NULL
;
382 view
->growbuf_blocks
= 0;
383 view
->growbuf_lastindex
= VIEW_PAGE_SIZE
;
384 view
->growbuf_finished
= FALSE
;
388 view_growbuf_free (WView
*view
)
392 assert (view
->growbuf_in_use
);
394 for (i
= 0; i
< view
->growbuf_blocks
; i
++)
395 g_free (view
->growbuf_blockptr
[i
]);
396 g_free (view
->growbuf_blockptr
);
397 view
->growbuf_blockptr
= NULL
;
398 view
->growbuf_in_use
= FALSE
;
402 view_growbuf_filesize (WView
*view
)
404 assert(view
->growbuf_in_use
);
406 if (view
->growbuf_blocks
== 0)
409 return ((offset_type
) view
->growbuf_blocks
- 1) * VIEW_PAGE_SIZE
410 + view
->growbuf_lastindex
;
413 /* Copies the output from the pipe to the growing buffer, until either
414 * the end-of-pipe is reached or the interval [0..ofs) of the growing
415 * buffer is completely filled. */
417 view_growbuf_read_until (WView
*view
, offset_type ofs
)
424 assert (view
->growbuf_in_use
);
426 if (view
->growbuf_finished
)
430 while (view_growbuf_filesize (view
) < ofs
|| short_read
) {
431 if (view
->growbuf_lastindex
== VIEW_PAGE_SIZE
) {
432 /* Append a new block to the growing buffer */
433 byte
*newblock
= g_try_malloc (VIEW_PAGE_SIZE
);
434 byte
**newblocks
= g_try_malloc (sizeof (*newblocks
) * (view
->growbuf_blocks
+ 1));
435 if (!newblock
|| !newblocks
) {
440 memcpy (newblocks
, view
->growbuf_blockptr
, sizeof (*newblocks
) * view
->growbuf_blocks
);
441 g_free (view
->growbuf_blockptr
);
442 view
->growbuf_blockptr
= newblocks
;
443 view
->growbuf_blockptr
[view
->growbuf_blocks
++] = newblock
;
444 view
->growbuf_lastindex
= 0;
446 p
= view
->growbuf_blockptr
[view
->growbuf_blocks
- 1] + view
->growbuf_lastindex
;
447 bytesfree
= VIEW_PAGE_SIZE
- view
->growbuf_lastindex
;
449 if (view
->datasource
== DS_STDIO_PIPE
) {
450 nread
= fread (p
, 1, bytesfree
, view
->ds_stdio_pipe
);
452 view
->growbuf_finished
= TRUE
;
453 (void) pclose (view
->ds_stdio_pipe
);
455 close_error_pipe (D_NORMAL
, NULL
);
456 view
->ds_stdio_pipe
= NULL
;
460 assert (view
->datasource
== DS_VFS_PIPE
);
462 nread
= mc_read (view
->ds_vfs_pipe
, p
, bytesfree
);
463 } while (nread
== -1 && errno
== EINTR
);
464 if (nread
== -1 || nread
== 0) {
465 view
->growbuf_finished
= TRUE
;
466 (void) mc_close (view
->ds_vfs_pipe
);
467 view
->ds_vfs_pipe
= -1;
471 short_read
= ((size_t)nread
< bytesfree
);
472 view
->growbuf_lastindex
+= nread
;
477 get_byte_growing_buffer (WView
*view
, offset_type byte_index
)
479 offset_type pageno
= byte_index
/ VIEW_PAGE_SIZE
;
480 offset_type pageindex
= byte_index
% VIEW_PAGE_SIZE
;
482 assert (view
->growbuf_in_use
);
484 if ((size_t) pageno
!= pageno
)
487 view_growbuf_read_until (view
, byte_index
+ 1);
488 if (view
->growbuf_blocks
== 0)
490 if (pageno
< view
->growbuf_blocks
- 1)
491 return view
->growbuf_blockptr
[pageno
][pageindex
];
492 if (pageno
== view
->growbuf_blocks
- 1 && pageindex
< view
->growbuf_lastindex
)
493 return view
->growbuf_blockptr
[pageno
][pageindex
];
497 /* {{{ Data sources }}} */
500 The data source provides the viewer with data from either a file, a
501 string or the output of a command. The get_byte() function can be
502 used to get the value of a byte at a specific offset. If the offset
503 is out of range, -1 is returned. The function get_byte_indexed(a,b)
504 returns the byte at the offset a+b, or -1 if a+b is out of range.
506 The view_set_byte() function has the effect that later calls to
507 get_byte() will return the specified byte for this offset. This
508 function is designed only for use by the hexedit component after
509 saving its changes. Inspect the source before you want to use it for
512 The view_get_filesize() function returns the current size of the
513 data source. If the growing buffer is used, this size may increase
514 later on. Use the view_may_still_grow() function when you want to
515 know if the size can change later.
519 view_get_filesize (WView
*view
)
521 switch (view
->datasource
) {
526 return view_growbuf_filesize (view
);
528 return view
->ds_file_filesize
;
530 return view
->ds_string_len
;
532 assert(!"Unknown datasource type");
537 static inline gboolean
538 view_may_still_grow (WView
*view
)
540 return (view
->growbuf_in_use
&& !view
->growbuf_finished
);
543 /* returns TRUE if the idx lies in the half-open interval
544 * [offset; offset + size), FALSE otherwise.
546 static inline gboolean
547 already_loaded (offset_type offset
, offset_type idx
, size_t size
)
549 return (offset
<= idx
&& idx
- offset
< size
);
553 view_file_load_data (WView
*view
, offset_type byte_index
)
555 offset_type blockoffset
;
559 assert (view
->datasource
== DS_FILE
);
561 if (already_loaded (view
->ds_file_offset
, byte_index
, view
->ds_file_datalen
))
564 if (byte_index
>= view
->ds_file_filesize
)
567 blockoffset
= offset_rounddown (byte_index
, view
->ds_file_datasize
);
568 if (mc_lseek (view
->ds_file_fd
, blockoffset
, SEEK_SET
) == -1)
572 while (bytes_read
< view
->ds_file_datasize
) {
573 res
= mc_read (view
->ds_file_fd
, view
->ds_file_data
+ bytes_read
, view
->ds_file_datasize
- bytes_read
);
578 bytes_read
+= (size_t) res
;
580 view
->ds_file_offset
= blockoffset
;
581 if (bytes_read
> view
->ds_file_filesize
- view
->ds_file_offset
) {
582 /* the file has grown in the meantime -- stick to the old size */
583 view
->ds_file_datalen
= view
->ds_file_filesize
- view
->ds_file_offset
;
585 view
->ds_file_datalen
= bytes_read
;
590 view
->ds_file_datalen
= 0;
594 get_ptr_file (WView
*view
, offset_type byte_index
)
596 assert (view
->datasource
== DS_FILE
);
598 view_file_load_data (view
, byte_index
);
599 if (already_loaded(view
->ds_file_offset
, byte_index
, view
->ds_file_datalen
))
600 return (char *) (view
->ds_file_data
+ (byte_index
- view
->ds_file_offset
));
605 get_ptr_string (WView
*view
, offset_type byte_index
)
607 assert (view
->datasource
== DS_STRING
);
608 if (byte_index
< view
->ds_string_len
)
609 return (char *) (view
->ds_string_data
+ byte_index
);
614 get_ptr_growing_buffer (WView
*view
, offset_type byte_index
)
616 offset_type pageno
= byte_index
/ VIEW_PAGE_SIZE
;
617 offset_type pageindex
= byte_index
% VIEW_PAGE_SIZE
;
619 assert (view
->growbuf_in_use
);
621 if ((size_t) pageno
!= pageno
)
624 view_growbuf_read_until (view
, byte_index
+ 1);
625 if (view
->growbuf_blocks
== 0)
627 if (pageno
< view
->growbuf_blocks
- 1)
628 return (char *) (view
->growbuf_blockptr
[pageno
] + pageindex
);
629 if (pageno
== view
->growbuf_blocks
- 1 && pageindex
< view
->growbuf_lastindex
)
630 return (char *) (view
->growbuf_blockptr
[pageno
] + pageindex
);
635 get_utf (WView
*view
, offset_type byte_index
, int *char_width
)
640 gchar
*next_ch
= NULL
;
643 switch (view
->datasource
) {
646 str
= get_ptr_growing_buffer (view
, byte_index
);
649 str
= get_ptr_file (view
, byte_index
);
652 str
= get_ptr_string (view
, byte_index
);
663 res
= g_utf8_get_char_validated (str
, -1);
670 /* Calculate UTF-8 char width */
671 next_ch
= g_utf8_next_char(str
);
673 if ( next_ch
!= str
) {
674 width
= next_ch
- str
;
688 get_byte_string (WView
*view
, offset_type byte_index
)
690 assert (view
->datasource
== DS_STRING
);
691 if (byte_index
< view
->ds_string_len
)
692 return view
->ds_string_data
[byte_index
];
697 get_byte_none (WView
*view
, offset_type byte_index
)
699 assert (view
->datasource
== DS_NONE
);
706 get_byte_file (WView
*view
, offset_type byte_index
)
708 assert (view
->datasource
== DS_FILE
);
710 view_file_load_data (view
, byte_index
);
711 if (already_loaded(view
->ds_file_offset
, byte_index
, view
->ds_file_datalen
))
712 return view
->ds_file_data
[byte_index
- view
->ds_file_offset
];
717 get_byte (WView
*view
, offset_type offset
)
719 switch (view
->datasource
) {
722 return get_byte_growing_buffer (view
, offset
);
724 return get_byte_file (view
, offset
);
726 return get_byte_string (view
, offset
);
728 return get_byte_none (view
, offset
);
730 assert(!"Unknown datasource type");
735 get_byte_indexed (WView
*view
, offset_type base
, offset_type ofs
)
737 if (base
<= OFFSETTYPE_MAX
- ofs
)
738 return get_byte (view
, base
+ ofs
);
743 view_set_byte (WView
*view
, offset_type offset
, byte b
)
746 assert (offset
< view_get_filesize (view
));
747 assert (view
->datasource
== DS_FILE
);
748 view
->ds_file_datalen
= 0; /* just force reloading */
752 view_set_datasource_none (WView
*view
)
754 view
->datasource
= DS_NONE
;
758 view_set_datasource_vfs_pipe (WView
*view
, int fd
)
761 view
->datasource
= DS_VFS_PIPE
;
762 view
->ds_vfs_pipe
= fd
;
764 view_init_growbuf (view
);
768 view_set_datasource_stdio_pipe (WView
*view
, FILE *fp
)
771 view
->datasource
= DS_STDIO_PIPE
;
772 view
->ds_stdio_pipe
= fp
;
774 view_init_growbuf (view
);
778 view_set_datasource_string (WView
*view
, const char *s
)
780 view
->datasource
= DS_STRING
;
781 view
->ds_string_data
= (byte
*) g_strdup (s
);
782 view
->ds_string_len
= strlen (s
);
786 view_set_datasource_file (WView
*view
, int fd
, const struct stat
*st
)
788 view
->datasource
= DS_FILE
;
789 view
->ds_file_fd
= fd
;
790 view
->ds_file_filesize
= st
->st_size
;
791 view
->ds_file_offset
= 0;
792 view
->ds_file_data
= g_malloc (4096);
793 view
->ds_file_datalen
= 0;
794 view
->ds_file_datasize
= 4096;
798 view_close_datasource (WView
*view
)
800 switch (view
->datasource
) {
804 if (view
->ds_stdio_pipe
!= NULL
) {
805 (void) pclose (view
->ds_stdio_pipe
);
807 close_error_pipe (D_NORMAL
, NULL
);
808 view
->ds_stdio_pipe
= NULL
;
810 view_growbuf_free (view
);
813 if (view
->ds_vfs_pipe
!= -1) {
814 (void) mc_close (view
->ds_vfs_pipe
);
815 view
->ds_vfs_pipe
= -1;
817 view_growbuf_free (view
);
820 (void) mc_close (view
->ds_file_fd
);
821 view
->ds_file_fd
= -1;
822 g_free (view
->ds_file_data
);
823 view
->ds_file_data
= NULL
;
826 g_free (view
->ds_string_data
);
827 view
->ds_string_data
= NULL
;
830 assert (!"Unknown datasource type");
832 view
->datasource
= DS_NONE
;
835 /* {{{ The Coordinate Cache }}} */
838 This cache provides you with a fast lookup to map file offsets into
839 line/column pairs and vice versa. The interface to the mapping is
840 provided by the functions view_coord_to_offset() and
841 view_offset_to_coord().
843 The cache is implemented as a simple sorted array holding entries
844 that map some of the offsets to their line/column pair. Entries that
845 are not cached themselves are interpolated (exactly) from their
846 neighbor entries. The algorithm used for determining the line/column
847 for a specific offset needs to be kept synchronized with the one used
856 static inline gboolean
857 coord_cache_entry_less (const struct coord_cache_entry
*a
,
858 const struct coord_cache_entry
*b
, enum ccache_type crit
,
861 if (crit
== CCACHE_OFFSET
)
862 return (a
->cc_offset
< b
->cc_offset
);
864 if (a
->cc_line
< b
->cc_line
)
867 if (a
->cc_line
== b
->cc_line
) {
869 return (a
->cc_nroff_column
< b
->cc_nroff_column
);
871 return (a
->cc_column
< b
->cc_column
);
877 #ifdef MC_ENABLE_DEBUGGING_CODE
878 static void view_coord_to_offset (WView
*, offset_type
*, offset_type
, offset_type
);
879 static void view_offset_to_coord (WView
*, offset_type
*, offset_type
*, offset_type
);
882 view_ccache_dump (WView
*view
)
885 offset_type offset
, line
, column
, nextline_offset
, filesize
;
887 const struct coord_cache_entry
*cache
;
889 assert (view
->coord_cache
!= NULL
);
891 filesize
= view_get_filesize (view
);
892 cache
= &(g_array_index (view
->coord_cache
, struct coord_cache_entry
, 0));
894 f
= fopen("mcview-ccache.out", "w");
897 (void)setvbuf(f
, NULL
, _IONBF
, 0);
900 for (i
= 0; i
< view
->coord_cache
->len
; i
++) {
903 "offset %8"OFFSETTYPE_PRId
" "
904 "line %8"OFFSETTYPE_PRId
" "
905 "column %8"OFFSETTYPE_PRId
" "
906 "nroff_column %8"OFFSETTYPE_PRId
"\n",
907 (unsigned int) i
, cache
[i
].cc_offset
, cache
[i
].cc_line
,
908 cache
[i
].cc_column
, cache
[i
].cc_nroff_column
);
910 (void)fprintf (f
, "\n");
912 /* offset -> line/column translation */
913 for (offset
= 0; offset
< filesize
; offset
++) {
914 view_offset_to_coord (view
, &line
, &column
, offset
);
916 "offset %8"OFFSETTYPE_PRId
" "
917 "line %8"OFFSETTYPE_PRId
" "
918 "column %8"OFFSETTYPE_PRId
"\n",
919 offset
, line
, column
);
922 /* line/column -> offset translation */
923 for (line
= 0; TRUE
; line
++) {
924 view_coord_to_offset (view
, &nextline_offset
, line
+ 1, 0);
925 (void)fprintf (f
, "nextline_offset %8"OFFSETTYPE_PRId
"\n",
928 for (column
= 0; TRUE
; column
++) {
929 view_coord_to_offset (view
, &offset
, line
, column
);
930 if (offset
>= nextline_offset
)
933 (void)fprintf (f
, "line %8"OFFSETTYPE_PRId
" column %8"OFFSETTYPE_PRId
" offset %8"OFFSETTYPE_PRId
"\n",
934 line
, column
, offset
);
937 if (nextline_offset
>= filesize
- 1)
945 static inline gboolean
946 is_nroff_sequence (WView
*view
, offset_type offset
)
950 /* The following commands are ordered to speed up the calculation. */
952 c1
= get_byte_indexed (view
, offset
, 1);
953 if (c1
== -1 || c1
!= '\b')
956 c0
= get_byte_indexed (view
, offset
, 0);
957 if (c0
== -1 || !g_ascii_isprint(c0
))
960 c2
= get_byte_indexed (view
, offset
, 2);
961 if (c2
== -1 || !g_ascii_isprint(c2
))
964 return (c0
== c2
|| c0
== '_' || (c0
== '+' && c2
== 'o'));
967 /* Find and return the index of the last cache entry that is
968 * smaller than ''coord'', according to the criterion ''sort_by''. */
970 view_ccache_find (WView
*view
, const struct coord_cache_entry
*cache
,
971 const struct coord_cache_entry
*coord
, enum ccache_type sort_by
)
973 guint base
, i
, limit
;
975 limit
= view
->coord_cache
->len
;
980 i
= base
+ limit
/ 2;
981 if (coord_cache_entry_less (coord
, &cache
[i
], sort_by
, view
->text_nroff_mode
)) {
982 /* continue the search in the lower half of the cache */
984 /* continue the search in the upper half of the cache */
987 limit
= (limit
+ 1) / 2;
992 /* Look up the missing components of ''coord'', which are given by
993 * ''lookup_what''. The function returns the smallest value that
994 * matches the existing components of ''coord''.
997 view_ccache_lookup (WView
*view
, struct coord_cache_entry
*coord
,
998 enum ccache_type lookup_what
)
1001 struct coord_cache_entry
*cache
, current
, next
, entry
;
1002 enum ccache_type sorter
;
1010 if (!view
->coord_cache
) {
1011 view
->coord_cache
= g_array_new (FALSE
, FALSE
, sizeof(struct coord_cache_entry
));
1012 current
.cc_offset
= 0;
1013 current
.cc_line
= 0;
1014 current
.cc_column
= 0;
1015 current
.cc_nroff_column
= 0;
1016 g_array_append_val (view
->coord_cache
, current
);
1019 sorter
= (lookup_what
== CCACHE_OFFSET
) ? CCACHE_LINECOL
: CCACHE_OFFSET
;
1022 /* find the two neighbor entries in the cache */
1023 cache
= &(g_array_index (view
->coord_cache
, struct coord_cache_entry
, 0));
1024 i
= view_ccache_find (view
, cache
, coord
, sorter
);
1025 /* now i points to the lower neighbor in the cache */
1028 if (i
+ 1 < view
->coord_cache
->len
)
1029 limit
= cache
[i
+ 1].cc_offset
;
1031 limit
= current
.cc_offset
+ VIEW_COORD_CACHE_GRANUL
;
1034 nroff_state
= NROFF_START
;
1035 for (; current
.cc_offset
< limit
; current
= next
) {
1038 if ((c
= get_byte (view
, current
.cc_offset
)) == -1)
1041 if (!coord_cache_entry_less (¤t
, coord
, sorter
, view
->text_nroff_mode
)) {
1042 if (lookup_what
== CCACHE_OFFSET
1043 && view
->text_nroff_mode
1044 && nroff_state
!= NROFF_START
) {
1045 /* don't break here */
1051 /* Provide useful default values for ''next'' */
1052 next
.cc_offset
= current
.cc_offset
+ 1;
1053 next
.cc_line
= current
.cc_line
;
1054 next
.cc_column
= current
.cc_column
+ 1;
1055 next
.cc_nroff_column
= current
.cc_nroff_column
+ 1;
1057 /* and override some of them as necessary. */
1059 nextc
= get_byte_indexed(view
, current
.cc_offset
, 1);
1061 /* Ignore '\r' if it is followed by '\r' or '\n'. If it is
1062 * followed by anything else, it is a Mac line ending and
1063 * produces a line break.
1065 if (nextc
== '\r' || nextc
== '\n') {
1066 next
.cc_column
= current
.cc_column
;
1067 next
.cc_nroff_column
= current
.cc_nroff_column
;
1069 next
.cc_line
= current
.cc_line
+ 1;
1071 next
.cc_nroff_column
= 0;
1074 } else if (nroff_state
== NROFF_BACKSPACE
) {
1075 next
.cc_nroff_column
= current
.cc_nroff_column
- 1;
1077 } else if (c
== '\t') {
1078 next
.cc_column
= offset_rounddown (current
.cc_column
, 8) + 8;
1079 next
.cc_nroff_column
=
1080 offset_rounddown (current
.cc_nroff_column
, 8) + 8;
1082 } else if (c
== '\n') {
1083 next
.cc_line
= current
.cc_line
+ 1;
1085 next
.cc_nroff_column
= 0;
1088 /* Use all default values from above */
1091 switch (nroff_state
) {
1093 case NROFF_CONTINUATION
:
1094 if (is_nroff_sequence (view
, current
.cc_offset
))
1095 nroff_state
= NROFF_BACKSPACE
;
1097 nroff_state
= NROFF_START
;
1099 case NROFF_BACKSPACE
:
1100 nroff_state
= NROFF_CONTINUATION
;
1104 /* Cache entries must guarantee that for each i < j,
1105 * line[i] <= line[j] and column[i] < column[j]. In the case of
1106 * nroff sequences and '\r' characters, this is not guaranteed,
1107 * so we cannot save them. */
1108 if (nroff_state
== NROFF_START
&& c
!= '\r')
1112 if (i
+ 1 == view
->coord_cache
->len
&& entry
.cc_offset
!= cache
[i
].cc_offset
) {
1113 g_array_append_val (view
->coord_cache
, entry
);
1117 if (lookup_what
== CCACHE_OFFSET
) {
1118 coord
->cc_offset
= current
.cc_offset
;
1120 coord
->cc_line
= current
.cc_line
;
1121 coord
->cc_column
= current
.cc_column
;
1122 coord
->cc_nroff_column
= current
.cc_nroff_column
;
1127 view_coord_to_offset (WView
*view
, offset_type
*ret_offset
,
1128 offset_type line
, offset_type column
)
1130 struct coord_cache_entry coord
;
1132 coord
.cc_line
= line
;
1133 coord
.cc_column
= column
;
1134 coord
.cc_nroff_column
= column
;
1135 view_ccache_lookup (view
, &coord
, CCACHE_OFFSET
);
1136 *ret_offset
= coord
.cc_offset
;
1140 view_offset_to_coord (WView
*view
, offset_type
*ret_line
,
1141 offset_type
*ret_column
, offset_type offset
)
1143 struct coord_cache_entry coord
;
1145 coord
.cc_offset
= offset
;
1146 view_ccache_lookup (view
, &coord
, CCACHE_LINECOL
);
1147 *ret_line
= coord
.cc_line
;
1148 *ret_column
= (view
->text_nroff_mode
)
1149 ? coord
.cc_nroff_column
1153 /* {{{ Cursor Movement }}} */
1156 The following variables have to do with the current position and are
1157 updated by the cursor movement functions.
1159 In hex view and wrapped text view mode, dpy_start marks the offset of
1160 the top-left corner on the screen, in non-wrapping text mode it is
1161 the beginning of the current line. In hex mode, hex_cursor is the
1162 offset of the cursor. In non-wrapping text mode, dpy_text_column is
1163 the number of columns that are hidden on the left side on the screen.
1165 In hex mode, dpy_start is updated by the view_fix_cursor_position()
1166 function in order to keep the other functions simple. In
1167 non-wrapping text mode dpy_start and dpy_text_column are normalized
1168 such that dpy_text_column < view_get_datacolumns().
1171 /* prototypes for functions used by view_moveto_bottom() */
1172 static void view_move_up (WView
*, offset_type
);
1173 static void view_moveto_bol (WView
*);
1176 view_scroll_to_cursor (WView
*view
)
1178 if (view
->hex_mode
) {
1179 const offset_type bytes
= view
->bytes_per_line
;
1180 const offset_type displaysize
= view
->data_area
.height
* bytes
;
1181 const offset_type cursor
= view
->hex_cursor
;
1182 offset_type topleft
= view
->dpy_start
;
1184 if (topleft
+ displaysize
<= cursor
)
1185 topleft
= offset_rounddown (cursor
, bytes
)
1186 - (displaysize
- bytes
);
1187 if (cursor
< topleft
)
1188 topleft
= offset_rounddown (cursor
, bytes
);
1189 view
->dpy_start
= topleft
;
1190 } else if (view
->text_wrap_mode
) {
1191 offset_type line
, col
, columns
;
1193 columns
= view
->data_area
.width
;
1194 view_offset_to_coord (view
, &line
, &col
, view
->dpy_start
+ view
->dpy_text_column
);
1196 col
= offset_rounddown (col
, columns
);
1197 view_coord_to_offset (view
, &(view
->dpy_start
), line
, col
);
1198 view
->dpy_text_column
= 0;
1205 view_movement_fixups (WView
*view
, gboolean reset_search
)
1207 view_scroll_to_cursor (view
);
1209 view
->search_start
= view
->dpy_start
;
1210 view
->search_end
= view
->dpy_start
;
1216 view_moveto_top (WView
*view
)
1218 view
->dpy_start
= 0;
1219 view
->hex_cursor
= 0;
1220 view
->dpy_text_column
= 0;
1221 view_movement_fixups (view
, TRUE
);
1225 view_moveto_bottom (WView
*view
)
1227 offset_type datalines
, lines_up
, filesize
, last_offset
;
1229 if (view
->growbuf_in_use
)
1230 view_growbuf_read_until (view
, OFFSETTYPE_MAX
);
1232 filesize
= view_get_filesize (view
);
1233 last_offset
= offset_doz(filesize
, 1);
1234 datalines
= view
->data_area
.height
;
1235 lines_up
= offset_doz(datalines
, 1);
1237 if (view
->hex_mode
) {
1238 view
->hex_cursor
= filesize
;
1239 view_move_up (view
, lines_up
);
1240 view
->hex_cursor
= last_offset
;
1242 view
->dpy_start
= last_offset
;
1243 view_moveto_bol (view
);
1244 view_move_up (view
, lines_up
);
1246 view_movement_fixups (view
, TRUE
);
1250 view_moveto_bol (WView
*view
)
1252 if (view
->hex_mode
) {
1253 view
->hex_cursor
-= view
->hex_cursor
% view
->bytes_per_line
;
1254 } else if (view
->text_wrap_mode
) {
1257 offset_type line
, column
;
1258 view_offset_to_coord (view
, &line
, &column
, view
->dpy_start
);
1259 view_coord_to_offset (view
, &(view
->dpy_start
), line
, 0);
1260 view
->dpy_text_column
= 0;
1262 view_movement_fixups (view
, TRUE
);
1266 view_moveto_eol (WView
*view
)
1268 if (view
->hex_mode
) {
1269 offset_type filesize
, bol
;
1271 bol
= offset_rounddown (view
->hex_cursor
, view
->bytes_per_line
);
1272 if (get_byte_indexed (view
, bol
, view
->bytes_per_line
- 1) != -1) {
1273 view
->hex_cursor
= bol
+ view
->bytes_per_line
- 1;
1275 filesize
= view_get_filesize (view
);
1276 view
->hex_cursor
= offset_doz(filesize
, 1);
1278 } else if (view
->text_wrap_mode
) {
1281 offset_type line
, col
;
1283 view_offset_to_coord (view
, &line
, &col
, view
->dpy_start
);
1284 view_coord_to_offset (view
, &(view
->dpy_start
), line
, OFFSETTYPE_MAX
);
1286 view_movement_fixups (view
, FALSE
);
1290 view_moveto_offset (WView
*view
, offset_type offset
)
1292 if (view
->hex_mode
) {
1293 view
->hex_cursor
= offset
;
1294 view
->dpy_start
= offset
- offset
% view
->bytes_per_line
;
1296 view
->dpy_start
= offset
;
1298 view_movement_fixups (view
, TRUE
);
1302 view_moveto (WView
*view
, offset_type line
, offset_type col
)
1306 view_coord_to_offset (view
, &offset
, line
, col
);
1307 view_moveto_offset (view
, offset
);
1311 view_move_up (WView
*view
, offset_type lines
)
1313 if (view
->hex_mode
) {
1314 offset_type bytes
= lines
* view
->bytes_per_line
;
1315 if (view
->hex_cursor
>= bytes
) {
1316 view
->hex_cursor
-= bytes
;
1317 if (view
->hex_cursor
< view
->dpy_start
)
1318 view
->dpy_start
= offset_doz (view
->dpy_start
, bytes
);
1320 view
->hex_cursor
%= view
->bytes_per_line
;
1322 } else if (view
->text_wrap_mode
) {
1323 const screen_dimen width
= view
->data_area
.width
;
1324 offset_type i
, col
, line
, linestart
;
1326 for (i
= 0; i
< lines
; i
++) {
1327 view_offset_to_coord (view
, &line
, &col
, view
->dpy_start
);
1330 } else if (line
>= 1) {
1331 view_coord_to_offset (view
, &linestart
, line
, 0);
1332 view_offset_to_coord (view
, &line
, &col
, linestart
- 1);
1334 /* if the only thing that would be displayed were a
1335 * single newline character, advance to the previous
1336 * part of the line. */
1337 if (col
> 0 && col
% width
== 0)
1344 view_coord_to_offset (view
, &(view
->dpy_start
), line
, col
);
1347 offset_type line
, column
;
1349 view_offset_to_coord (view
, &line
, &column
, view
->dpy_start
);
1350 line
= offset_doz(line
, lines
);
1351 view_coord_to_offset (view
, &(view
->dpy_start
), line
, column
);
1353 view_movement_fixups (view
, (lines
!= 1));
1357 view_move_down (WView
*view
, offset_type lines
)
1359 if (view
->hex_mode
) {
1360 offset_type i
, limit
, last_byte
;
1362 last_byte
= view_get_filesize (view
);
1363 if (last_byte
>= (offset_type
) view
->bytes_per_line
)
1364 limit
= last_byte
- view
->bytes_per_line
;
1367 for (i
= 0; i
< lines
&& view
->hex_cursor
< limit
; i
++) {
1368 view
->hex_cursor
+= view
->bytes_per_line
;
1370 view
->dpy_start
+= view
->bytes_per_line
;
1373 } else if (view
->dpy_end
== view_get_filesize (view
)) {
1374 /* don't move further down. There's nothing more to see. */
1376 } else if (view
->text_wrap_mode
) {
1377 offset_type line
, col
, i
;
1379 for (i
= 0; i
< lines
; i
++) {
1380 offset_type new_offset
, chk_line
, chk_col
;
1382 view_offset_to_coord (view
, &line
, &col
, view
->dpy_start
);
1383 col
+= view
->data_area
.width
;
1384 view_coord_to_offset (view
, &new_offset
, line
, col
);
1386 /* skip to the next line if the only thing that would be
1387 * displayed is the newline character. */
1388 view_offset_to_coord (view
, &chk_line
, &chk_col
, new_offset
);
1389 if (chk_line
== line
&& chk_col
== col
1390 && get_byte (view
, new_offset
) == '\n')
1393 view
->dpy_start
= new_offset
;
1397 offset_type line
, col
;
1399 view_offset_to_coord (view
, &line
, &col
, view
->dpy_start
);
1401 view_coord_to_offset (view
, &(view
->dpy_start
), line
, col
);
1403 view_movement_fixups (view
, (lines
!= 1));
1407 view_move_left (WView
*view
, offset_type columns
)
1409 if (view
->hex_mode
) {
1410 assert (columns
== 1);
1411 if (view
->hexview_in_text
|| !view
->hexedit_lownibble
) {
1412 if (view
->hex_cursor
> 0)
1415 if (!view
->hexview_in_text
)
1416 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
1417 } else if (view
->text_wrap_mode
) {
1420 if (view
->dpy_text_column
>= columns
)
1421 view
->dpy_text_column
-= columns
;
1423 view
->dpy_text_column
= 0;
1425 view_movement_fixups (view
, FALSE
);
1429 view_move_right (WView
*view
, offset_type columns
)
1431 if (view
->hex_mode
) {
1432 assert (columns
== 1);
1433 if (view
->hexview_in_text
|| view
->hexedit_lownibble
) {
1434 if (get_byte_indexed (view
, view
->hex_cursor
, 1) != -1)
1437 if (!view
->hexview_in_text
)
1438 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
1439 } else if (view
->text_wrap_mode
) {
1442 view
->dpy_text_column
+= columns
;
1444 view_movement_fixups (view
, FALSE
);
1447 /* {{{ Toggling of viewer modes }}} */
1450 view_toggle_hex_mode (WView
*view
)
1452 view
->hex_mode
= !view
->hex_mode
;
1454 if (view
->hex_mode
) {
1455 view
->hex_cursor
= view
->dpy_start
;
1457 offset_rounddown (view
->dpy_start
, view
->bytes_per_line
);
1458 view
->widget
.options
|= W_WANT_CURSOR
;
1460 view
->dpy_start
= view
->hex_cursor
;
1461 view_moveto_bol (view
);
1462 view
->widget
.options
&= ~W_WANT_CURSOR
;
1464 altered_hex_mode
= 1;
1465 view
->dpy_bbar_dirty
= TRUE
;
1470 view_toggle_hexedit_mode (WView
*view
)
1472 view
->hexedit_mode
= !view
->hexedit_mode
;
1473 view
->dpy_bbar_dirty
= TRUE
;
1478 view_toggle_wrap_mode (WView
*view
)
1480 view
->text_wrap_mode
= !view
->text_wrap_mode
;
1481 if (view
->text_wrap_mode
) {
1482 view_scroll_to_cursor (view
);
1486 view_offset_to_coord (view
, &line
, &(view
->dpy_text_column
), view
->dpy_start
);
1487 view_coord_to_offset (view
, &(view
->dpy_start
), line
, 0);
1489 view
->dpy_bbar_dirty
= TRUE
;
1494 view_toggle_nroff_mode (WView
*view
)
1496 view
->text_nroff_mode
= !view
->text_nroff_mode
;
1497 altered_nroff_flag
= 1;
1498 view
->dpy_bbar_dirty
= TRUE
;
1503 view_toggle_magic_mode (WView
*view
)
1505 char *filename
, *command
;
1507 altered_magic_flag
= 1;
1508 view
->magic_mode
= !view
->magic_mode
;
1509 filename
= g_strdup (view
->filename
);
1510 command
= g_strdup (view
->command
);
1513 view_load (view
, command
, filename
, 0);
1516 view
->dpy_bbar_dirty
= TRUE
;
1520 /* {{{ Miscellaneous functions }}} */
1523 view_done (WView
*view
)
1525 /* Save current file position */
1526 if (mcview_remember_file_position
&& view
->filename
!= NULL
) {
1528 offset_type line
, col
;
1530 canon_fname
= vfs_canon (view
->filename
);
1531 view_offset_to_coord (view
, &line
, &col
, view
->dpy_start
);
1532 save_file_position (canon_fname
, line
+ 1, col
);
1533 g_free (canon_fname
);
1536 /* Write back the global viewer mode */
1537 default_hex_mode
= view
->hex_mode
;
1538 default_nroff_flag
= view
->text_nroff_mode
;
1539 default_magic_flag
= view
->magic_mode
;
1540 global_wrap_mode
= view
->text_wrap_mode
;
1542 /* Free memory used by the viewer */
1544 /* view->widget needs no destructor */
1546 g_free (view
->filename
), view
->filename
= NULL
;
1547 g_free (view
->command
), view
->command
= NULL
;
1549 view_close_datasource (view
);
1550 /* the growing buffer is freed with the datasource */
1552 if (view
->coord_cache
) {
1553 g_array_free (view
->coord_cache
, TRUE
), view
->coord_cache
= NULL
;
1556 view_hexedit_free_change_list (view
);
1557 /* FIXME: what about view->search_exp? */
1559 if (view
->converter
!= str_cnv_from_term
)
1560 str_close_conv (view
->converter
);
1564 view_show_error (WView
*view
, const char *msg
)
1566 view_close_datasource (view
);
1567 if (view_is_in_panel (view
)) {
1568 view_set_datasource_string (view
, msg
);
1570 message (D_ERROR
, MSG_ERROR
, "%s", msg
);
1575 view_load_command_output (WView
*view
, const char *command
)
1579 view_close_datasource (view
);
1582 if ((fp
= popen (command
, "r")) == NULL
) {
1583 /* Avoid two messages. Message from stderr has priority. */
1585 if (!close_error_pipe (view_is_in_panel (view
) ? -1 : D_ERROR
, NULL
))
1586 view_show_error (view
, _(" Cannot spawn child process "));
1590 /* First, check if filter produced any output */
1591 view_set_datasource_stdio_pipe (view
, fp
);
1592 if (get_byte (view
, 0) == -1) {
1593 view_close_datasource (view
);
1595 /* Avoid two messages. Message from stderr has priority. */
1597 if (!close_error_pipe (view_is_in_panel (view
) ? -1 : D_ERROR
, NULL
))
1598 view_show_error (view
, _("Empty output from child filter"));
1602 * At least something was read correctly. Close stderr and let
1603 * program die if it will try to write something there.
1605 * Ideally stderr should be read asynchronously to prevent programs
1606 * from blocking (poll/select multiplexor).
1608 close_error_pipe (D_NORMAL
, NULL
);
1614 view_load (WView
*view
, const char *command
, const char *file
,
1619 char tmp
[BUF_MEDIUM
];
1626 gboolean retval
= FALSE
;
1628 cp_id
= get_codepage_id (source_codepage
);
1630 if (cp_id
!= NULL
&& str_isutf8 (cp_id
) != 0)
1636 assert (view
->bytes_per_line
!= 0);
1639 /* Set up the state */
1640 view_set_datasource_none (view
);
1641 view
->filename
= g_strdup (file
);
1644 /* Clear the markers */
1646 for (i
= 0; i
< 10; i
++)
1649 if (!view_is_in_panel (view
)) {
1650 view
->dpy_text_column
= 0;
1653 if (command
&& (view
->magic_mode
|| file
== NULL
|| file
[0] == '\0')) {
1654 retval
= view_load_command_output (view
, command
);
1655 } else if (file
!= NULL
&& file
[0] != '\0') {
1657 if ((fd
= mc_open (file
, O_RDONLY
| O_NONBLOCK
)) == -1) {
1658 g_snprintf (tmp
, sizeof (tmp
), _(" Cannot open \"%s\"\n %s "),
1659 file
, unix_error_string (errno
));
1660 view_show_error (view
, tmp
);
1661 g_free (view
->filename
);
1662 view
->filename
= NULL
;
1666 /* Make sure we are working with a regular file */
1667 if (mc_fstat (fd
, &st
) == -1) {
1669 g_snprintf (tmp
, sizeof (tmp
), _(" Cannot stat \"%s\"\n %s "),
1670 file
, unix_error_string (errno
));
1671 view_show_error (view
, tmp
);
1672 g_free (view
->filename
);
1673 view
->filename
= NULL
;
1677 if (!S_ISREG (st
.st_mode
)) {
1679 view_show_error (view
, _(" Cannot view: not a regular file "));
1680 g_free (view
->filename
);
1681 view
->filename
= NULL
;
1685 if (st
.st_size
== 0 || mc_lseek (fd
, 0, SEEK_SET
) == -1) {
1686 /* Must be one of those nice files that grow (/proc) */
1687 view_set_datasource_vfs_pipe (view
, fd
);
1689 type
= get_compression_type (fd
);
1691 if (view
->magic_mode
&& (type
!= COMPRESSION_NONE
)) {
1692 g_free (view
->filename
);
1693 view
->filename
= g_strconcat (file
, decompress_extension (type
), (char *) NULL
);
1695 view_set_datasource_file (view
, fd
, &st
);
1701 view
->command
= g_strdup (command
);
1702 view
->dpy_start
= 0;
1703 view
->search_start
= 0;
1704 view
->search_end
= 0;
1705 view
->dpy_text_column
= 0;
1707 view
->converter
= str_cnv_from_term
;
1708 /* try detect encoding from path */
1709 if (view
->filename
!= NULL
) {
1710 canon_fname
= vfs_canon (view
->filename
);
1711 enc
= vfs_get_encoding (canon_fname
);
1713 view
->converter
= str_crt_conv_from (enc
);
1714 if (view
->converter
== INVALID_CONV
)
1715 view
->converter
= str_cnv_from_term
;
1717 g_free (canon_fname
);
1720 view_compute_areas (view
);
1721 assert (view
->bytes_per_line
!= 0);
1722 if (mcview_remember_file_position
&& view
->filename
!= NULL
&& start_line
== 0) {
1725 canon_fname
= vfs_canon (view
->filename
);
1726 load_file_position (canon_fname
, &line
, &col
);
1727 g_free (canon_fname
);
1728 view_moveto (view
, offset_doz(line
, 1), col
);
1729 } else if (start_line
> 0) {
1730 view_moveto (view
, start_line
- 1, 0);
1733 view
->hexedit_lownibble
= FALSE
;
1734 view
->hexview_in_text
= FALSE
;
1735 view
->change_list
= NULL
;
1740 /* {{{ Display management }}} */
1743 view_update_bytes_per_line (WView
*view
)
1745 const screen_dimen cols
= view
->data_area
.width
;
1751 bytes
= 4 * ((cols
- 8) / ((cols
< 80) ? 17 : 18));
1754 view
->bytes_per_line
= bytes
;
1755 view
->dirty
= max_dirt_limit
+ 1; /* To force refresh */
1759 view_percent (WView
*view
, offset_type p
)
1761 const screen_dimen top
= view
->status_area
.top
;
1762 const screen_dimen right
= view
->status_area
.left
+ view
->status_area
.width
;
1763 const screen_dimen height
= view
->status_area
.height
;
1765 offset_type filesize
;
1767 if (height
< 1 || right
< 4)
1769 if (view_may_still_grow (view
))
1771 filesize
= view_get_filesize (view
);
1773 if (filesize
== 0 || view
->dpy_end
== filesize
)
1775 else if (p
> (INT_MAX
/ 100))
1776 percent
= p
/ (filesize
/ 100);
1778 percent
= p
* 100 / filesize
;
1780 widget_move (view
, top
, right
- 4);
1781 tty_printf ("%3d%%", percent
);
1785 view_display_status (WView
*view
)
1787 const screen_dimen top
= view
->status_area
.top
;
1788 const screen_dimen left
= view
->status_area
.left
;
1789 const screen_dimen width
= view
->status_area
.width
;
1790 const screen_dimen height
= view
->status_area
.height
;
1791 const char *file_label
, *file_name
;
1792 screen_dimen file_label_width
;
1798 tty_setcolor (SELECTED_COLOR
);
1799 tty_draw_hline (view
->widget
.y
+ top
, view
->widget
.x
+ left
, ' ', width
);
1801 file_label
= _("File: %s");
1802 file_label_width
= str_term_width1 (file_label
) - 2;
1803 file_name
= view
->filename
? view
->filename
1804 : view
->command
? view
->command
1807 if (width
< file_label_width
+ 6)
1808 tty_print_string (str_fit_to_term (file_name
, width
, J_LEFT_FIT
));
1810 i
= (width
> 22 ? 22 : width
) - file_label_width
;
1811 tty_printf (file_label
, str_fit_to_term (file_name
, i
, J_LEFT_FIT
));
1814 widget_move (view
, top
, left
+ 24);
1815 /* FIXME: the format strings need to be changed when offset_type changes */
1817 tty_printf (_("Offset 0x%08lx"), (unsigned long) view
->hex_cursor
);
1819 offset_type line
, col
;
1820 view_offset_to_coord (view
, &line
, &col
, view
->dpy_start
);
1821 tty_printf (_("Line %lu Col %lu"),
1822 (unsigned long) line
+ 1,
1823 (unsigned long) (view
->text_wrap_mode
? col
: view
->dpy_text_column
));
1827 offset_type filesize
;
1828 filesize
= view_get_filesize (view
);
1829 widget_move (view
, top
, left
+ 43);
1830 if (!view_may_still_grow (view
)) {
1831 tty_printf (_("%s bytes"), size_trunc (filesize
));
1833 tty_printf (_(">= %s bytes"), size_trunc (filesize
));
1837 view_percent (view
, view
->hex_mode
1842 tty_setcolor (SELECTED_COLOR
);
1846 view_display_clean (WView
*view
)
1848 tty_setcolor (NORMAL_COLOR
);
1849 widget_erase ((Widget
*) view
);
1850 if (view
->dpy_frame_size
!= 0) {
1851 draw_box (view
->widget
.parent
, view
->widget
.y
,
1852 view
->widget
.x
, view
->widget
.lines
,
1865 view_count_backspaces (WView
*view
, off_t offset
)
1868 while (offset
>= 2 * backspaces
1869 && get_byte (view
, offset
- 2 * backspaces
) == '\b')
1875 view_display_ruler (WView
*view
)
1877 static const char ruler_chars
[] = "|----*----";
1878 const screen_dimen top
= view
->ruler_area
.top
;
1879 const screen_dimen left
= view
->ruler_area
.left
;
1880 const screen_dimen width
= view
->ruler_area
.width
;
1881 const screen_dimen height
= view
->ruler_area
.height
;
1882 const screen_dimen line_row
= (ruler
== RULER_TOP
) ? 0 : 1;
1883 const screen_dimen nums_row
= (ruler
== RULER_TOP
) ? 1 : 0;
1889 if (ruler
== RULER_NONE
|| height
< 1)
1892 tty_setcolor (MARKED_COLOR
);
1893 for (c
= 0; c
< width
; c
++) {
1894 cl
= view
->dpy_text_column
+ c
;
1895 if (line_row
< height
) {
1896 widget_move (view
, top
+ line_row
, left
+ c
);
1897 tty_print_char (ruler_chars
[cl
% 10]);
1900 if ((cl
!= 0) && (cl
% 10) == 0) {
1901 g_snprintf (r_buff
, sizeof (r_buff
), "%"OFFSETTYPE_PRId
, cl
);
1902 if (nums_row
< height
) {
1903 widget_move (view
, top
+ nums_row
, left
+ c
- 1);
1904 tty_print_string (r_buff
);
1908 tty_setcolor (NORMAL_COLOR
);
1912 view_display_hex (WView
*view
)
1914 const screen_dimen top
= view
->data_area
.top
;
1915 const screen_dimen left
= view
->data_area
.left
;
1916 const screen_dimen height
= view
->data_area
.height
;
1917 const screen_dimen width
= view
->data_area
.width
;
1918 const int ngroups
= view
->bytes_per_line
/ 4;
1919 const screen_dimen text_start
=
1920 8 + 13 * ngroups
+ ((width
< 80) ? 0 : (ngroups
- 1 + 1));
1921 /* 8 characters are used for the file offset, and every hex group
1922 * takes 13 characters. On ``big'' screens, the groups are separated
1923 * by an extra vertical line, and there is an extra space before the
1927 screen_dimen row
, col
;
1930 mark_t boldflag
= MARK_NORMAL
;
1931 struct hexedit_change_node
*curr
= view
->change_list
;
1936 char hex_buff
[10]; /* A temporary buffer for sprintf */
1937 int bytes
; /* Number of bytes already printed on the line */
1939 view_display_clean (view
);
1941 /* Find the first displayable changed byte */
1942 from
= view
->dpy_start
;
1943 while (curr
&& (curr
->offset
< from
)) {
1947 for (row
= 0; get_byte (view
, from
) != -1 && row
< height
; row
++) {
1950 /* Print the hex offset */
1951 g_snprintf (hex_buff
, sizeof (hex_buff
), "%08"OFFSETTYPE_PRIX
" ", from
);
1952 widget_move (view
, top
+ row
, left
);
1953 tty_setcolor (MARKED_COLOR
);
1954 for (i
= 0; col
< width
&& hex_buff
[i
] != '\0'; i
++) {
1955 tty_print_char (hex_buff
[i
]);
1958 tty_setcolor (NORMAL_COLOR
);
1960 for (bytes
= 0; bytes
< view
->bytes_per_line
; bytes
++, from
++) {
1964 if ((ch
= get_utf (view
, from
, &cw
)) == -1)
1968 if ((c
= get_byte (view
, from
)) == -1)
1971 /* Save the cursor position for view_place_cursor() */
1972 if (from
== view
->hex_cursor
&& !view
->hexview_in_text
) {
1973 view
->cursor_row
= row
;
1974 view
->cursor_col
= col
;
1977 /* Determine the state of the current byte */
1979 (from
== view
->hex_cursor
) ? MARK_CURSOR
1980 : (curr
!= NULL
&& from
== curr
->offset
) ? MARK_CHANGED
1981 : (view
->search_start
<= from
&&
1982 from
< view
->search_end
) ? MARK_SELECTED
1985 /* Determine the value of the current byte */
1986 if (curr
!= NULL
&& from
== curr
->offset
) {
1991 /* Select the color for the hex number */
1993 boldflag
== MARK_NORMAL
? NORMAL_COLOR
:
1994 boldflag
== MARK_SELECTED
? MARKED_COLOR
:
1995 boldflag
== MARK_CHANGED
? VIEW_UNDERLINED_COLOR
:
1996 /* boldflag == MARK_CURSOR */
1997 view
->hexview_in_text
? MARKED_SELECTED_COLOR
:
1998 VIEW_UNDERLINED_COLOR
);
2000 /* Print the hex number */
2001 widget_move (view
, top
+ row
, left
+ col
);
2003 tty_print_char (hex_char
[c
/ 16]);
2007 tty_print_char (hex_char
[c
% 16]);
2011 /* Print the separator */
2012 tty_setcolor (NORMAL_COLOR
);
2013 if (bytes
!= view
->bytes_per_line
- 1) {
2015 tty_print_char (' ');
2019 /* After every four bytes, print a group separator */
2020 if (bytes
% 4 == 3) {
2021 if (view
->data_area
.width
>= 80 && col
< width
) {
2022 tty_print_one_vline ();
2026 tty_print_char (' ');
2032 /* Select the color for the character; this differs from the
2033 * hex color when boldflag == MARK_CURSOR */
2035 boldflag
== MARK_NORMAL
? NORMAL_COLOR
:
2036 boldflag
== MARK_SELECTED
? MARKED_COLOR
:
2037 boldflag
== MARK_CHANGED
? VIEW_UNDERLINED_COLOR
:
2038 /* boldflag == MARK_CURSOR */
2039 view
->hexview_in_text
? VIEW_UNDERLINED_COLOR
:
2040 MARKED_SELECTED_COLOR
);
2043 if ( utf8_display
) {
2044 if ( !view
->utf8
) {
2045 ch
= convert_from_8bit_to_utf_c ((unsigned char) ch
, view
->converter
);
2049 ch
= convert_from_utf_to_current_c (ch
, view
->converter
);
2052 ch
= convert_to_display_c (ch
);
2057 c
= convert_to_display_c (c
);
2058 if (!g_ascii_isprint (c
))
2061 /* Print corresponding character on the text side */
2062 if (text_start
+ bytes
< width
) {
2063 widget_move (view
, top
+ row
, left
+ text_start
+ bytes
);
2064 if ( !view
->utf8
) {
2067 tty_print_anychar (ch
);
2071 /* Save the cursor position for view_place_cursor() */
2072 if (from
== view
->hex_cursor
&& view
->hexview_in_text
) {
2073 view
->cursor_row
= row
;
2074 view
->cursor_col
= text_start
+ bytes
;
2079 /* Be polite to the other functions */
2080 tty_setcolor (NORMAL_COLOR
);
2082 view_place_cursor (view
);
2083 view
->dpy_end
= from
;
2087 view_display_text (WView
* view
)
2089 const screen_dimen left
= view
->data_area
.left
;
2090 const screen_dimen top
= view
->data_area
.top
;
2091 const screen_dimen width
= view
->data_area
.width
;
2092 const screen_dimen height
= view
->data_area
.height
;
2093 screen_dimen row
, col
;
2097 struct hexedit_change_node
*curr
= view
->change_list
;
2099 view_display_clean (view
);
2100 view_display_ruler (view
);
2102 /* Find the first displayable changed byte */
2103 from
= view
->dpy_start
;
2104 while (curr
&& (curr
->offset
< from
)) {
2108 tty_setcolor (NORMAL_COLOR
);
2109 for (row
= 0, col
= 0; row
< height
; ) {
2112 if ((c
= get_utf (view
, from
, &cw
)) == -1)
2117 if ((c
= get_byte (view
, from
)) == -1)
2121 mc_log("cw=%i\n", cw
);
2125 if (view
->text_nroff_mode
&& c
== '\b') {
2129 if ((c_next
= get_byte_indexed (view
, from
, 1)) != -1
2130 && g_ascii_isprint (c_next
)
2132 && (c_prev
= get_byte (view
, from
- 1)) != -1
2133 && g_ascii_isprint (c_prev
)
2134 && (c_prev
== c_next
|| c_prev
== '_'
2135 || (c_prev
== '+' && c_next
== 'o'))) {
2138 /* We're inside an nroff character sequence at the
2139 * beginning of the screen -- just skip the
2140 * backspace and continue with the next character. */
2147 if (c_prev
== '_' && (c_next
!= '_' || view_count_backspaces (view
, from
) == 1))
2148 tty_setcolor (VIEW_UNDERLINED_COLOR
);
2150 tty_setcolor (MARKED_COLOR
);
2155 if ((c
== '\n') || (col
>= width
&& view
->text_wrap_mode
)) {
2158 if (c
== '\n' || row
>= height
)
2163 c
= get_byte_indexed(view
, from
, 1);
2164 if (c
== '\r' || c
== '\n')
2172 offset_type line
, column
;
2173 view_offset_to_coord (view
, &line
, &column
, from
);
2174 col
+= (8 - column
% 8);
2175 if (view
->text_wrap_mode
&& col
>= width
&& width
!= 0) {
2182 if (view
->search_start
<= from
&& from
< view
->search_end
) {
2183 tty_setcolor (SELECTED_COLOR
);
2186 if (col
>= view
->dpy_text_column
2187 && col
- view
->dpy_text_column
< width
) {
2188 widget_move (view
, top
+ row
, left
+ (col
- view
->dpy_text_column
));
2190 if ( utf8_display
) {
2191 if ( !view
->utf8
) {
2192 c
= convert_from_8bit_to_utf_c ((unsigned char) c
, view
->converter
);
2196 c
= convert_from_utf_to_current_c (c
, view
->converter
);
2199 c
= convert_to_display_c (c
);
2204 tty_print_anychar (c
);
2207 tty_setcolor (NORMAL_COLOR
);
2209 view
->dpy_end
= from
;
2212 /* Displays as much data from view->dpy_start as fits on the screen */
2214 display (WView
*view
)
2216 view_compute_areas (view
);
2217 if (view
->hex_mode
) {
2218 view_display_hex (view
);
2220 view_display_text (view
);
2222 view_display_status (view
);
2226 view_place_cursor (WView
*view
)
2228 const screen_dimen top
= view
->data_area
.top
;
2229 const screen_dimen left
= view
->data_area
.left
;
2232 col
= view
->cursor_col
;
2233 if (!view
->hexview_in_text
&& view
->hexedit_lownibble
)
2235 widget_move (&view
->widget
, top
+ view
->cursor_row
, left
+ col
);
2239 view_update (WView
*view
)
2241 static int dirt_limit
= 1;
2243 if (view
->dpy_bbar_dirty
) {
2244 view
->dpy_bbar_dirty
= FALSE
;
2246 buttonbar_redraw (view
->widget
.parent
);
2249 if (view
->dirty
> dirt_limit
) {
2250 /* Too many updates skipped -> force a update */
2253 /* Raise the update skipping limit */
2255 if (dirt_limit
> max_dirt_limit
)
2256 dirt_limit
= max_dirt_limit
;
2260 /* We have time to update the screen properly */
2266 /* We are busy -> skipping full update,
2267 only the status line is updated */
2268 view_display_status (view
);
2270 /* Here we had a refresh, if fast scrolling does not work
2271 restore the refresh, although this should not happen */
2275 /* {{{ Hex editor }}} */
2278 enqueue_change (struct hexedit_change_node
**head
,
2279 struct hexedit_change_node
*node
)
2281 /* chnode always either points to the head of the list or
2282 * to one of the ->next fields in the list. The value at
2283 * this location will be overwritten with the new node. */
2284 struct hexedit_change_node
**chnode
= head
;
2286 while (*chnode
!= NULL
&& (*chnode
)->offset
< node
->offset
)
2287 chnode
= &((*chnode
)->next
);
2289 node
->next
= *chnode
;
2294 view_handle_editkey (WView
*view
, int key
)
2296 struct hexedit_change_node
*node
;
2299 /* Has there been a change at this position? */
2300 node
= view
->change_list
;
2301 while (node
&& (node
->offset
!= view
->hex_cursor
))
2304 if (!view
->hexview_in_text
) {
2306 unsigned int hexvalue
= 0;
2308 if (key
>= '0' && key
<= '9')
2309 hexvalue
= 0 + (key
- '0');
2310 else if (key
>= 'A' && key
<= 'F')
2311 hexvalue
= 10 + (key
- 'A');
2312 else if (key
>= 'a' && key
<= 'f')
2313 hexvalue
= 10 + (key
- 'a');
2315 return MSG_NOT_HANDLED
;
2318 byte_val
= node
->value
;
2320 byte_val
= get_byte (view
, view
->hex_cursor
);
2322 if (view
->hexedit_lownibble
) {
2323 byte_val
= (byte_val
& 0xf0) | (hexvalue
);
2325 byte_val
= (byte_val
& 0x0f) | (hexvalue
<< 4);
2329 if (key
< 256 && ((key
== '\n') || is_printable (key
)))
2332 return MSG_NOT_HANDLED
;
2335 node
= g_new (struct hexedit_change_node
, 1);
2336 node
->offset
= view
->hex_cursor
;
2337 node
->value
= byte_val
;
2338 enqueue_change (&view
->change_list
, node
);
2340 node
->value
= byte_val
;
2344 view_move_right (view
, 1);
2349 view_hexedit_save_changes (WView
*view
)
2351 struct hexedit_change_node
*curr
, *next
;
2355 if (view
->change_list
== NULL
)
2359 assert (view
->filename
!= NULL
);
2360 fp
= mc_open (view
->filename
, O_WRONLY
);
2364 for (curr
= view
->change_list
; curr
!= NULL
; curr
= next
) {
2367 if (mc_lseek (fp
, curr
->offset
, SEEK_SET
) == -1
2368 || mc_write (fp
, &(curr
->value
), 1) != 1)
2371 /* delete the saved item from the change list */
2372 view
->change_list
= next
;
2374 view_set_byte (view
, curr
->offset
, curr
->value
);
2378 if (mc_close (fp
) == -1) {
2379 error
= g_strdup (strerror (errno
));
2380 message (D_ERROR
, _(" Save file "),
2381 _(" Error while closing the file: \n %s \n"
2382 " Data may have been written or not. "), error
);
2389 error
= g_strdup (strerror (errno
));
2390 text
= g_strdup_printf (_(" Cannot save file: \n %s "), error
);
2392 (void) mc_close (fp
);
2394 answer
= query_dialog (_(" Save file "), text
, D_ERROR
,
2395 2, _("&Retry"), _("&Cancel"));
2403 /* {{{ Miscellaneous functions }}} */
2406 view_ok_to_quit (WView
*view
)
2410 if (view
->change_list
== NULL
)
2413 r
= query_dialog (_("Quit"),
2414 _(" File was modified, Save with exit? "), D_NORMAL
, 3,
2415 _("&Cancel quit"), _("&Yes"), _("&No"));
2419 return view_hexedit_save_changes (view
);
2421 view_hexedit_free_change_list (view
);
2429 my_define (Dlg_head
*h
, int idx
, const char *text
, void (*fn
) (WView
*),
2432 buttonbar_set_label_data (h
, idx
, text
, (buttonbarfn
) fn
, view
);
2435 /* {{{ Searching }}} */
2438 /* we have set view->search_start and view->search_end and must set
2439 * view->dpy_text_column, view->first_showed_line and view->dpy_start
2440 * try to displaye maximum of match */
2442 view_moveto_match (WView
*view
)
2448 grow_string_buffer (char *text
, gulong
*size
)
2452 /* The grow steps */
2454 new = g_realloc (text
, *size
);
2462 get_line_at (WView
*view
, offset_type
*p
, offset_type
*skipped
)
2464 char *buffer
= NULL
;
2465 gulong buffer_size
= 0;
2466 offset_type usable_size
= 0;
2468 const gboolean direction
= !view
->search_backwards
;
2469 offset_type pos
= *p
;
2475 if (pos
== 0 && !direction
)
2478 /* skip over all the possible zeros in the file */
2479 while ((ch
= get_byte (view
, pos
)) == 0) {
2480 if (pos
== 0 && !direction
)
2482 pos
+= (direction
? 1 : -1);
2487 if (i
== 0 && (pos
!= 0 || !direction
)) {
2488 prev
= get_byte (view
, pos
- (direction
? 1 : -1));
2489 if ((prev
== -1) || (prev
== '\n'))
2493 for (i
= 1; ch
!= -1; ch
= get_byte (view
, pos
)) {
2494 if (i
>= usable_size
) {
2495 buffer
= grow_string_buffer (buffer
, &buffer_size
);
2496 usable_size
= buffer_size
- 2; /* prev & null terminator */
2501 if (pos
== 0 && !direction
)
2504 pos
+= (direction
? 1 : -1);
2506 if (ch
== '\n' || ch
== '\0') {
2507 i
--; /* Strip newline/zero */
2516 /* If we are searching backwards, reverse the string */
2518 g_strreverse (buffer
+ 1);
2527 search_update_steps (WView
*view
)
2529 offset_type filesize
= view_get_filesize (view
);
2531 view
->update_steps
= 40000;
2532 else /* viewing a data stream, not a file */
2533 view
->update_steps
= filesize
/ 100;
2535 /* Do not update the percent display but every 20 ks */
2536 if (view
->update_steps
< 20000)
2537 view
->update_steps
= 20000;
2540 /* {{{ User-definable commands }}} */
2543 The functions in this section can be bound to hotkeys. They are all
2544 of the same type (taking a pointer to WView as parameter and
2545 returning void). TODO: In the not-too-distant future, these commands
2546 will become fully configurable, like they already are in the
2547 internal editor. By convention, all the function names end in
2552 view_help_cmd (void)
2554 interactive_display (NULL
, "[Internal File Viewer]");
2557 /* Toggle between hexview and hexedit mode */
2559 view_toggle_hexedit_mode_cmd (WView
*view
)
2561 view_toggle_hexedit_mode (view
);
2565 /* Toggle between wrapped and unwrapped view */
2567 view_toggle_wrap_mode_cmd (WView
*view
)
2569 view_toggle_wrap_mode (view
);
2573 /* Toggle between hex view and text view */
2575 view_toggle_hex_mode_cmd (WView
*view
)
2577 view_toggle_hex_mode (view
);
2582 view_moveto_line_cmd (WView
*view
)
2584 char *answer
, *answer_end
, prompt
[BUF_SMALL
];
2585 offset_type line
, col
;
2587 view_offset_to_coord (view
, &line
, &col
, view
->dpy_start
);
2589 g_snprintf (prompt
, sizeof (prompt
),
2590 _(" The current line number is %d.\n"
2591 " Enter the new line number:"), (int) (line
+ 1));
2592 answer
= input_dialog (_(" Goto line "), prompt
, MC_HISTORY_VIEW_GOTO_LINE
, "");
2593 if (answer
!= NULL
&& answer
[0] != '\0') {
2595 line
= strtoul (answer
, &answer_end
, 10);
2596 if (*answer_end
== '\0' && errno
== 0 && line
>= 1)
2597 view_moveto (view
, line
- 1, 0);
2605 view_moveto_addr_cmd (WView
*view
)
2607 char *line
, *error
, prompt
[BUF_SMALL
];
2610 g_snprintf (prompt
, sizeof (prompt
),
2611 _(" The current address is 0x%lx.\n"
2612 " Enter the new address:"), view
->hex_cursor
);
2613 line
= input_dialog (_(" Goto Address "), prompt
, MC_HISTORY_VIEW_GOTO_ADDR
, "");
2615 if (*line
!= '\0') {
2616 addr
= strtoul (line
, &error
, 0);
2617 if ((*error
== '\0') && get_byte (view
, addr
) != -1) {
2618 view_moveto_offset (view
, addr
);
2620 message (D_ERROR
, _("Warning"), _(" Invalid address "));
2630 view_hexedit_save_changes_cmd (WView
*view
)
2632 (void) view_hexedit_save_changes (view
);
2636 view__get_nroff_real_len(WView
*view
, offset_type start
, offset_type length
)
2638 return 0; /* AB:FIXME */
2641 /* {{{ Searching }}} */
2644 view_search_update_cmd_callback(const void *user_data
, gsize char_offset
)
2646 WView
*view
= (WView
*) user_data
;
2648 if (char_offset
>= view
->update_activate
) {
2649 view
->update_activate
+= view
->update_steps
;
2651 view_percent (view
, char_offset
);
2654 if (tty_got_interrupt ())
2655 return MC_SEARCH_CB_ABORT
;
2657 /* may be in future return from this callback will change current position
2658 * in searching block. Now this just constant return value.
2664 view_search_cmd_callback(const void *user_data
, gsize char_offset
)
2667 WView
*view
= (WView
*) user_data
;
2669 byte
= get_byte (view
, char_offset
);
2671 return MC_SEARCH_CB_ABORT
;
2672 /* view_read_continue (view, &view->search_onechar_info); */ /* AB:FIXME */
2674 if (view
->search_numNeedSkipChar
) {
2675 view
->search_numNeedSkipChar
--;
2676 if (view
->search_numNeedSkipChar
){
2679 return MC_SEARCH_CB_SKIP
;
2682 #if 0 /* AB:FIXME */
2683 if (view_read_test_nroff_back (view
, &view
->search_onechar_info
)) {
2685 cmp (view
->search_onechar_info
.chi1
, "_") &&
2686 (!cmp (view
->search_onechar_info
.cnxt
, "_") || !cmp (view
->search_onechar_info
.chi2
, "\b"))
2688 view
->search_numNeedSkipChar
= 2;
2690 view
->search_numNeedSkipChar
= 1;
2692 return MC_SEARCH_CB_SKIP
;
2694 if (byte
== '_' && *view
->search_onechar_info
.cnxt
== 0x8)
2696 view
->search_numNeedSkipChar
= 1;
2697 return MC_SEARCH_CB_SKIP
;
2705 view_find (WView
*view
, gsize search_start
, gsize
*len
)
2707 #if 0 /* AB:FIXME */
2710 view
->search_numNeedSkipChar
= 0;
2712 if (view
->search_backwards
) {
2713 search_end
= view_get_filesize (view
);
2714 while ((int) search_start
>= 0) {
2715 if (search_end
> search_start
+ view
->search
->original_len
&& mc_search_is_fixed_search_str(view
->search
))
2716 search_end
= search_start
+ view
->search
->original_len
;
2718 view_read_start (view
, &view
->search_onechar_info
, search_start
);
2720 if ( mc_search_run(view
->search
, (void *) view
, search_start
, search_end
, len
)
2721 && view
->search
->normal_offset
== search_start
)
2726 view
->search
->error_str
= g_strdup(_(" Search string not found "));
2729 view_read_start (view
, &view
->search_onechar_info
, search_start
);
2730 return mc_search_run(view
->search
, (void *) view
, search_start
, view_get_filesize (view
), len
);
2734 /* {{{ User-definable commands }}} */
2737 do_search (WView
*view
)
2739 offset_type search_start
;
2740 gboolean isFound
= FALSE
;
2747 d
= create_message (D_NORMAL
, _("Search"), _("Searching %s"), view
->last_search_string
);
2751 /*for avoid infinite search loop we need to increase or decrease start offset of search */
2753 if (view
->search_start
)
2755 search_start
= (view
->search_backwards
) ? -2 : 2;
2756 search_start
= view
->search_start
+ search_start
+
2757 view__get_nroff_real_len(view
, view
->search_start
, 2) * search_start
;
2761 search_start
= view
->search_start
;
2764 if (view
->search_backwards
&& (int) search_start
< 0 )
2767 /* Compute the percent steps */
2768 search_update_steps (view
);
2769 view
->update_activate
= 0;
2771 tty_enable_interrupt_key ();
2775 if (view_find(view
, search_start
, &match_len
))
2777 view
->search_start
= view
->search
->normal_offset
+
2778 view__get_nroff_real_len(view
,
2779 view
->search
->start_buffer
,
2780 view
->search
->normal_offset
- view
->search
->start_buffer
);
2782 view
->search_end
= view
->search_start
+ match_len
+
2783 view__get_nroff_real_len(view
, view
->search_start
, match_len
+ 1);
2785 if (view
->hex_mode
){
2786 view
->hex_cursor
= view
->search_start
;
2787 view
->hexedit_lownibble
= FALSE
;
2788 view
->dpy_start
= view
->search_start
- view
->search_start
% view
->bytes_per_line
;
2789 view
->dpy_end
= view
->search_end
- view
->search_end
% view
->bytes_per_line
;
2795 d
= create_message (D_NORMAL
, _("Search"), _("Seeking to search result"));
2799 view_moveto_match (view
);
2803 } while (view_may_still_grow(view
));
2806 if (view
->search
->error_str
)
2807 message (D_NORMAL
, _("Search"), "%s", view
->search
->error_str
);
2813 tty_disable_interrupt_key ();
2824 view_search_cmd (WView
*view
)
2827 SEARCH_DLG_MIN_HEIGHT
= 10,
2828 SEARCH_DLG_HEIGHT_SUPPLY
= 3,
2829 SEARCH_DLG_WIDTH
= 58
2832 char *defval
= g_strdup (view
->last_search_string
!= NULL
? view
->last_search_string
: "");
2838 int ttype_of_search
= (int) view
->search_type
;
2839 int tall_codepages
= (int) view
->search_all_codepages
;
2840 int tsearch_case
= (int) view
->search_case
;
2841 int tsearch_backwards
= (int) view
->search_backwards
;
2843 gchar
**list_of_types
= mc_search_get_types_strings_array();
2844 int SEARCH_DLG_HEIGHT
= SEARCH_DLG_MIN_HEIGHT
+ g_strv_length (list_of_types
) - SEARCH_DLG_HEIGHT_SUPPLY
;
2846 QuickWidget quick_widgets
[] = {
2848 {quick_button
, 6, 10, SEARCH_DLG_HEIGHT
- 3, SEARCH_DLG_HEIGHT
, N_("&Cancel"), 0,
2849 B_CANCEL
, 0, 0, NULL
, NULL
, NULL
},
2851 {quick_button
, 2, 10, SEARCH_DLG_HEIGHT
- 3, SEARCH_DLG_HEIGHT
, N_("&OK"), 0, B_ENTER
,
2852 0, 0, NULL
, NULL
, NULL
},
2855 {quick_checkbox
, SEARCH_DLG_WIDTH
/2 + 3, SEARCH_DLG_WIDTH
, 6, SEARCH_DLG_HEIGHT
, N_("All charsets"), 0, 0,
2856 &tall_codepages
, 0, NULL
, NULL
, NULL
},
2859 {quick_checkbox
, SEARCH_DLG_WIDTH
/2 + 3, SEARCH_DLG_WIDTH
, 5, SEARCH_DLG_HEIGHT
,
2860 N_("&Backwards"), 0, 0, &tsearch_backwards
, 0, NULL
, NULL
, NULL
},
2862 {quick_checkbox
, SEARCH_DLG_WIDTH
/2 + 3, SEARCH_DLG_WIDTH
, 4, SEARCH_DLG_HEIGHT
, N_("case &Sensitive"), 0, 0,
2863 &tsearch_case
, 0, NULL
, NULL
, NULL
},
2865 {quick_radio
, 3, SEARCH_DLG_WIDTH
, 4, SEARCH_DLG_HEIGHT
, 0, g_strv_length (list_of_types
), ttype_of_search
,
2866 (void *) &ttype_of_search
, const_cast (char **, list_of_types
), NULL
, NULL
, NULL
},
2869 {quick_input
, 3, SEARCH_DLG_WIDTH
, 3, SEARCH_DLG_HEIGHT
, defval
, 52, 0,
2870 0, &exp
, MC_HISTORY_SHARED_SEARCH
, NULL
, NULL
},
2872 {quick_label
, 2, SEARCH_DLG_WIDTH
, 2, SEARCH_DLG_HEIGHT
,
2873 N_(" Enter search string:"), 0, 0, 0, 0, 0, NULL
, NULL
},
2878 QuickDialog Quick_input
= {
2879 SEARCH_DLG_WIDTH
, SEARCH_DLG_HEIGHT
, -1, 0, N_("Search"),
2880 "[Input Line Keys]", quick_widgets
, 0
2883 convert_to_display (defval
);
2886 if (quick_dialog (&Quick_input
) == B_CANCEL
)
2889 view
->search_backwards
= tsearch_backwards
;
2890 view
->search_type
= (mc_search_type_t
) ttype_of_search
;
2892 view
->search_all_codepages
= (gboolean
) tall_codepages
;
2893 view
->search_case
= (gboolean
) tsearch_case
;
2895 if (exp
== NULL
|| exp
[0] == '\0')
2899 tmp
= str_convert_to_input (exp
);
2904 exp
= g_string_free (tmp
, FALSE
);
2908 g_free (view
->last_search_string
);
2909 view
->last_search_string
= exp
;
2913 mc_search_free(view
->search
);
2915 view
->search
= mc_search_new(view
->last_search_string
, -1);
2919 view
->search
->search_type
= view
->search_type
;
2920 view
->search
->is_all_charsets
= view
->search_all_codepages
;
2921 view
->search
->is_case_sentitive
= view
->search_case
;
2922 view
->search
->search_fn
= view_search_cmd_callback
;
2923 view
->search
->update_fn
= view_search_update_cmd_callback
;
2933 view_toggle_magic_mode_cmd (WView
*view
)
2935 view_toggle_magic_mode (view
);
2940 view_toggle_nroff_mode_cmd (WView
*view
)
2942 view_toggle_nroff_mode (view
);
2947 view_quit_cmd (WView
*view
)
2949 if (view_ok_to_quit (view
))
2950 dlg_stop (view
->widget
.parent
);
2953 /* {{{ Miscellaneous functions }}} */
2955 /* Define labels and handlers for functional keys */
2957 view_labels (WView
*view
)
2960 Dlg_head
*h
= view
->widget
.parent
;
2962 buttonbar_set_label (h
, 1, Q_("ButtonBar|Help"), view_help_cmd
);
2964 my_define (h
, 10, Q_("ButtonBar|Quit"), view_quit_cmd
, view
);
2965 text
= view
->hex_mode
? "ButtonBar|Ascii" : "ButtonBar|Hex";
2966 my_define (h
, 4, Q_(text
), view_toggle_hex_mode_cmd
, view
);
2967 text
= view
->hex_mode
?"ButtonBar|Goto": "ButtonBar|Line";
2968 my_define (h
, 5, Q_(text
),
2969 view
->hex_mode
? view_moveto_addr_cmd
: view_moveto_line_cmd
, view
);
2971 if (view
->hex_mode
) {
2972 if (view
->hexedit_mode
) {
2973 my_define (h
, 2, Q_("ButtonBar|View"),
2974 view_toggle_hexedit_mode_cmd
, view
);
2975 } else if (view
->datasource
== DS_FILE
) {
2976 my_define (h
, 2, Q_("ButtonBar|Edit"),
2977 view_toggle_hexedit_mode_cmd
, view
);
2979 buttonbar_clear_label (h
, 2);
2981 my_define (h
, 6, Q_("ButtonBar|Save"),
2982 view_hexedit_save_changes_cmd
, view
);
2984 text
= view
->text_wrap_mode
? "ButtonBar|UnWrap" : "ButtonBar|Wrap";
2985 my_define (h
, 2, Q_(text
), view_toggle_wrap_mode_cmd
, view
);
2988 text
= view
->hex_mode
? "ButtonBar|HxSrch" : "ButtonBar|Search";
2989 my_define (h
, 7, Q_(text
), view_search_cmd
, view
);
2990 text
= view
->magic_mode
? "ButtonBar|Raw" : "ButtonBar|Parse";
2991 my_define (h
, 8, Q_(text
), view_toggle_magic_mode_cmd
, view
);
2993 /* don't override the key to access the main menu */
2994 if (!view_is_in_panel (view
)) {
2995 text
= view
->text_nroff_mode
? "ButtonBar|Unform" : "ButtonBar|Format";
2996 my_define (h
, 9, Q_(text
), view_toggle_nroff_mode_cmd
, view
);
2997 my_define (h
, 3, Q_("ButtonBar|Quit"), view_quit_cmd
, view
);
3001 /* {{{ Event handling }}} */
3003 /* Check for left and right arrows, possibly with modifiers */
3005 check_left_right_keys (WView
*view
, int c
)
3007 if (c
== KEY_LEFT
) {
3008 view_move_left (view
, 1);
3012 if (c
== KEY_RIGHT
) {
3013 view_move_right (view
, 1);
3017 /* Ctrl with arrows moves by 10 postions in the unwrap mode */
3018 if (view
->hex_mode
|| view
->text_wrap_mode
)
3019 return MSG_NOT_HANDLED
;
3021 if (c
== (KEY_M_CTRL
| KEY_LEFT
)) {
3022 if (view
->dpy_text_column
>= 10)
3023 view
->dpy_text_column
-= 10;
3025 view
->dpy_text_column
= 0;
3030 if (c
== (KEY_M_CTRL
| KEY_RIGHT
)) {
3031 if (view
->dpy_text_column
<= OFFSETTYPE_MAX
- 10)
3032 view
->dpy_text_column
+= 10;
3034 view
->dpy_text_column
= OFFSETTYPE_MAX
;
3039 return MSG_NOT_HANDLED
;
3042 /* {{{ User-definable commands }}} */
3045 view_continue_search_cmd (WView
*view
)
3047 if (view
->last_search_string
!= NULL
) {
3050 /* if not... then ask for an expression */
3051 view_search_cmd (view
);
3056 view_toggle_ruler_cmd (WView
*view
)
3058 static const enum ruler_type next
[3] = {
3064 assert ((size_t) ruler
< 3);
3065 ruler
= next
[(size_t) ruler
];
3069 /* {{{ Event handling }}} */
3071 static void view_cmk_move_up (void *w
, int n
) {
3072 view_move_up ((WView
*) w
, n
);
3074 static void view_cmk_move_down (void *w
, int n
) {
3075 view_move_down ((WView
*) w
, n
);
3077 static void view_cmk_moveto_top (void *w
, int n
) {
3079 view_moveto_top ((WView
*) w
);
3081 static void view_cmk_moveto_bottom (void *w
, int n
) {
3083 view_moveto_bottom ((WView
*) w
);
3087 view_select_encoding (WView
*view
)
3090 const char *enc
= NULL
;
3092 if (!do_select_codepage ())
3095 enc
= get_codepage_id (source_codepage
);
3099 conv
= str_crt_conv_from (enc
);
3100 if (conv
!= INVALID_CONV
) {
3101 if (view
->converter
!= str_cnv_from_term
)
3102 str_close_conv (view
->converter
);
3103 view
->converter
= conv
;
3106 if (enc
!= NULL
&& str_isutf8 (enc
) != 0)
3116 view_handle_key (WView
*view
, int c
)
3118 c
= convert_from_input_c (c
);
3120 if (view
->hex_mode
) {
3123 view
->hexview_in_text
= !view
->hexview_in_text
;
3128 view_moveto_bol (view
);
3133 view_move_left (view
, 1);
3137 view_moveto_eol (view
);
3141 view_move_right (view
, 1);
3145 if (view
->hexedit_mode
3146 && view_handle_editkey (view
, c
) == MSG_HANDLED
)
3150 if (check_left_right_keys (view
, c
))
3153 if (check_movement_keys (c
, view
->data_area
.height
+ 1, view
,
3154 view_cmk_move_up
, view_cmk_move_down
,
3155 view_cmk_moveto_top
, view_cmk_moveto_bottom
))
3162 view
->search_type
= MC_SEARCH_T_REGEX
;
3163 view_search_cmd(view
);
3166 /* Continue search */
3171 view_continue_search_cmd (view
);
3176 view_toggle_ruler_cmd (view
);
3180 view_move_left (view
, 1);
3186 view_move_down (view
, 1);
3190 view_move_down (view
, (view
->data_area
.height
+ 1) / 2);
3194 view_move_up (view
, (view
->data_area
.height
+ 1) / 2);
3199 view_move_up (view
, 1);
3203 view_move_right (view
, 1);
3208 view_move_down (view
, view
->data_area
.height
);
3215 /* Unlike Ctrl-O, run a new shell if the subshell is not running. */
3221 view_move_up (view
, view
->data_area
.height
);
3225 view_move_up (view
, 2);
3229 view_move_down (view
, 2);
3233 view
->marks
[view
->marker
] = view
->dpy_start
;
3237 view
->dpy_start
= view
->marks
[view
->marker
];
3241 /* Use to indicate parent that we want to see the next/previous file */
3242 /* Does not work in panel mode */
3245 if (!view_is_in_panel (view
))
3246 view
->move_dir
= c
== XCTRL ('f') ? 1 : -1;
3251 if (view_ok_to_quit (view
))
3252 view
->want_to_quit
= TRUE
;
3256 view_select_encoding (view
);
3261 #ifdef MC_ENABLE_DEBUGGING_CODE
3262 case 't': /* mnemonic: "test" */
3263 view_ccache_dump (view
);
3267 if (c
>= '0' && c
<= '9')
3268 view
->marker
= c
- '0';
3271 return MSG_NOT_HANDLED
;
3276 view_event (WView
*view
, Gpm_Event
*event
, int *result
)
3280 *result
= MOU_NORMAL
;
3282 /* We are not interested in the release events */
3283 if (!(event
->type
& (GPM_DOWN
| GPM_DRAG
)))
3287 if ((event
->buttons
& GPM_B_UP
) && (event
->type
& GPM_DOWN
)) {
3288 view_move_up (view
, 2);
3291 if ((event
->buttons
& GPM_B_DOWN
) && (event
->type
& GPM_DOWN
)) {
3292 view_move_down (view
, 2);
3299 /* Scrolling left and right */
3300 if (!view
->text_wrap_mode
) {
3301 if (x
< view
->data_area
.width
* 1/4) {
3302 view_move_left (view
, 1);
3304 } else if (x
< view
->data_area
.width
* 3/4) {
3305 /* ignore the click */
3307 view_move_right (view
, 1);
3312 /* Scrolling up and down */
3313 if (y
< view
->data_area
.top
+ view
->data_area
.height
* 1/3) {
3314 if (mouse_move_pages_viewer
)
3315 view_move_up (view
, view
->data_area
.height
/ 2);
3317 view_move_up (view
, 1);
3319 } else if (y
< view
->data_area
.top
+ view
->data_area
.height
* 2/3) {
3320 /* ignore the click */
3322 if (mouse_move_pages_viewer
)
3323 view_move_down (view
, view
->data_area
.height
/ 2);
3325 view_move_down (view
, 1);
3332 *result
= MOU_REPEAT
;
3336 /* Real view only */
3338 real_view_event (Gpm_Event
*event
, void *x
)
3340 WView
*view
= (WView
*) x
;
3343 if (view_event (view
, event
, &result
))
3349 view_adjust_size (Dlg_head
*h
)
3354 /* Look up the viewer and the buttonbar, we assume only two widgets here */
3355 view
= (WView
*) find_widget_type (h
, view_callback
);
3356 bar
= find_buttonbar (h
);
3357 widget_set_size (&view
->widget
, 0, 0, LINES
- 1, COLS
);
3358 widget_set_size ((Widget
*) bar
, LINES
- 1, 0, 1, COLS
);
3360 view_compute_areas (view
);
3361 view_update_bytes_per_line (view
);
3364 /* Callback for the view dialog */
3366 view_dialog_callback (Dlg_head
*h
, dlg_msg_t msg
, int parm
)
3370 view_adjust_size (h
);
3374 return default_dlg_callback (h
, msg
, parm
);
3378 /* {{{ External interface }}} */
3380 /* Real view only */
3382 mc_internal_viewer (const char *command
, const char *file
,
3383 int *move_dir_p
, int start_line
)
3390 /* Create dialog and widgets, put them on the dialog */
3392 create_dlg (0, 0, LINES
, COLS
, NULL
, view_dialog_callback
,
3393 "[Internal File Viewer]", NULL
, DLG_WANT_TAB
);
3395 wview
= view_new (0, 0, COLS
, LINES
- 1, 0);
3397 bar
= buttonbar_new (1);
3399 add_widget (view_dlg
, bar
);
3400 add_widget (view_dlg
, wview
);
3402 succeeded
= view_load (wview
, command
, file
, start_line
);
3406 *move_dir_p
= wview
->move_dir
;
3411 destroy_dlg (view_dlg
);
3416 /* {{{ Miscellaneous functions }}} */
3421 WView
*view
= (WView
*) v
;
3424 /* If the user is busy typing, wait until he finishes to update the
3427 if (!hook_present (idle_hook
, view_hook
))
3428 add_hook (&idle_hook
, view_hook
, v
);
3432 delete_hook (&idle_hook
, view_hook
);
3434 if (get_current_type () == view_listing
)
3435 panel
= current_panel
;
3436 else if (get_other_type () == view_listing
)
3437 panel
= other_panel
;
3441 view_load (view
, 0, panel
->dir
.list
[panel
->selected
].fname
, 0);
3445 /* {{{ Event handling }}} */
3448 view_callback (Widget
*w
, widget_msg_t msg
, int parm
)
3450 WView
*view
= (WView
*) w
;
3452 Dlg_head
*h
= view
->widget
.parent
;
3454 view_compute_areas (view
);
3455 view_update_bytes_per_line (view
);
3459 if (view_is_in_panel (view
))
3460 add_hook (&select_file_hook
, view_hook
, view
);
3462 view
->dpy_bbar_dirty
= TRUE
;
3471 view_place_cursor (view
);
3475 i
= view_handle_key ((WView
*) view
, parm
);
3476 if (view
->want_to_quit
&& !view_is_in_panel (view
))
3484 view
->dpy_bbar_dirty
= TRUE
;
3488 case WIDGET_DESTROY
:
3490 if (view_is_in_panel (view
))
3491 delete_hook (&select_file_hook
, view_hook
);
3495 return default_proc (msg
, parm
);
3499 /* {{{ External interface }}} */
3502 view_new (int y
, int x
, int cols
, int lines
, int is_panel
)
3504 WView
*view
= g_new0 (WView
, 1);
3507 init_widget (&view
->widget
, y
, x
, lines
, cols
,
3511 view
->filename
= NULL
;
3512 view
->command
= NULL
;
3514 view_set_datasource_none (view
);
3516 view
->growbuf_in_use
= FALSE
;
3517 /* leave the other growbuf fields uninitialized */
3519 view
->hex_mode
= FALSE
;
3520 view
->hexedit_mode
= FALSE
;
3521 view
->hexview_in_text
= FALSE
;
3522 view
->text_nroff_mode
= FALSE
;
3523 view
->text_wrap_mode
= FALSE
;
3524 view
->magic_mode
= FALSE
;
3527 view
->hexedit_lownibble
= FALSE
;
3528 view
->coord_cache
= NULL
;
3530 view
->dpy_frame_size
= is_panel
? 1 : 0;
3531 view
->dpy_start
= 0;
3532 view
->dpy_text_column
= 0;
3534 view
->hex_cursor
= 0;
3535 view
->cursor_col
= 0;
3536 view
->cursor_row
= 0;
3537 view
->change_list
= NULL
;
3538 view
->converter
= str_cnv_from_term
;
3540 /* {status,ruler,data}_area are left uninitialized */
3543 view
->dpy_bbar_dirty
= TRUE
;
3544 view
->bytes_per_line
= 1;
3546 view
->search_start
= 0;
3547 view
->search_end
= 0;
3549 view
->want_to_quit
= FALSE
;
3551 for (i
= 0; i
< sizeof(view
->marks
) / sizeof(view
->marks
[0]); i
++)
3555 view
->update_steps
= 0;
3556 view
->update_activate
= 0;
3558 if (default_hex_mode
)
3559 view_toggle_hex_mode (view
);
3560 if (default_nroff_flag
)
3561 view_toggle_nroff_mode (view
);
3562 if (global_wrap_mode
)
3563 view_toggle_wrap_mode (view
);
3564 if (default_magic_flag
)
3565 view_toggle_magic_mode (view
);