Warning fix: format not a string literal and no format arguments
[midnight-commander.git] / src / view.c
blob6ed6f9fc0d2e92c61efbbfa5e3c7e75572659503
1 /*
2 Internal file viewer for the Midnight Commander
4 Copyright (C) 1994, 1995, 1996 The Free Software Foundation
6 Written by: 1994, 1995, 1998 Miguel de Icaza
7 1994, 1995 Janne Kukonlehto
8 1995 Jakub Jelinek
9 1996 Joseph M. Hinkle
10 1997 Norbert Warmuth
11 1998 Pavel Machek
12 2004 Roland Illig <roland.illig@gmx.de>
13 2005 Roland Illig <roland.illig@gmx.de>
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation; either version 2 of the License, or
18 (at your option) any later version.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 /** \file view.c
31 * \brief Source: internal file viewer
34 #ifdef HAVE_CONFIG_H
35 # include <config.h>
36 #endif
38 #include <assert.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <unistd.h>
49 #include "global.h"
50 #include "tty.h"
51 #include "cmd.h" /* For view_other_cmd */
52 #include "dialog.h" /* Needed by widget.h */
53 #include "widget.h" /* Needed for buttonbar_new */
54 #include "color.h"
55 #include "mouse.h"
56 #include "help.h"
57 #include "key.h" /* For mi_getch() */
58 #include "layout.h"
59 #include "setup.h"
60 #include "wtools.h" /* For query_set_sel() */
61 #include "dir.h"
62 #include "panel.h" /* Needed for current_panel and other_panel */
63 #include "win.h"
64 #include "execute.h"
65 #include "main.h" /* slow_terminal */
66 #include "view.h"
67 #include "history.h"
68 #include "charsets.h"
69 #include "selcodepage.h"
70 #include "strutil.h"
71 #include "../src/search/search.h"
73 /* Block size for reading files in parts */
74 #define VIEW_PAGE_SIZE ((size_t) 8192)
76 typedef unsigned char byte;
78 /* Offset in bytes into a file */
79 typedef unsigned long offset_type;
80 #define INVALID_OFFSET ((offset_type) -1)
81 #define OFFSETTYPE_MAX (~((offset_type) 0))
82 #define OFFSETTYPE_PRIX "lX"
83 #define OFFSETTYPE_PRId "lu"
85 /* A width or height on the screen */
86 typedef unsigned int screen_dimen;
88 /* A node for building a change list on change_list */
89 struct hexedit_change_node {
90 struct hexedit_change_node *next;
91 offset_type offset;
92 byte value;
95 /* data sources of the view */
96 enum view_ds {
97 DS_NONE, /* No data available */
98 DS_STDIO_PIPE, /* Data comes from a pipe using popen/pclose */
99 DS_VFS_PIPE, /* Data comes from a piped-in VFS file */
100 DS_FILE, /* Data comes from a VFS file */
101 DS_STRING /* Data comes from a string in memory */
104 struct area {
105 screen_dimen top, left;
106 screen_dimen height, width;
109 #define VLF_DISCARD 1
110 #define VLF_INIT 2
111 #define VLF_COMPLETE 3
113 /* basic structure for caching text, it correspond to one line in wrapped text.
114 * That makes easier to move in wrap mode.
115 * cache_lines are stored in two linked lists, one for normal mode
116 * and one for nroff mode
117 * cache_line is valid of end is not INVALID_OFFSET
118 * last line has set width to (screen_dimen) (-1)*/
119 /* never access next and previous in cache_line directly, use appropriately function
120 * instead */
121 struct cache_line {
122 /* number of line in text, so two cache_line have same number
123 * if they are on same text line (text line is wider than screen) */
124 unsigned long number;
125 /* previous cache_line in list*/
126 struct cache_line *prev;
127 /* next cache_line in list*/
128 struct cache_line *next;
129 /* offset when cache_line start in text,
130 * cache_line.start = cache_line->prev.end */
131 offset_type start;
132 /* offset when cache_line ends
133 * cache_line.end = cache_line->next.start */
134 offset_type end;
135 /* how many column take on screen */
136 screen_dimen width;
137 /* correct ident, if prevoius line ends by tabulator */
138 screen_dimen left;
141 struct WView {
142 Widget widget;
144 char *filename; /* Name of the file */
145 char *command; /* Command used to pipe data in */
147 enum view_ds datasource; /* Where the displayed data comes from */
149 /* stdio pipe data source */
150 FILE *ds_stdio_pipe; /* Output of a shell command */
152 /* vfs pipe data source */
153 int ds_vfs_pipe; /* Non-seekable vfs file descriptor */
155 /* vfs file data source */
156 int ds_file_fd; /* File with random access */
157 off_t ds_file_filesize; /* Size of the file */
158 off_t ds_file_offset; /* Offset of the currently loaded data */
159 byte *ds_file_data; /* Currently loaded data */
160 size_t ds_file_datalen; /* Number of valid bytes in file_data */
161 size_t ds_file_datasize; /* Number of allocated bytes in file_data */
163 /* string data source */
164 byte *ds_string_data; /* The characters of the string */
165 size_t ds_string_len; /* The length of the string */
167 /* Growing buffers information */
168 gboolean growbuf_in_use; /* Use the growing buffers? */
169 byte **growbuf_blockptr; /* Pointer to the block pointers */
170 size_t growbuf_blocks; /* The number of blocks in *block_ptr */
171 size_t growbuf_lastindex; /* Number of bytes in the last page of the
172 growing buffer */
173 gboolean growbuf_finished; /* TRUE when all data has been read. */
175 /* Editor modes */
176 gboolean hex_mode; /* Hexview or Hexedit */
177 gboolean hexedit_mode; /* Hexedit */
178 gboolean hexview_in_text; /* Is the hexview cursor in the text area? */
179 gboolean text_nroff_mode; /* Nroff-style highlighting */
180 gboolean text_wrap_mode; /* Wrap text lines to fit them on the screen */
181 gboolean magic_mode; /* Preprocess the file using external programs */
183 /* Additional editor state */
184 gboolean hexedit_lownibble; /* Are we editing the last significant nibble? */
186 /* Display information */
187 screen_dimen dpy_frame_size;/* Size of the frame surrounding the real viewer */
188 offset_type dpy_start; /* Offset of the displayed data */
189 offset_type dpy_end; /* Offset after the displayed data */
190 offset_type dpy_text_column;/* Number of skipped columns in non-wrap
191 * text mode */
192 offset_type hex_cursor; /* Hexview cursor position in file */
193 screen_dimen cursor_col; /* Cursor column */
194 screen_dimen cursor_row; /* Cursor row */
195 struct hexedit_change_node *change_list; /* Linked list of changes */
196 struct area status_area; /* Where the status line is displayed */
197 struct area ruler_area; /* Where the ruler is displayed */
198 struct area data_area; /* Where the data is displayed */
200 int dirty; /* Number of skipped updates */
201 gboolean dpy_bbar_dirty; /* Does the button bar need to be updated? */
203 /* Mode variables */
204 int bytes_per_line; /* Number of bytes per line in hex mode */
206 /* Search variables */
207 offset_type search_start; /* First character to start searching from */
208 offset_type search_end; /* Length of found string or 0 if none was found */
210 /* Pointer to the last search command */
211 gboolean want_to_quit; /* Prepare for cleanup ... */
213 /* Markers */
214 int marker; /* mark to use */
215 offset_type marks [10]; /* 10 marks: 0..9 */
217 int move_dir; /* return value from widget:
218 * 0 do nothing
219 * -1 view previous file
220 * 1 view next file
223 offset_type update_steps; /* The number of bytes between percent
224 * increments */
225 offset_type update_activate;/* Last point where we updated the status */
227 /* never access cache_line in view directly, use appropriately function
228 * instead */
229 /* first cache_line for normal mode */
230 struct cache_line *lines;
231 /* last cache_line for normal mode, is set when text is read to the end */
232 struct cache_line *lines_end;
233 /* first cache_line for nroff mode */
234 struct cache_line *nroff_lines;
235 /* last cache_line for nroff mode, is set when text is read to the end */
236 struct cache_line *nroff_lines_end;
237 /* cache_line, that is first showed on display (something like cursor)
238 * used for both normal adn nroff mode */
239 struct cache_line *first_showed_line;
240 /* converter for translation of text */
241 GIConv converter;
243 /* handle of search engine */
244 mc_search_t *search;
245 gchar *last_search_string;
246 mc_search_type_t search_type;
247 gboolean search_all_codepages;
248 gboolean search_case;
249 gboolean search_backwards;
253 /* {{{ Global Variables }}} */
255 /* Maxlimit for skipping updates */
256 int max_dirt_limit = 10;
258 /* If set, show a ruler */
259 static enum ruler_type {
260 RULER_NONE,
261 RULER_TOP,
262 RULER_BOTTOM
263 } ruler = RULER_NONE;
265 /* Scrolling is done in pages or line increments */
266 int mouse_move_pages_viewer = 1;
268 /* wrap mode default */
269 int global_wrap_mode = 1;
271 int default_hex_mode = 0;
272 int default_magic_flag = 1;
273 int default_nroff_flag = 1;
274 int altered_hex_mode = 0;
275 int altered_magic_flag = 0;
276 int altered_nroff_flag = 0;
278 static const char hex_char[] = "0123456789ABCDEF";
280 int mcview_remember_file_position = FALSE;
282 /* {{{ Function Prototypes }}} */
284 /* Our widget callback */
285 static cb_ret_t view_callback (Widget *, widget_msg_t, int);
287 static void view_labels (WView * view);
289 static void view_init_growbuf (WView *);
290 static void view_place_cursor (WView *view);
291 static void display (WView *);
292 static void view_done (WView *);
294 /* {{{ Helper Functions }}} */
296 /* difference or zero */
297 static inline screen_dimen
298 dimen_doz (screen_dimen a, screen_dimen b)
300 return (a >= b) ? a - b : 0;
303 static inline screen_dimen
304 dimen_min (screen_dimen a, screen_dimen b)
306 return (a < b) ? a : b;
309 static inline offset_type
310 offset_doz (offset_type a, offset_type b)
312 return (a >= b) ? a - b : 0;
315 static inline offset_type
316 offset_rounddown (offset_type a, offset_type b)
318 assert (b != 0);
319 return a - a % b;
322 /* {{{ Simple Primitive Functions for WView }}} */
324 static inline gboolean
325 view_is_in_panel (WView *view)
327 return (view->dpy_frame_size != 0);
330 static inline int get_byte (WView *view, offset_type offset);
332 /* read on character from text, character is translated into terminal encoding
333 * writes result in ch, return how many bytes was read or -1 if end of text */
334 static int
335 view_get_char (WView *view, offset_type from, char *ch, int size)
337 static char buffer [MB_LEN_MAX + 1];
338 static int c;
339 static size_t result;
341 result = 0;
343 while (result < sizeof (buffer) - 1) {
344 c = get_byte (view, from + result);
345 if (c == -1) break;
346 buffer[result] = (unsigned char) c;
347 result++;
348 buffer[result] = '\0';
349 switch (str_translate_char (view->converter, buffer, result, ch, size)) {
350 case ESTR_SUCCESS:
351 return (int) result;
352 case ESTR_PROBLEM:
353 break;
354 case ESTR_FAILURE:
355 ch[0] = '?';
356 ch[1] = '\0';
357 return 1;
360 return -1;
363 /* this structure and view_read_* functions make reading text simpler
364 * view need remeber 4 following charsets: actual, next and two previous */
365 struct read_info {
366 char ch[4][MB_LEN_MAX + 1];
367 char *cnxt;
368 char *cact;
369 char *chi1;
370 char *chi2;
371 offset_type next;
372 offset_type actual;
373 int result;
376 /* set read_info into initial state and read first character to cnxt
377 * return how many bytes was read or -1 if end of text */
378 static void
379 view_read_start (WView *view, struct read_info *info, offset_type from)
381 info->ch[0][0] = '\0';
382 info->ch[1][0] = '\0';
383 info->ch[2][0] = '\0';
384 info->ch[3][0] = '\0';
386 info->cnxt = info->ch[0];
387 info->cact = info->ch[1];
388 info->chi1 = info->ch[2];
389 info->chi2 = info->ch[3];
391 info->next = from;
393 info->result = view_get_char (view, info->next, info->cnxt, MB_LEN_MAX + 1);
396 /* move characters in read_info one forward (chi2 = last previous is forgotten)
397 * read additional charsets into cnxt
398 * return how many bytes was read or -1 if end of text */
399 static void
400 view_read_continue (WView *view, struct read_info *info)
402 char *tmp;
404 tmp = info->chi2;
405 info->chi2 = info->chi1;
406 info->chi1 = info->cact;
407 info->cact = info->cnxt;
408 info->cnxt = tmp;
410 info->actual = info->next;
411 info->next+= info->result;
413 info->result = view_get_char (view, info->next, info->cnxt, MB_LEN_MAX + 1);
416 #define VRT_NW_NO 0
417 #define VRT_NW_YES 1
418 #define VRT_NW_CONTINUE 2
420 /* test if cact of read_info is newline
421 * return VRT_NW_NO, VRT_NW_YES
422 * or VRT_NW_CONTINUE follow after cact ("\r\n", ...) */
423 static int
424 view_read_test_new_line (WView *view, struct read_info *info)
426 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
427 (void) view;
428 if (cmp (info->cact, "\n")) return VRT_NW_YES;
430 if (cmp (info->cact, "\r")) {
431 if (info->result != -1 && (cmp (info->cnxt, "\r") || cmp (info->cnxt, "\n")))
432 return VRT_NW_CONTINUE;
434 return VRT_NW_YES;
436 return VRT_NW_NO;
439 /* test if cact of read_info is tabulator
440 * return VRT_NW_NO or VRT_NW_YES */
441 static int
442 view_read_test_tabulator (WView *view, struct read_info *info)
444 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
445 (void) view;
446 return cmp (info->cact, "\t");
449 /* test if read_info.cact is '\b' and charsets in read_info are correct
450 * nroff sequence, return VRT_NW_NO or VRT_NW_YES */
451 static int
452 view_read_test_nroff_back (WView *view, struct read_info *info)
454 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
455 return view->text_nroff_mode && cmp (info->cact, "\b") &&
456 (info->result != -1) && str_isprint (info->cnxt) &&
457 !cmp (info->chi1, "") && str_isprint (info->chi1) &&
458 (cmp (info->chi1, info->cnxt) || cmp (info->chi1, "_")
459 || (cmp (info->chi1, "+") && cmp (info->cnxt, "o")));
463 /* rutine for view_load_cache_line, line is ended and following cache_line is
464 * set up for loading
465 * used when end of line has been read in text */
466 static struct cache_line *
467 view_lc_create_next_line (struct cache_line *line, offset_type start)
469 struct cache_line *result = NULL;
471 line->end = start;
473 if (line->next == NULL) {
474 result = g_new0 (struct cache_line, 1);
475 line->next = result;
476 result->prev = line;
477 } else result = line->next;
479 result->start = start;
480 result->width = 0;
481 result->left = 0;
482 result->number = line->number + 1;
483 result->end = INVALID_OFFSET;
485 return result;
488 /* rutine for view_load_cache_line, line is ended and following cache_line is
489 * set up for loading
490 * used when line has maximum width */
491 static struct cache_line *
492 view_lc_create_wrap_line (struct cache_line *line, offset_type start,
493 screen_dimen left)
495 struct cache_line *result = NULL;
497 line->end = start;
499 if (line->next == NULL) {
500 result = g_new0 (struct cache_line, 1);
501 line->next = result;
502 result->prev = line;
503 } else result = line->next;
505 result->start = start;
506 result->width = 0;
507 result->left = left;
508 result->number = line->number;
509 result->end = INVALID_OFFSET;
511 return result;
514 /* read charsets fro text and set up correct width, end and start of next line
515 * view->data_area.width must be greater than 0 */
516 static struct cache_line *
517 view_load_cache_line (WView *view, struct cache_line *line)
519 const screen_dimen width = view->data_area.width;
520 struct read_info info;
521 offset_type nroff_start = 0;
522 int nroff_seq = 0;
523 gsize w;
525 line->width = 0;
527 view_read_start (view, &info, line->start);
528 while (info.result != -1) {
529 view_read_continue (view, &info);
531 switch (view_read_test_new_line (view, &info)) {
532 case VRT_NW_YES:
533 line = view_lc_create_next_line (line, info.next);
534 return line;
535 case VRT_NW_CONTINUE:
536 continue;
539 if (view_read_test_tabulator (view, &info)) {
540 line->width+= 8 - (line->left + line->width) % 8;
541 if ((width != 0) && (line->left + line->width >= width)) {
542 w = line->left + line->width - width;
543 /* if width of screen is very small, tabulator is cut to line
544 * left of next line is 0 */
545 w = (w < width) ? w : 0;
546 line->width = width - line->left;
547 line = view_lc_create_wrap_line (line, info.next, w);
548 return line;
552 if (view_read_test_nroff_back (view, &info)) {
553 w = str_term_width1 (info.chi1);
554 line->width-= w;
555 nroff_seq = 1;
556 continue;
558 /* assure, that nroff sequence never start in previous cache_line */
559 if (nroff_seq > 0)
560 nroff_seq--;
561 else
562 nroff_start = info.actual;
564 w = str_isprint (info.cact) ? str_term_width1 (info.cact) : 1;
566 if (line->left + line->width + w > width) {
567 line = view_lc_create_wrap_line (line, nroff_start, 0);
568 return line;
569 } else {
570 while (info.result != -1 && str_iscombiningmark (info.cnxt)) {
571 view_read_continue (view, &info);
574 line->width+= w;
578 /* text read to the end, seting lines_end*/
579 if (view->text_nroff_mode)
580 view->nroff_lines_end = line;
581 else
582 view->lines_end = line;
584 line = view_lc_create_next_line (line, info.next);
585 line->width = (screen_dimen) (-1);
586 line->end = info.next;
588 return line;
591 static void
592 view_lc_set_param (offset_type *param, offset_type value)
594 if ((*param) > value) (*param) = value;
597 /* offset to column or column to offset, value that will be maped must be
598 * set to INVALID_OFFSET, it is very analogous to view_load_cache_line
599 * and it is possible to integrate them in one function */
600 static void
601 view_map_offset_and_column (WView *view, struct cache_line *line,
602 offset_type *column, offset_type *offset)
604 const screen_dimen width = view->data_area.width;
605 struct read_info info;
606 offset_type nroff_start = 0;
607 int nroff_seq = 0;
608 gsize w;
609 screen_dimen col;
611 /* HACK: valgrind screams here.
612 * TODO: to figure out WHY valgrind detects uninitialized
613 * variables. Maybe, info isn't fully initialized?
615 memset (&info, 0, sizeof info);
617 col = 0;
618 view_read_start (view, &info, line->start);
619 while (info.result != -1) {
620 view_read_continue (view, &info);
622 if (*column == INVALID_OFFSET) {
623 if (*offset < info.next) {
624 (*column) = col + line->left;
625 return;
629 switch (view_read_test_new_line (view, &info)) {
630 case VRT_NW_YES:
631 view_lc_set_param (offset, info.actual);
632 view_lc_set_param (column, line->left + col);
633 return;
634 case VRT_NW_CONTINUE:
635 continue;
638 if (view_read_test_tabulator (view, &info)) {
639 col+= 8 - (line->left + col) % 8;
640 if ((width != 0) && (line->left + col >= width)) {
641 w = line->left + col - width;
642 w = (w < width) ? w : 0;
643 col = width - line->left;
644 view_lc_set_param (offset, info.actual);
645 view_lc_set_param (column, line->left + col);
646 return;
650 if (view_read_test_nroff_back (view, &info)) {
651 w = str_term_width1 (info.chi1);
652 col-= w;
653 nroff_seq = 1;
654 continue;
656 if (nroff_seq > 0)
657 nroff_seq--;
658 else
659 nroff_start = info.actual;
661 w = str_isprint (info.cact) ? str_term_width1 (info.cact) : 1;
663 if (line->left + col + w > width) {
664 view_lc_set_param (offset, nroff_start);
665 view_lc_set_param (column, line->left + col);
666 return;
667 } else {
668 while (info.result != -1 && str_iscombiningmark (info.cnxt)) {
669 view_read_continue (view, &info);
673 col+= w;
675 if (*offset == INVALID_OFFSET) {
676 if (*column < col + line->left) {
677 (*offset) = nroff_start;
678 return;
683 view_lc_set_param (offset, info.actual);
684 view_lc_set_param (column, line->left + col);
687 /* macro, that iterate cache_line until stop holds,
688 * nf is function to iterate cache_line
689 * l is line to iterate and t is temporary line only */
690 #define view_move_to_stop(l,t,stop,nf) \
691 while (((t = nf (view, l)) != NULL) && (stop)) l = t;
693 /* make all cache_lines invalidet */
694 static void
695 view_reset_cache_lines (WView *view)
697 if (view->lines != NULL) {
698 view->lines->end = INVALID_OFFSET;
700 view->lines_end = NULL;
702 if (view->nroff_lines != NULL) {
703 view->nroff_lines->end = INVALID_OFFSET;
705 view->nroff_lines_end = NULL;
707 view->first_showed_line = NULL;
710 #define MAX_UNLOADED_CACHE_LINE 100
712 /* free some cache_lines, if count of cache_lines is bigger
713 * than MAX_UNLOADED_CACHE_LINE */
714 static void
715 view_reduce_cache_lines (WView *view)
717 struct cache_line *line;
718 struct cache_line *next;
719 int li;
721 li = 0;
722 line = view->lines;
723 while (line != NULL && li < MAX_UNLOADED_CACHE_LINE) {
724 line = line->next;
725 li++;
727 if (line != NULL) line->prev->next = NULL;
728 while (line != NULL) {
729 next = line->next;
730 g_free (line);
731 line = next;
733 view->lines_end = NULL;
735 line = view->nroff_lines;
736 while (line != NULL && li < MAX_UNLOADED_CACHE_LINE) {
737 line = line->next;
738 li++;
740 if (line != NULL) line->prev->next = NULL;
741 while (line != NULL) {
742 next = line->next;
743 g_free (line);
744 line = next;
746 view->nroff_lines_end = NULL;
748 view->first_showed_line = NULL;
751 /* return first cache_line for actual mode (normal / nroff) */
752 static struct cache_line *
753 view_get_first_line (WView *view)
755 struct cache_line **first_line;
757 first_line = (view->text_nroff_mode) ?
758 &(view->nroff_lines) : &(view->lines);
760 if (*first_line == NULL) {
761 (*first_line) = g_new0 (struct cache_line, 1);
762 (*first_line)->end = INVALID_OFFSET;
765 if ((*first_line)->end == INVALID_OFFSET)
766 view_load_cache_line (view, *first_line);
768 return (*first_line);
771 /* return following chahe_line or NULL */
772 static struct cache_line*
773 view_get_next_line (WView *view, struct cache_line *line)
775 struct cache_line *result;
777 if (line->next != NULL) {
778 result = line->next;
780 if (result->end == INVALID_OFFSET)
781 view_load_cache_line (view, result);
783 return (result->width != (screen_dimen) (-1)) ? result : NULL;
785 return NULL;
788 /* return last cache_line, it could take same time, because whole read text must
789 * be read (only once) */
790 static struct cache_line *
791 view_get_last_line (WView *view)
793 struct cache_line *result;
794 struct cache_line *next;
796 result = (view->text_nroff_mode) ?
797 view->nroff_lines_end : view->nroff_lines_end;
799 if (result != NULL) return result;
801 if (view->first_showed_line == NULL)
802 view->first_showed_line = view_get_first_line (view);
804 result = view->first_showed_line;
805 next = view_get_next_line (view, result);
806 while (next != NULL) {
807 result = next;
808 next = view_get_next_line (view, result);
810 return result;
813 /* return previous cache_line or NULL */
814 static struct cache_line *
815 view_get_previous_line (WView *view, struct cache_line *line)
817 (void) view;
818 return line->prev;
821 /* return first displayed cache_line */
822 static struct cache_line *
823 view_get_first_showed_line (WView *view)
825 struct cache_line *result;
827 if (view->first_showed_line == NULL)
828 view->first_showed_line = view_get_first_line (view);
830 result = view->first_showed_line;
831 return result;
834 /* return first cache_line with same number as line */
835 static struct cache_line *
836 view_get_start_of_whole_line (WView *view, struct cache_line *line)
838 struct cache_line *t;
840 if (line != NULL) {
841 view_move_to_stop (line, t, t->number == line->number, view_get_previous_line)
842 return line;
844 return NULL;
847 /* return last cache_line with same number as line */
848 static struct cache_line *
849 view_get_end_of_whole_line (WView *view, struct cache_line *line)
851 struct cache_line *t;
853 if (line != NULL) {
854 view_move_to_stop (line, t, t->number == line->number, view_get_next_line)
855 return line;
857 return NULL;
860 /* return last cache_line, that has number lesser than line
861 * or NULL */
862 static struct cache_line *
863 view_get_previous_whole_line (WView *view, struct cache_line *line)
865 line = view_get_start_of_whole_line (view, line);
866 return view_get_previous_line (view, line);
869 /* return first cache_line, that has number greater than line
870 * or NULL */
871 static struct cache_line *
872 view_get_next_whole_line (WView *view, struct cache_line *line)
874 line = view_get_end_of_whole_line (view, line);
875 return view_get_next_line (view, line);
878 /* return sum of widths of all cache_lines that has same number as line */
879 static screen_dimen
880 view_width_of_whole_line (WView *view, struct cache_line *line)
882 struct cache_line *next;
883 screen_dimen result = 0;
885 line = view_get_start_of_whole_line (view, line);
886 next = view_get_next_line (view, line);
887 while ((next != NULL) && (next->number == line->number)) {
888 result+= line->left + line->width;
889 line = next;
890 next = view_get_next_line (view, line);
892 result+= line->left + line->width;
893 return result;
896 /* return sum of widths of cache_lines before line, that has same number as line */
897 static screen_dimen
898 view_width_of_whole_line_before (WView *view, struct cache_line *line)
900 struct cache_line *next;
901 screen_dimen result = 0;
903 next = view_get_start_of_whole_line (view, line);
904 while (next != line) {
905 result+= next->left + next->width;
906 next = view_get_next_line (view, next);
908 return result;
911 /* map column to offset and cache_line */
912 static offset_type
913 view_column_to_offset (WView *view, struct cache_line **line, offset_type column)
915 struct cache_line *next;
916 offset_type result;
918 *line = view_get_start_of_whole_line (view, *line);
920 while (column >= (*line)->left + (*line)->width) {
921 column-= (*line)->left + (*line)->width;
922 result = (*line)->end;
923 next = view_get_next_line (view, *line);
924 if ((next == NULL) || (next->number != (*line)->number)) break;
925 (*line) = next;
927 /* if (column < (*line)->left + (*line)->width) { */
928 result = INVALID_OFFSET,
929 view_map_offset_and_column (view, *line, &column, &result);
930 /* } */
931 return result;
934 /* map offset to cache_line */
935 static struct cache_line *
936 view_offset_to_line (WView *view, offset_type from)
938 struct cache_line *result;
939 struct cache_line *t;
941 result = view_get_first_line (view);
943 view_move_to_stop (result, t, result->end <= from, view_get_next_line)
944 return result;
947 /* map offset to cache_line, searching starts from line */
948 static struct cache_line *
949 view_offset_to_line_from (WView *view, offset_type from, struct cache_line *line)
951 struct cache_line *result;
952 struct cache_line *t;
954 result = line;
956 view_move_to_stop (result, t, result->start > from, view_get_previous_line)
957 view_move_to_stop (result, t, result->end <= from, view_get_next_line)
959 return result;
962 /* mam offset to column */
963 static screen_dimen
964 view_offset_to_column (WView *view, struct cache_line *line, offset_type from)
966 offset_type result = INVALID_OFFSET;
968 view_map_offset_and_column (view, line, &result, &from);
970 result+= view_width_of_whole_line_before (view, line);
972 return result;
975 static void
976 view_compute_areas (WView *view)
978 struct area view_area;
979 struct cache_line *next;
980 screen_dimen height, rest, y;
981 screen_dimen old_width;
983 old_width = view->data_area.width;
985 /* The viewer is surrounded by a frame of size view->dpy_frame_size.
986 * Inside that frame, there are: The status line (at the top),
987 * the data area and an optional ruler, which is shown above or
988 * below the data area. */
990 view_area.top = view->dpy_frame_size;
991 view_area.left = view->dpy_frame_size;
992 view_area.height = dimen_doz(view->widget.lines, 2 * view->dpy_frame_size);
993 view_area.width = dimen_doz(view->widget.cols, 2 * view->dpy_frame_size);
995 /* Most coordinates of the areas equal those of the whole viewer */
996 view->status_area = view_area;
997 view->ruler_area = view_area;
998 view->data_area = view_area;
1000 /* Compute the heights of the areas */
1001 rest = view_area.height;
1003 height = dimen_min(rest, 1);
1004 view->status_area.height = height;
1005 rest -= height;
1007 height = dimen_min(rest, (ruler == RULER_NONE || view->hex_mode) ? 0 : 2);
1008 view->ruler_area.height = height;
1009 rest -= height;
1011 view->data_area.height = rest;
1013 /* Compute the position of the areas */
1014 y = view_area.top;
1016 view->status_area.top = y;
1017 y += view->status_area.height;
1019 if (ruler == RULER_TOP) {
1020 view->ruler_area.top = y;
1021 y += view->ruler_area.height;
1024 view->data_area.top = y;
1025 y += view->data_area.height;
1027 if (ruler == RULER_BOTTOM) {
1028 view->ruler_area.top = y;
1029 y += view->ruler_area.height;
1032 if (old_width != view->data_area.width) {
1033 view_reset_cache_lines (view);
1034 view->first_showed_line = view_get_first_line (view);
1035 next = view_get_next_line (view, view->first_showed_line);
1036 while ((next != NULL) && (view->first_showed_line->end <= view->dpy_start)) {
1037 view->first_showed_line = next;
1038 next = view_get_next_line (view, view->first_showed_line);
1040 view->dpy_start = view->first_showed_line->start;
1044 static void
1045 view_hexedit_free_change_list (WView *view)
1047 struct hexedit_change_node *curr, *next;
1049 for (curr = view->change_list; curr != NULL; curr = next) {
1050 next = curr->next;
1051 g_free (curr);
1053 view->change_list = NULL;
1054 view->dirty++;
1057 /* {{{ Growing buffer }}} */
1059 static void
1060 view_init_growbuf (WView *view)
1062 view->growbuf_in_use = TRUE;
1063 view->growbuf_blockptr = NULL;
1064 view->growbuf_blocks = 0;
1065 view->growbuf_lastindex = VIEW_PAGE_SIZE;
1066 view->growbuf_finished = FALSE;
1069 static void
1070 view_growbuf_free (WView *view)
1072 size_t i;
1074 assert (view->growbuf_in_use);
1076 for (i = 0; i < view->growbuf_blocks; i++)
1077 g_free (view->growbuf_blockptr[i]);
1078 g_free (view->growbuf_blockptr);
1079 view->growbuf_blockptr = NULL;
1080 view->growbuf_in_use = FALSE;
1083 static offset_type
1084 view_growbuf_filesize (WView *view)
1086 assert(view->growbuf_in_use);
1088 if (view->growbuf_blocks == 0)
1089 return 0;
1090 else
1091 return ((offset_type) view->growbuf_blocks - 1) * VIEW_PAGE_SIZE
1092 + view->growbuf_lastindex;
1095 /* Copies the output from the pipe to the growing buffer, until either
1096 * the end-of-pipe is reached or the interval [0..ofs) of the growing
1097 * buffer is completely filled. */
1098 static void
1099 view_growbuf_read_until (WView *view, offset_type ofs)
1101 ssize_t nread;
1102 byte *p;
1103 size_t bytesfree;
1104 gboolean short_read;
1106 assert (view->growbuf_in_use);
1108 if (view->growbuf_finished)
1109 return;
1111 short_read = FALSE;
1112 while (view_growbuf_filesize (view) < ofs || short_read) {
1113 if (view->growbuf_lastindex == VIEW_PAGE_SIZE) {
1114 /* Append a new block to the growing buffer */
1115 byte *newblock = g_try_malloc (VIEW_PAGE_SIZE);
1116 byte **newblocks = g_try_malloc (sizeof (*newblocks) * (view->growbuf_blocks + 1));
1117 if (!newblock || !newblocks) {
1118 g_free (newblock);
1119 g_free (newblocks);
1120 return;
1122 memcpy (newblocks, view->growbuf_blockptr, sizeof (*newblocks) * view->growbuf_blocks);
1123 g_free (view->growbuf_blockptr);
1124 view->growbuf_blockptr = newblocks;
1125 view->growbuf_blockptr[view->growbuf_blocks++] = newblock;
1126 view->growbuf_lastindex = 0;
1128 p = view->growbuf_blockptr[view->growbuf_blocks - 1] + view->growbuf_lastindex;
1129 bytesfree = VIEW_PAGE_SIZE - view->growbuf_lastindex;
1131 if (view->datasource == DS_STDIO_PIPE) {
1132 nread = fread (p, 1, bytesfree, view->ds_stdio_pipe);
1133 if (nread == 0) {
1134 view->growbuf_finished = TRUE;
1135 (void) pclose (view->ds_stdio_pipe);
1136 display (view);
1137 close_error_pipe (D_NORMAL, NULL);
1138 view->ds_stdio_pipe = NULL;
1139 return;
1141 } else {
1142 assert (view->datasource == DS_VFS_PIPE);
1143 do {
1144 nread = mc_read (view->ds_vfs_pipe, p, bytesfree);
1145 } while (nread == -1 && errno == EINTR);
1146 if (nread == -1 || nread == 0) {
1147 view->growbuf_finished = TRUE;
1148 (void) mc_close (view->ds_vfs_pipe);
1149 view->ds_vfs_pipe = -1;
1150 return;
1153 short_read = ((size_t)nread < bytesfree);
1154 view->growbuf_lastindex += nread;
1158 static int
1159 get_byte_growing_buffer (WView *view, offset_type byte_index)
1161 offset_type pageno = byte_index / VIEW_PAGE_SIZE;
1162 offset_type pageindex = byte_index % VIEW_PAGE_SIZE;
1164 assert (view->growbuf_in_use);
1166 if ((size_t) pageno != pageno)
1167 return -1;
1169 view_growbuf_read_until (view, byte_index + 1);
1170 if (view->growbuf_blocks == 0)
1171 return -1;
1172 if (pageno < view->growbuf_blocks - 1)
1173 return view->growbuf_blockptr[pageno][pageindex];
1174 if (pageno == view->growbuf_blocks - 1 && pageindex < view->growbuf_lastindex)
1175 return view->growbuf_blockptr[pageno][pageindex];
1176 return -1;
1179 /* {{{ Data sources }}} */
1182 The data source provides the viewer with data from either a file, a
1183 string or the output of a command. The get_byte() function can be
1184 used to get the value of a byte at a specific offset. If the offset
1185 is out of range, -1 is returned. The function get_byte_indexed(a,b)
1186 returns the byte at the offset a+b, or -1 if a+b is out of range.
1188 The view_set_byte() function has the effect that later calls to
1189 get_byte() will return the specified byte for this offset. This
1190 function is designed only for use by the hexedit component after
1191 saving its changes. Inspect the source before you want to use it for
1192 other purposes.
1194 The view_get_filesize() function returns the current size of the
1195 data source. If the growing buffer is used, this size may increase
1196 later on. Use the view_may_still_grow() function when you want to
1197 know if the size can change later.
1200 static offset_type
1201 view_get_filesize (WView *view)
1203 switch (view->datasource) {
1204 case DS_NONE:
1205 return 0;
1206 case DS_STDIO_PIPE:
1207 case DS_VFS_PIPE:
1208 return view_growbuf_filesize (view);
1209 case DS_FILE:
1210 return view->ds_file_filesize;
1211 case DS_STRING:
1212 return view->ds_string_len;
1213 default:
1214 assert(!"Unknown datasource type");
1215 return 0;
1219 static inline gboolean
1220 view_may_still_grow (WView *view)
1222 return (view->growbuf_in_use && !view->growbuf_finished);
1225 /* returns TRUE if the idx lies in the half-open interval
1226 * [offset; offset + size), FALSE otherwise.
1228 static inline gboolean
1229 already_loaded (offset_type offset, offset_type idx, size_t size)
1231 return (offset <= idx && idx - offset < size);
1234 static inline void
1235 view_file_load_data (WView *view, offset_type byte_index)
1237 offset_type blockoffset;
1238 ssize_t res;
1239 size_t bytes_read;
1241 assert (view->datasource == DS_FILE);
1243 if (already_loaded (view->ds_file_offset, byte_index, view->ds_file_datalen))
1244 return;
1246 if (byte_index >= view->ds_file_filesize)
1247 return;
1249 blockoffset = offset_rounddown (byte_index, view->ds_file_datasize);
1250 if (mc_lseek (view->ds_file_fd, blockoffset, SEEK_SET) == -1)
1251 goto error;
1253 bytes_read = 0;
1254 while (bytes_read < view->ds_file_datasize) {
1255 res = mc_read (view->ds_file_fd, view->ds_file_data + bytes_read, view->ds_file_datasize - bytes_read);
1256 if (res == -1)
1257 goto error;
1258 if (res == 0)
1259 break;
1260 bytes_read += (size_t) res;
1262 view->ds_file_offset = blockoffset;
1263 if (bytes_read > view->ds_file_filesize - view->ds_file_offset) {
1264 /* the file has grown in the meantime -- stick to the old size */
1265 view->ds_file_datalen = view->ds_file_filesize - view->ds_file_offset;
1266 } else {
1267 view->ds_file_datalen = bytes_read;
1269 return;
1271 error:
1272 view->ds_file_datalen = 0;
1275 static int
1276 get_byte_none (WView *view, offset_type byte_index)
1278 assert (view->datasource == DS_NONE);
1279 (void) &view;
1280 (void) byte_index;
1281 return -1;
1284 static inline int
1285 get_byte_file (WView *view, offset_type byte_index)
1287 assert (view->datasource == DS_FILE);
1289 view_file_load_data (view, byte_index);
1290 if (already_loaded(view->ds_file_offset, byte_index, view->ds_file_datalen))
1291 return view->ds_file_data[byte_index - view->ds_file_offset];
1292 return -1;
1295 static int
1296 get_byte_string (WView *view, offset_type byte_index)
1298 assert (view->datasource == DS_STRING);
1299 if (byte_index < view->ds_string_len)
1300 return view->ds_string_data[byte_index];
1301 return -1;
1304 static inline int
1305 get_byte (WView *view, offset_type offset)
1307 switch (view->datasource) {
1308 case DS_STDIO_PIPE:
1309 case DS_VFS_PIPE:
1310 return get_byte_growing_buffer (view, offset);
1311 case DS_FILE:
1312 return get_byte_file (view, offset);
1313 case DS_STRING:
1314 return get_byte_string (view, offset);
1315 case DS_NONE:
1316 return get_byte_none (view, offset);
1318 assert(!"Unknown datasource type");
1319 return -1;
1322 static inline int
1323 get_byte_indexed (WView *view, offset_type base, offset_type ofs)
1325 if (base <= OFFSETTYPE_MAX - ofs)
1326 return get_byte (view, base + ofs);
1327 return -1;
1330 static void
1331 view_set_byte (WView *view, offset_type offset, byte b)
1333 (void) &b;
1334 assert (offset < view_get_filesize (view));
1335 assert (view->datasource == DS_FILE);
1336 view->ds_file_datalen = 0; /* just force reloading */
1339 static void
1340 view_set_datasource_none (WView *view)
1342 view->datasource = DS_NONE;
1345 static void
1346 view_set_datasource_vfs_pipe (WView *view, int fd)
1348 assert (fd != -1);
1349 view->datasource = DS_VFS_PIPE;
1350 view->ds_vfs_pipe = fd;
1352 view_init_growbuf (view);
1355 static void
1356 view_set_datasource_stdio_pipe (WView *view, FILE *fp)
1358 assert (fp != NULL);
1359 view->datasource = DS_STDIO_PIPE;
1360 view->ds_stdio_pipe = fp;
1362 view_init_growbuf (view);
1365 static void
1366 view_set_datasource_string (WView *view, const char *s)
1368 view->datasource = DS_STRING;
1369 view->ds_string_data = (byte *) g_strdup (s);
1370 view->ds_string_len = strlen (s);
1373 static void
1374 view_set_datasource_file (WView *view, int fd, const struct stat *st)
1376 view->datasource = DS_FILE;
1377 view->ds_file_fd = fd;
1378 view->ds_file_filesize = st->st_size;
1379 view->ds_file_offset = 0;
1380 view->ds_file_data = g_malloc (4096);
1381 view->ds_file_datalen = 0;
1382 view->ds_file_datasize = 4096;
1385 static void
1386 view_close_datasource (WView *view)
1388 switch (view->datasource) {
1389 case DS_NONE:
1390 break;
1391 case DS_STDIO_PIPE:
1392 if (view->ds_stdio_pipe != NULL) {
1393 (void) pclose (view->ds_stdio_pipe);
1394 display (view);
1395 close_error_pipe (D_NORMAL, NULL);
1396 view->ds_stdio_pipe = NULL;
1398 view_growbuf_free (view);
1399 break;
1400 case DS_VFS_PIPE:
1401 if (view->ds_vfs_pipe != -1) {
1402 (void) mc_close (view->ds_vfs_pipe);
1403 view->ds_vfs_pipe = -1;
1405 view_growbuf_free (view);
1406 break;
1407 case DS_FILE:
1408 (void) mc_close (view->ds_file_fd);
1409 view->ds_file_fd = -1;
1410 g_free (view->ds_file_data);
1411 view->ds_file_data = NULL;
1412 break;
1413 case DS_STRING:
1414 g_free (view->ds_string_data);
1415 view->ds_string_data = NULL;
1416 break;
1417 default:
1418 assert (!"Unknown datasource type");
1420 view->datasource = DS_NONE;
1423 /* {{{ Cursor Movement }}} */
1426 The following variables have to do with the current position and are
1427 updated by the cursor movement functions.
1429 In hex view and wrapped text view mode, dpy_start marks the offset of
1430 the top-left corner on the screen, in non-wrapping text mode it is
1431 the beginning of the current line. In hex mode, hex_cursor is the
1432 offset of the cursor. In non-wrapping text mode, dpy_text_column is
1433 the number of columns that are hidden on the left side on the screen.
1435 In hex mode, dpy_start is updated by the view_fix_cursor_position()
1436 function in order to keep the other functions simple. In
1437 non-wrapping text mode dpy_start and dpy_text_column are normalized
1438 such that dpy_text_column < view_get_datacolumns().
1441 /* prototypes for functions used by view_moveto_bottom() */
1442 static void view_move_up (WView *, offset_type);
1443 static void view_moveto_bol (WView *);
1445 /* set view->first_showed_line and view->dpy_start
1446 * use view->dpy_text_column in nowrap mode */
1447 static void
1448 view_set_first_showed (WView *view, struct cache_line *line)
1450 if (view->text_wrap_mode) {
1451 view->dpy_start = line->start;
1452 view->first_showed_line = line;
1453 } else {
1454 view->dpy_start = view_column_to_offset (view, &line, view->dpy_text_column);
1455 view->first_showed_line = line;
1457 if (view->search_start == view->search_end) {
1458 view->search_start = view->dpy_start;
1459 view->search_end = view->dpy_start;
1463 static void
1464 view_scroll_to_cursor (WView *view)
1466 if (view->hex_mode) {
1467 const offset_type bytes = view->bytes_per_line;
1468 const offset_type displaysize = view->data_area.height * bytes;
1469 const offset_type cursor = view->hex_cursor;
1470 offset_type topleft = view->dpy_start;
1472 if (topleft + displaysize <= cursor)
1473 topleft = offset_rounddown (cursor, bytes)
1474 - (displaysize - bytes);
1475 if (cursor < topleft)
1476 topleft = offset_rounddown (cursor, bytes);
1477 view->dpy_start = topleft;
1478 } else if (view->text_wrap_mode) {
1479 view->dpy_text_column = 0;
1480 } else {
1484 static void
1485 view_movement_fixups (WView *view, gboolean reset_search)
1487 view_scroll_to_cursor (view);
1488 if (reset_search) {
1489 view->search_start = view->dpy_start;
1490 view->search_end = view->dpy_start;
1492 view->dirty++;
1495 static void
1496 view_moveto_top (WView *view)
1498 view->dpy_start = 0;
1499 view->hex_cursor = 0;
1500 view->first_showed_line = view_get_first_line (view);
1501 view->dpy_text_column = 0;
1502 view_movement_fixups (view, TRUE);
1505 static void
1506 view_moveto_bottom (WView *view)
1508 offset_type datalines, lines_up, filesize, last_offset;
1509 struct cache_line *line;
1511 if (view->growbuf_in_use)
1512 view_growbuf_read_until (view, OFFSETTYPE_MAX);
1514 filesize = view_get_filesize (view);
1515 last_offset = offset_doz(filesize, 1);
1516 datalines = view->data_area.height;
1517 lines_up = offset_doz(datalines, 1);
1519 if (view->hex_mode) {
1520 view->hex_cursor = filesize;
1521 view_move_up (view, lines_up);
1522 view->hex_cursor = last_offset;
1523 } else {
1524 line = view_get_last_line (view);
1525 if (!view->text_wrap_mode)
1526 line = view_get_start_of_whole_line (view, line);
1527 view_set_first_showed (view, line);
1528 view->dpy_text_column = 0;
1529 view_move_up (view, lines_up);
1531 view_movement_fixups (view, TRUE);
1534 static void
1535 view_moveto_bol (WView *view)
1537 struct cache_line *line;
1539 if (view->hex_mode) {
1540 view->hex_cursor -= view->hex_cursor % view->bytes_per_line;
1541 } else if (view->text_wrap_mode) {
1542 /* do nothing */
1543 } else {
1544 line = view_get_first_showed_line (view);
1545 line = view_get_start_of_whole_line (view, line);
1546 view->dpy_text_column = 0;
1547 view_set_first_showed (view, line);
1549 view_movement_fixups (view, TRUE);
1552 static void
1553 view_moveto_eol (WView *view)
1555 const screen_dimen width = view->data_area.width;
1556 struct cache_line *line;
1557 screen_dimen w;
1559 if (view->hex_mode) {
1560 offset_type filesize, bol;
1562 bol = offset_rounddown (view->hex_cursor, view->bytes_per_line);
1563 if (get_byte_indexed (view, bol, view->bytes_per_line - 1) != -1) {
1564 view->hex_cursor = bol + view->bytes_per_line - 1;
1565 } else {
1566 filesize = view_get_filesize (view);
1567 view->hex_cursor = offset_doz(filesize, 1);
1569 } else if (view->text_wrap_mode) {
1570 /* nothing to do */
1571 } else {
1572 line = view_get_first_showed_line (view);
1573 line = view_get_start_of_whole_line (view, line);
1574 w = view_width_of_whole_line (view, line);
1575 if (w > width) {
1576 view->dpy_text_column = w - width;
1577 } else {
1578 /* if (w + width <= view->dpy_text_column) {*/
1579 view->dpy_text_column = 0;
1580 /* }*/
1582 view_set_first_showed (view, line);
1584 view_movement_fixups (view, FALSE);
1587 static void
1588 view_moveto_offset (WView *view, offset_type offset)
1590 struct cache_line *line;
1592 if (view->hex_mode) {
1593 view->hex_cursor = offset;
1594 view->dpy_start = offset - offset % view->bytes_per_line;
1595 } else {
1596 line = view_offset_to_line (view, offset);
1597 view->dpy_start = (view->text_wrap_mode) ? line->start : offset;
1598 view->first_showed_line = line;
1599 view->dpy_text_column = (view->text_wrap_mode) ?
1600 0 : view_offset_to_column (view, line, offset);
1602 view_movement_fixups (view, TRUE);
1605 static void
1606 view_moveto (WView *view, offset_type row, offset_type col)
1608 struct cache_line *act;
1609 struct cache_line *t;
1611 act = view_get_first_line (view);
1612 view_move_to_stop (act, t, (off_t)act->number != row, view_get_next_line)
1614 view->dpy_text_column = (view->text_wrap_mode) ? 0 : col;
1615 view->dpy_start = view_column_to_offset (view, &act, col);
1616 view->dpy_start = (view->text_wrap_mode) ? act->start : view->dpy_start;
1617 view->first_showed_line = act;
1619 if (view->hex_mode)
1620 view_moveto_offset (view, view->dpy_start);
1623 /* extendet view_move_to_stop, now has counter, too */
1624 #define view_count_to_stop(l,t,i,stop,nf)\
1625 while (((t = nf(view, l)) != NULL) && (stop)) {\
1626 l = t;i++;}
1628 static void
1629 view_move_up (WView *view, offset_type lines)
1631 struct cache_line *line, *t;
1632 off_t li;
1634 if (view->hex_mode) {
1635 offset_type bytes = lines * view->bytes_per_line;
1636 if (view->hex_cursor >= bytes) {
1637 view->hex_cursor -= bytes;
1638 if (view->hex_cursor < view->dpy_start)
1639 view->dpy_start = offset_doz (view->dpy_start, bytes);
1640 } else {
1641 view->hex_cursor %= view->bytes_per_line;
1643 } else if (view->text_wrap_mode) {
1644 line = view_get_first_showed_line (view);
1645 li = 0;
1646 view_count_to_stop (line, t, li, (li < lines), view_get_previous_line)
1647 view_set_first_showed (view, line);
1648 } else {
1649 line = view_get_first_showed_line (view);
1650 li = 0;
1651 view_count_to_stop (line, t, li, (li < lines), view_get_previous_whole_line)
1652 view_set_first_showed (view, line);
1654 view_movement_fixups (view, (lines != 1));
1657 static void
1658 return_up (struct cache_line * (*ne) (WView *, struct cache_line *),
1659 struct cache_line * (*pr) (WView *, struct cache_line *),
1660 off_t *li, struct cache_line **t, struct cache_line **line,
1661 WView *view)
1663 *li = 0;
1664 *t = *line;
1665 while ((*t != NULL) && (*li < view->data_area.height)) {
1666 (*li)++;
1667 *t = ne (view, *t);
1669 *li = view->data_area.height - *li;
1670 *t = pr (view, *line);
1671 while ((*t != NULL) && (*li > 0)) {
1672 *line = *t;
1673 *t = pr (view, *line);
1674 (*li)--;
1678 static void
1679 view_move_down (WView *view, offset_type lines)
1681 struct cache_line *line;
1682 struct cache_line *t;
1683 off_t li;
1686 if (view->hex_mode) {
1687 offset_type i, limit, last_byte;
1689 last_byte = view_get_filesize (view);
1690 if (last_byte >= (offset_type) view->bytes_per_line)
1691 limit = last_byte - view->bytes_per_line;
1692 else
1693 limit = 0;
1694 for (i = 0; i < lines && view->hex_cursor < limit; i++) {
1695 view->hex_cursor += view->bytes_per_line;
1696 if (lines != 1)
1697 view->dpy_start += view->bytes_per_line;
1700 } else if (view->text_wrap_mode) {
1701 line = view_get_first_showed_line (view);
1702 li = 0;
1703 view_count_to_stop (line, t, li, (off_t)li < lines, view_get_next_line)
1705 /* return_up (view_get_next_line, view_get_previous_line); */
1706 view_set_first_showed (view, line);
1707 } else {
1708 line = view_get_first_showed_line (view);
1709 li = 0;
1710 view_count_to_stop (line, t, li, li < lines, view_get_next_whole_line)
1712 /* return_up (view_get_next_whole_line, view_get_previous_whole_line); */
1713 view_set_first_showed (view, line);
1715 view_movement_fixups (view, (lines != 1));
1718 static void
1719 view_move_left (WView *view, offset_type columns)
1721 struct cache_line *line;
1723 if (view->hex_mode) {
1724 assert (columns == 1);
1725 if (view->hexview_in_text || !view->hexedit_lownibble) {
1726 if (view->hex_cursor > 0)
1727 view->hex_cursor--;
1729 if (!view->hexview_in_text)
1730 view->hexedit_lownibble = !view->hexedit_lownibble;
1731 } else if (view->text_wrap_mode) {
1732 /* nothing to do */
1733 } else {
1734 if (view->dpy_text_column >= columns)
1735 view->dpy_text_column-= columns;
1736 else
1737 view->dpy_text_column = 0;
1739 line = view_get_first_showed_line (view);
1740 view_set_first_showed (view, line);
1742 view_movement_fixups (view, FALSE);
1745 static void
1746 view_move_right (WView *view, offset_type columns)
1748 struct cache_line *line;
1750 if (view->hex_mode) {
1751 assert (columns == 1);
1752 if (view->hexview_in_text || view->hexedit_lownibble) {
1753 if (get_byte_indexed (view, view->hex_cursor, 1) != -1)
1754 view->hex_cursor++;
1756 if (!view->hexview_in_text)
1757 view->hexedit_lownibble = !view->hexedit_lownibble;
1758 } else if (view->text_wrap_mode) {
1759 /* nothing to do */
1760 } else {
1761 view->dpy_text_column += columns;
1762 line = view_get_first_showed_line (view);
1763 view_set_first_showed (view, line);
1765 view_movement_fixups (view, FALSE);
1768 /* {{{ Toggling of viewer modes }}} */
1770 static void
1771 view_toggle_hex_mode (WView *view)
1773 struct cache_line *line;
1775 view->hex_mode = !view->hex_mode;
1776 view->search_type = (view->hex_mode)?MC_SEARCH_T_HEX:MC_SEARCH_T_NORMAL;
1778 if (view->hex_mode) {
1779 view->hex_cursor = view->dpy_start;
1780 view->dpy_start =
1781 offset_rounddown (view->dpy_start, view->bytes_per_line);
1782 view->widget.options |= W_WANT_CURSOR;
1783 } else {
1784 line = view_offset_to_line (view, view->hex_cursor);
1785 view->dpy_text_column = (view->text_wrap_mode) ? 0 :
1786 view_offset_to_column (view, line, view->hex_cursor);
1787 view_set_first_showed (view, line);
1788 view->widget.options &= ~W_WANT_CURSOR;
1790 altered_hex_mode = 1;
1791 view->dpy_bbar_dirty = TRUE;
1792 view->dirty++;
1795 static void
1796 view_toggle_hexedit_mode (WView *view)
1798 view->hexedit_mode = !view->hexedit_mode;
1799 view->dpy_bbar_dirty = TRUE;
1800 view->dirty++;
1803 static void
1804 view_toggle_wrap_mode (WView *view)
1806 struct cache_line *line;
1808 view->text_wrap_mode = !view->text_wrap_mode;
1809 if (view->text_wrap_mode) {
1810 view->dpy_text_column = 0;
1811 view->dpy_start = view_get_first_showed_line (view)->start;
1812 } else {
1813 line = view_get_first_showed_line (view);
1814 view->dpy_text_column = view_width_of_whole_line_before (view, line);
1816 view->dpy_bbar_dirty = TRUE;
1817 view->dirty++;
1820 static void
1821 view_toggle_nroff_mode (WView *view)
1823 struct cache_line *line;
1824 struct cache_line *next;
1826 view->text_nroff_mode = !view->text_nroff_mode;
1827 altered_nroff_flag = 1;
1828 view->dpy_bbar_dirty = TRUE;
1829 view->dirty++;
1831 line = view_get_first_line (view);
1832 view_move_to_stop (line, next, line->end <= view->dpy_start, view_get_next_line)
1834 view_set_first_showed (view, line);
1837 static void
1838 view_toggle_magic_mode (WView *view)
1840 char *filename, *command;
1842 altered_magic_flag = 1;
1843 view->magic_mode = !view->magic_mode;
1844 filename = g_strdup (view->filename);
1845 command = g_strdup (view->command);
1847 view_done (view);
1848 view_load (view, command, filename, 0);
1849 g_free (filename);
1850 g_free (command);
1851 view->dpy_bbar_dirty = TRUE;
1852 view->dirty++;
1855 /* {{{ Miscellaneous functions }}} */
1857 static void
1858 view_done (WView *view)
1860 /* Save current file position */
1861 if (mcview_remember_file_position && view->filename != NULL) {
1862 struct cache_line *line;
1863 char *canon_fname;
1864 offset_type row, col;
1866 canon_fname = vfs_canon (view->filename);
1867 line = view_get_first_showed_line (view);
1868 row = line->number + 1;
1869 col = view_offset_to_column (view, line, view->dpy_start);
1871 save_file_position (canon_fname, row, col);
1872 g_free (canon_fname);
1875 /* Write back the global viewer mode */
1876 default_hex_mode = view->hex_mode;
1877 default_nroff_flag = view->text_nroff_mode;
1878 default_magic_flag = view->magic_mode;
1879 global_wrap_mode = view->text_wrap_mode;
1882 /* view->widget needs no destructor */
1884 g_free (view->filename), view->filename = NULL;
1885 g_free (view->command), view->command = NULL;
1887 view_close_datasource (view);
1888 /* the growing buffer is freed with the datasource */
1890 view_hexedit_free_change_list (view);
1892 g_free(view->last_search_string);
1893 view->last_search_string = NULL;
1895 /* Free memory used by the viewer */
1896 view_reduce_cache_lines (view);
1897 if (view->converter != str_cnv_from_term) str_close_conv (view->converter);
1901 static void
1902 view_show_error (WView *view, const char *msg)
1904 view_close_datasource (view);
1905 if (view_is_in_panel (view)) {
1906 view_set_datasource_string (view, msg);
1907 } else {
1908 message (D_ERROR, MSG_ERROR, "%s", msg);
1912 static gboolean
1913 view_load_command_output (WView *view, const char *command)
1915 FILE *fp;
1917 view_close_datasource (view);
1919 open_error_pipe ();
1920 if ((fp = popen (command, "r")) == NULL) {
1921 /* Avoid two messages. Message from stderr has priority. */
1922 display (view);
1923 if (!close_error_pipe (view_is_in_panel (view) ? -1 : D_ERROR, NULL))
1924 view_show_error (view, _(" Cannot spawn child process "));
1925 return FALSE;
1928 /* First, check if filter produced any output */
1929 view_set_datasource_stdio_pipe (view, fp);
1930 if (get_byte (view, 0) == -1) {
1931 view_close_datasource (view);
1933 /* Avoid two messages. Message from stderr has priority. */
1934 display (view);
1935 if (!close_error_pipe (view_is_in_panel (view) ? -1 : D_ERROR, NULL))
1936 view_show_error (view, _("Empty output from child filter"));
1937 return FALSE;
1939 return TRUE;
1942 gboolean
1943 view_load (WView *view, const char *command, const char *file,
1944 int start_line)
1946 int i, type;
1947 int fd = -1;
1948 char tmp[BUF_MEDIUM];
1949 const char *enc;
1950 char *canon_fname;
1951 struct stat st;
1952 gboolean retval = FALSE;
1954 assert (view->bytes_per_line != 0);
1955 view_done (view);
1958 /* Set up the state */
1959 view_set_datasource_none (view);
1960 view->filename = g_strdup (file);
1961 view->command = 0;
1963 /* Clear the markers */
1964 view->marker = 0;
1965 for (i = 0; i < 10; i++)
1966 view->marks[i] = 0;
1968 if (!view_is_in_panel (view)) {
1969 view->dpy_text_column = 0;
1972 if (command && (view->magic_mode || file == NULL || file[0] == '\0')) {
1973 retval = view_load_command_output (view, command);
1974 } else if (file != NULL && file[0] != '\0') {
1975 /* Open the file */
1976 if ((fd = mc_open (file, O_RDONLY | O_NONBLOCK)) == -1) {
1977 g_snprintf (tmp, sizeof (tmp), _(" Cannot open \"%s\"\n %s "),
1978 file, unix_error_string (errno));
1979 view_show_error (view, tmp);
1980 g_free (view->filename);
1981 view->filename = NULL;
1982 goto finish;
1985 /* Make sure we are working with a regular file */
1986 if (mc_fstat (fd, &st) == -1) {
1987 mc_close (fd);
1988 g_snprintf (tmp, sizeof (tmp), _(" Cannot stat \"%s\"\n %s "),
1989 file, unix_error_string (errno));
1990 view_show_error (view, tmp);
1991 g_free (view->filename);
1992 view->filename = NULL;
1993 goto finish;
1996 if (!S_ISREG (st.st_mode)) {
1997 mc_close (fd);
1998 view_show_error (view, _(" Cannot view: not a regular file "));
1999 g_free (view->filename);
2000 view->filename = NULL;
2001 goto finish;
2004 if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1) {
2005 /* Must be one of those nice files that grow (/proc) */
2006 view_set_datasource_vfs_pipe (view, fd);
2007 } else {
2008 type = get_compression_type (fd);
2010 if (view->magic_mode && (type != COMPRESSION_NONE)) {
2011 g_free (view->filename);
2012 view->filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
2014 view_set_datasource_file (view, fd, &st);
2016 retval = TRUE;
2019 finish:
2020 view->command = g_strdup (command);
2021 view->dpy_start = 0;
2022 view->search_start = 0;
2023 view->search_end = 0;
2024 view->dpy_text_column = 0;
2026 view->converter = str_cnv_from_term;
2027 /* try detect encoding from path */
2028 if (view->filename != NULL) {
2029 canon_fname = vfs_canon (view->filename);
2030 enc = vfs_get_encoding (canon_fname);
2031 if (enc != NULL) {
2032 view->converter = str_crt_conv_from (enc);
2033 if (view->converter == INVALID_CONV)
2034 view->converter = str_cnv_from_term;
2036 g_free (canon_fname);
2039 view_compute_areas (view);
2040 view_reset_cache_lines (view);
2041 view->first_showed_line = view_get_first_line (view);
2043 assert (view->bytes_per_line != 0);
2044 if (mcview_remember_file_position && view->filename != NULL && start_line == 0) {
2045 long line, col;
2047 canon_fname = vfs_canon (view->filename);
2048 load_file_position (file, &line, &col);
2049 g_free (canon_fname);
2050 view_moveto (view, offset_doz(line, 1), col);
2051 } else if (start_line > 0) {
2052 view_moveto (view, start_line - 1, 0);
2055 view->hexedit_lownibble = FALSE;
2056 view->hexview_in_text = FALSE;
2057 view->change_list = NULL;
2059 return retval;
2062 /* {{{ Display management }}} */
2064 static void
2065 view_update_bytes_per_line (WView *view)
2067 const screen_dimen cols = view->data_area.width;
2068 int bytes;
2070 if (cols < 8 + 17)
2071 bytes = 4;
2072 else
2073 bytes = 4 * ((cols - 8) / ((cols < 80) ? 17 : 18));
2074 assert(bytes != 0);
2076 view->bytes_per_line = bytes;
2077 view->dirty = max_dirt_limit + 1; /* To force refresh */
2080 static void
2081 view_percent (WView *view, offset_type p)
2083 const screen_dimen top = view->status_area.top;
2084 const screen_dimen right = view->status_area.left + view->status_area.width;
2085 const screen_dimen height = view->status_area.height;
2086 int percent;
2087 offset_type filesize;
2089 if (height < 1 || right < 4)
2090 return;
2091 if (view_may_still_grow (view))
2092 return;
2093 filesize = view_get_filesize (view);
2095 if (filesize == 0 || view->dpy_end == filesize)
2096 percent = 100;
2097 else if (p > (INT_MAX / 100))
2098 percent = p / (filesize / 100);
2099 else
2100 percent = p * 100 / filesize;
2102 widget_move (view, top, right - 4);
2103 tty_printf ("%3d%%", percent);
2106 static void
2107 view_display_status (WView *view)
2109 const screen_dimen top = view->status_area.top;
2110 const screen_dimen left = view->status_area.left;
2111 const screen_dimen width = view->status_area.width;
2112 const screen_dimen height = view->status_area.height;
2113 const char *file_label, *file_name;
2114 screen_dimen file_label_width;
2115 int i;
2116 char *tmp;
2118 if (height < 1)
2119 return;
2121 tty_setcolor (SELECTED_COLOR);
2122 widget_move (view, top, left);
2123 hline (' ', width);
2125 file_label = _("File: %s");
2126 file_label_width = str_term_width1 (file_label) - 2;
2127 file_name = view->filename ? view->filename
2128 : view->command ? view->command
2129 : "";
2131 if (width < file_label_width + 6)
2132 addstr (str_fit_to_term (file_name, width, J_LEFT_FIT));
2133 else {
2134 i = (width > 22 ? 22 : width) - file_label_width;
2136 tmp = g_strdup_printf (file_label, str_fit_to_term (file_name, i, J_LEFT_FIT));
2137 addstr (tmp);
2138 g_free (tmp);
2139 if (width > 46) {
2140 widget_move (view, top, left + 24);
2141 /* FIXME: the format strings need to be changed when offset_type changes */
2142 if (view->hex_mode)
2143 tty_printf (_("Offset 0x%08lx"), (unsigned long) view->hex_cursor);
2144 else {
2145 screen_dimen row, col;
2146 struct cache_line *line;
2148 line = view_get_first_showed_line (view);
2149 row = line->number + 1;
2151 col = (view->text_wrap_mode) ?
2152 view_width_of_whole_line_before (view, line) :
2153 view->dpy_text_column;
2154 col++;
2156 tty_printf (_("Line %lu Col %lu"),
2157 (unsigned long) row, (unsigned long) col);
2160 if (width > 62) {
2161 offset_type filesize;
2162 filesize = view_get_filesize (view);
2163 widget_move (view, top, left + 43);
2164 if (!view_may_still_grow (view)) {
2165 tty_printf (_("%s bytes"), size_trunc (filesize));
2166 } else {
2167 tty_printf (_(">= %s bytes"), size_trunc (filesize));
2170 if (width > 26) {
2171 view_percent (view, view->hex_mode
2172 ? view->hex_cursor
2173 : view->dpy_end);
2176 tty_setcolor (SELECTED_COLOR);
2179 static inline void
2180 view_display_clean (WView *view)
2182 tty_setcolor (NORMAL_COLOR);
2183 widget_erase ((Widget *) view);
2184 if (view->dpy_frame_size != 0) {
2185 draw_double_box (view->widget.parent, view->widget.y,
2186 view->widget.x, view->widget.lines,
2187 view->widget.cols);
2191 typedef enum {
2192 MARK_NORMAL,
2193 MARK_SELECTED,
2194 MARK_CURSOR,
2195 MARK_CHANGED
2196 } mark_t;
2198 static inline int
2199 view_count_backspaces (WView *view, off_t offset)
2201 int backspaces = 0;
2202 while (offset >= 2 * backspaces
2203 && get_byte (view, offset - 2 * backspaces) == '\b')
2204 backspaces++;
2205 return backspaces;
2208 static void
2209 view_display_ruler (WView *view)
2211 static const char ruler_chars[] = "|----*----";
2212 const screen_dimen top = view->ruler_area.top;
2213 const screen_dimen left = view->ruler_area.left;
2214 const screen_dimen width = view->ruler_area.width;
2215 const screen_dimen height = view->ruler_area.height;
2216 const screen_dimen line_row = (ruler == RULER_TOP) ? 0 : 1;
2217 const screen_dimen nums_row = (ruler == RULER_TOP) ? 1 : 0;
2219 char r_buff[10];
2220 offset_type cl;
2221 screen_dimen c;
2223 if (ruler == RULER_NONE || height < 1)
2224 return;
2226 tty_setcolor (MARKED_COLOR);
2227 for (c = 0; c < width; c++) {
2228 cl = view->dpy_text_column + c;
2229 if (line_row < height) {
2230 widget_move (view, top + line_row, left + c);
2231 tty_print_char (ruler_chars[cl % 10]);
2234 if ((cl != 0) && (cl % 10) == 0) {
2235 g_snprintf (r_buff, sizeof (r_buff), "%"OFFSETTYPE_PRId, cl);
2236 if (nums_row < height) {
2237 widget_move (view, top + nums_row, left + c - 1);
2238 tty_print_string (r_buff);
2242 attrset (NORMAL_COLOR);
2245 static void
2246 view_display_hex (WView *view)
2248 const screen_dimen top = view->data_area.top;
2249 const screen_dimen left = view->data_area.left;
2250 const screen_dimen height = view->data_area.height;
2251 const screen_dimen width = view->data_area.width;
2252 const int ngroups = view->bytes_per_line / 4;
2253 const screen_dimen text_start =
2254 8 + 13 * ngroups + ((width < 80) ? 0 : (ngroups - 1 + 1));
2255 /* 8 characters are used for the file offset, and every hex group
2256 * takes 13 characters. On ``big'' screens, the groups are separated
2257 * by an extra vertical line, and there is an extra space before the
2258 * text column.
2261 screen_dimen row, col;
2262 offset_type from;
2263 int c;
2264 mark_t boldflag = MARK_NORMAL;
2265 struct hexedit_change_node *curr = view->change_list;
2266 size_t i;
2268 char hex_buff[10]; /* A temporary buffer for sprintf and mvwaddstr */
2269 int bytes; /* Number of bytes already printed on the line */
2271 view_display_clean (view);
2273 /* Find the first displayable changed byte */
2274 from = view->dpy_start;
2275 while (curr && (curr->offset < from)) {
2276 curr = curr->next;
2279 for (row = 0; get_byte (view, from) != -1 && row < height; row++) {
2280 col = 0;
2282 /* Print the hex offset */
2283 g_snprintf (hex_buff, sizeof (hex_buff), "%08"OFFSETTYPE_PRIX" ", from);
2284 widget_move (view, top + row, left);
2285 tty_setcolor (MARKED_COLOR);
2286 for (i = 0; col < width && hex_buff[i] != '\0'; i++) {
2287 addch (hex_buff[i]);
2288 /* tty_print_char(hex_buff[i]);*/
2289 col += 1;
2291 tty_setcolor (NORMAL_COLOR);
2293 for (bytes = 0; bytes < view->bytes_per_line; bytes++, from++) {
2295 if ((c = get_byte (view, from)) == -1)
2296 break;
2298 /* Save the cursor position for view_place_cursor() */
2299 if (from == view->hex_cursor && !view->hexview_in_text) {
2300 view->cursor_row = row;
2301 view->cursor_col = col;
2304 /* Determine the state of the current byte */
2305 boldflag =
2306 (from == view->hex_cursor) ? MARK_CURSOR
2307 : (curr != NULL && from == curr->offset) ? MARK_CHANGED
2308 : (view->search_start <= from &&
2309 from < view->search_end) ? MARK_SELECTED
2310 : MARK_NORMAL;
2312 /* Determine the value of the current byte */
2313 if (curr != NULL && from == curr->offset) {
2314 c = curr->value;
2315 curr = curr->next;
2318 /* Select the color for the hex number */
2319 tty_setcolor (
2320 boldflag == MARK_NORMAL ? NORMAL_COLOR :
2321 boldflag == MARK_SELECTED ? MARKED_COLOR :
2322 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
2323 /* boldflag == MARK_CURSOR */
2324 view->hexview_in_text ? MARKED_SELECTED_COLOR :
2325 VIEW_UNDERLINED_COLOR);
2327 /* Print the hex number */
2328 widget_move (view, top + row, left + col);
2329 if (col < width) {
2330 tty_print_char (hex_char[c / 16]);
2331 col += 1;
2333 if (col < width) {
2334 tty_print_char (hex_char[c % 16]);
2335 col += 1;
2338 /* Print the separator */
2339 tty_setcolor (NORMAL_COLOR);
2340 if (bytes != view->bytes_per_line - 1) {
2341 if (col < width) {
2342 tty_print_char (' ');
2343 col += 1;
2346 /* After every four bytes, print a group separator */
2347 if (bytes % 4 == 3) {
2348 if (view->data_area.width >= 80 && col < width) {
2349 tty_print_one_vline ();
2350 col += 1;
2352 if (col < width) {
2353 tty_print_char (' ');
2354 col += 1;
2359 /* Select the color for the character; this differs from the
2360 * hex color when boldflag == MARK_CURSOR */
2361 tty_setcolor (
2362 boldflag == MARK_NORMAL ? NORMAL_COLOR :
2363 boldflag == MARK_SELECTED ? MARKED_COLOR :
2364 boldflag == MARK_CHANGED ? VIEW_UNDERLINED_COLOR :
2365 /* boldflag == MARK_CURSOR */
2366 view->hexview_in_text ? VIEW_UNDERLINED_COLOR :
2367 MARKED_SELECTED_COLOR);
2369 c = convert_to_display_c (c);
2370 if (!g_ascii_isprint (c))
2371 c = '.';
2373 /* Print corresponding character on the text side */
2374 if (text_start + bytes < width) {
2375 widget_move (view, top + row, left + text_start + bytes);
2376 tty_print_char (c);
2379 /* Save the cursor position for view_place_cursor() */
2380 if (from == view->hex_cursor && view->hexview_in_text) {
2381 view->cursor_row = row;
2382 view->cursor_col = text_start + bytes;
2387 /* Be polite to the other functions */
2388 tty_setcolor (NORMAL_COLOR);
2390 view_place_cursor (view);
2391 view->dpy_end = from;
2394 static void
2395 view_display_text (WView * view)
2397 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
2399 const screen_dimen left = view->data_area.left;
2400 const screen_dimen top = view->data_area.top;
2401 const screen_dimen width = view->data_area.width;
2402 const screen_dimen height = view->data_area.height;
2403 struct read_info info;
2404 offset_type row, col;
2405 int w;
2406 struct cache_line *line_act;
2407 struct cache_line *line_nxt;
2409 view_display_clean (view);
2410 view_display_ruler (view);
2412 tty_setcolor (NORMAL_COLOR);
2414 widget_move (view, top, left);
2416 line_act = view_get_first_showed_line (view);
2418 row = 0;
2419 /* set col correct value */
2420 col = (view->text_wrap_mode) ? 0 : view_width_of_whole_line_before (view, line_act);
2421 col+= line_act->left;
2423 view_read_start (view, &info, line_act->start);
2424 while ((info.result != -1) && (row < height)) {
2425 /* real detection of new line */
2426 if (info.next >= line_act->end) {
2427 line_nxt = view_get_next_line (view, line_act);
2428 if (line_nxt == NULL) break;
2430 if (view->text_wrap_mode || (line_act->number != line_nxt->number)){
2431 row++;
2432 col = line_nxt->left;
2434 line_act = line_nxt;
2436 continue;
2439 view_read_continue (view, &info);
2440 if (view_read_test_nroff_back (view, &info)) {
2441 int c;
2443 w = str_term_width1 (info.chi1);
2444 col-= w;
2445 if (col >= view->dpy_text_column
2446 && col + w - view->dpy_text_column <= width) {
2448 widget_move (view, top + row, left + (col - view->dpy_text_column));
2449 for (c = 0; c < w; c++) addch (' ');
2451 if (cmp (info.chi1, "_") && (!cmp (info.cnxt, "_") || !cmp (info.chi2, "\b")))
2452 tty_setcolor (VIEW_UNDERLINED_COLOR);
2453 else
2454 tty_setcolor (MARKED_COLOR);
2455 continue;
2458 if (view_read_test_new_line (view, &info))
2459 continue;
2462 if (view_read_test_tabulator (view, &info)) {
2463 col+= (8 - (col % 8));
2464 continue;
2467 if (view->search_start <= info.actual
2468 && info.actual < view->search_end) {
2469 tty_setcolor (SELECTED_COLOR);
2472 w = str_isprint (info.cact) ? str_term_width1 (info.cact) : 1;
2474 if (col >= view->dpy_text_column
2475 && col + w - view->dpy_text_column <= width) {
2476 widget_move (view, top + row, left + (col - view->dpy_text_column));
2478 if (!str_iscombiningmark (info.cnxt)) {
2479 if (str_isprint (info.cact)) {
2480 addstr (str_term_form (info.cact));
2481 } else {
2482 addch ('.');
2484 } else {
2485 GString *comb = g_string_new ("");
2486 if (str_isprint (info.cact)) {
2487 g_string_append(comb,info.cact);
2488 } else {
2489 g_string_append(comb,".");
2491 while (str_iscombiningmark (info.cnxt)) {
2492 view_read_continue (view, &info);
2493 g_string_append(comb,info.cact);
2495 addstr (str_term_form (comb->str));
2496 g_string_free (comb, TRUE);
2498 } else {
2499 while (str_iscombiningmark (info.cnxt)) {
2500 view_read_continue (view, &info);
2503 col+= w;
2505 tty_setcolor (NORMAL_COLOR);
2507 view->dpy_end = info.next;
2510 /* Displays as much data from view->dpy_start as fits on the screen */
2511 static void
2512 display (WView *view)
2514 view_compute_areas (view);
2515 if (view->hex_mode) {
2516 view_display_hex (view);
2517 } else {
2518 view_display_text (view);
2520 view_display_status (view);
2523 static void
2524 view_place_cursor (WView *view)
2526 const screen_dimen top = view->data_area.top;
2527 const screen_dimen left = view->data_area.left;
2528 screen_dimen col;
2530 col = view->cursor_col;
2531 if (!view->hexview_in_text && view->hexedit_lownibble)
2532 col++;
2533 widget_move (&view->widget, top + view->cursor_row, left + col);
2536 static void
2537 view_update (WView *view)
2539 static int dirt_limit = 1;
2541 if (view->dpy_bbar_dirty) {
2542 view->dpy_bbar_dirty = FALSE;
2543 view_labels (view);
2544 buttonbar_redraw (view->widget.parent);
2547 if (view->dirty > dirt_limit) {
2548 /* Too many updates skipped -> force a update */
2549 display (view);
2550 view->dirty = 0;
2551 /* Raise the update skipping limit */
2552 dirt_limit++;
2553 if (dirt_limit > max_dirt_limit)
2554 dirt_limit = max_dirt_limit;
2556 if (view->dirty) {
2557 if (is_idle ()) {
2558 /* We have time to update the screen properly */
2559 display (view);
2560 view->dirty = 0;
2561 if (dirt_limit > 1)
2562 dirt_limit--;
2563 } else {
2564 /* We are busy -> skipping full update,
2565 only the status line is updated */
2566 view_display_status (view);
2568 /* Here we had a refresh, if fast scrolling does not work
2569 restore the refresh, although this should not happen */
2573 /* {{{ Hex editor }}} */
2575 static void
2576 enqueue_change (struct hexedit_change_node **head,
2577 struct hexedit_change_node *node)
2579 /* chnode always either points to the head of the list or
2580 * to one of the ->next fields in the list. The value at
2581 * this location will be overwritten with the new node. */
2582 struct hexedit_change_node **chnode = head;
2584 while (*chnode != NULL && (*chnode)->offset < node->offset)
2585 chnode = &((*chnode)->next);
2587 node->next = *chnode;
2588 *chnode = node;
2591 static cb_ret_t
2592 view_handle_editkey (WView *view, int key)
2594 struct hexedit_change_node *node;
2595 byte byte_val;
2597 /* Has there been a change at this position? */
2598 node = view->change_list;
2599 while (node && (node->offset != view->hex_cursor))
2600 node = node->next;
2602 if (!view->hexview_in_text) {
2603 /* Hex editing */
2604 unsigned int hexvalue = 0;
2606 if (key >= '0' && key <= '9')
2607 hexvalue = 0 + (key - '0');
2608 else if (key >= 'A' && key <= 'F')
2609 hexvalue = 10 + (key - 'A');
2610 else if (key >= 'a' && key <= 'f')
2611 hexvalue = 10 + (key - 'a');
2612 else
2613 return MSG_NOT_HANDLED;
2615 if (node)
2616 byte_val = node->value;
2617 else
2618 byte_val = get_byte (view, view->hex_cursor);
2620 if (view->hexedit_lownibble) {
2621 byte_val = (byte_val & 0xf0) | (hexvalue);
2622 } else {
2623 byte_val = (byte_val & 0x0f) | (hexvalue << 4);
2625 } else {
2626 /* Text editing */
2627 if (key < 256 && (is_printable (key) || (key == '\n')))
2628 byte_val = key;
2629 else
2630 return MSG_NOT_HANDLED;
2632 if (!node) {
2633 node = g_new (struct hexedit_change_node, 1);
2634 node->offset = view->hex_cursor;
2635 node->value = byte_val;
2636 enqueue_change (&view->change_list, node);
2637 } else {
2638 node->value = byte_val;
2640 view->dirty++;
2641 view_update (view);
2642 view_move_right (view, 1);
2643 return MSG_HANDLED;
2646 static gboolean
2647 view_hexedit_save_changes (WView *view)
2649 struct hexedit_change_node *curr, *next;
2650 int fp, answer;
2651 char *text, *error;
2653 if (view->change_list == NULL)
2654 return TRUE;
2656 retry_save:
2657 assert (view->filename != NULL);
2658 fp = mc_open (view->filename, O_WRONLY);
2659 if (fp == -1)
2660 goto save_error;
2662 for (curr = view->change_list; curr != NULL; curr = next) {
2663 next = curr->next;
2665 if (mc_lseek (fp, curr->offset, SEEK_SET) == -1
2666 || mc_write (fp, &(curr->value), 1) != 1)
2667 goto save_error;
2669 /* delete the saved item from the change list */
2670 view->change_list = next;
2671 view->dirty++;
2672 view_set_byte (view, curr->offset, curr->value);
2673 g_free (curr);
2676 if (mc_close (fp) == -1) {
2677 error = g_strdup (strerror (errno));
2678 message (D_ERROR, _(" Save file "), _(
2679 " Error while closing the file: \n %s \n"
2680 " Data may have been written or not. "), error);
2681 g_free (error);
2683 view_update (view);
2684 return TRUE;
2686 save_error:
2687 error = g_strdup (strerror (errno));
2688 text = g_strdup_printf (_(" Cannot save file: \n %s "), error);
2689 g_free (error);
2690 (void) mc_close (fp);
2692 answer = query_dialog (_(" Save file "), text, D_ERROR,
2693 2, _("&Retry"), _("&Cancel"));
2694 g_free (text);
2696 if (answer == 0)
2697 goto retry_save;
2698 return FALSE;
2701 /* {{{ Miscellaneous functions }}} */
2703 static gboolean
2704 view_ok_to_quit (WView *view)
2706 int r;
2708 if (view->change_list == NULL)
2709 return TRUE;
2711 r = query_dialog (_("Quit"),
2712 _(" File was modified, Save with exit? "), D_NORMAL, 3,
2713 _("&Cancel quit"), _("&Yes"), _("&No"));
2715 switch (r) {
2716 case 1:
2717 return view_hexedit_save_changes (view);
2718 case 2:
2719 view_hexedit_free_change_list (view);
2720 return TRUE;
2721 default:
2722 return FALSE;
2726 static inline void
2727 my_define (Dlg_head *h, int idx, const char *text, void (*fn) (WView *),
2728 WView *view)
2730 buttonbar_set_label_data (h, idx, text, (buttonbarfn) fn, view);
2733 /* {{{ Searching }}} */
2735 /* read one whole line into buffer, return where line start and end */
2736 static int
2737 view_get_line_at (WView *view, offset_type from, GString * buffer,
2738 offset_type *buff_start, offset_type *buff_end)
2740 #define cmp(t1,t2) (strcmp((t1),(t2)) == 0)
2741 struct read_info info;
2742 struct cache_line *line;
2743 offset_type start;
2744 offset_type end;
2746 line = view_get_first_showed_line (view);
2748 line = view_offset_to_line_from (view, from, line);
2750 if (!view->search_backwards) {
2751 start = from;
2752 end = view_get_end_of_whole_line (view, line)->end;
2753 if (start >= end) return 0;
2754 } else {
2755 start = view_get_start_of_whole_line (view, line)->start;
2756 end = from;
2759 (*buff_start) = start;
2760 (*buff_end) = end;
2762 g_string_set_size(buffer,0);
2764 view_read_start (view, &info, start);
2765 while ((info.result != -1) && (info.next < end)) {
2766 view_read_continue (view, &info);
2768 /* if text contains '\0' */
2769 if (cmp (info.cact, "")) {
2770 if (info.actual < from) {
2771 /* '\0' before start offset, continue */
2772 g_string_set_size(buffer,0);
2773 (*buff_start) = info.next;
2774 continue;
2775 } else {
2776 /* '\0' after start offset, end */
2777 (*buff_end) = info.next;
2778 return 1;
2782 if (view_read_test_new_line (view, &info))
2783 continue;
2785 if (view_read_test_nroff_back (view, &info)) {
2786 g_string_truncate (buffer, buffer->len-1);
2787 continue;
2790 g_string_append(buffer,info.cact);
2793 return 1;
2796 /* map search result positions to offsets in text */
2797 void
2798 view_matchs_to_offsets (WView *view, offset_type start, offset_type end,
2799 size_t match_start, size_t match_end,
2800 offset_type *search_start, offset_type *search_end)
2802 struct read_info info;
2803 size_t c = 0;
2805 (*search_start) = INVALID_OFFSET;
2806 (*search_end) = INVALID_OFFSET;
2808 view_read_start (view, &info, start);
2810 while ((info.result != -1) && (info.next < end)) {
2811 view_read_continue (view, &info);
2813 if (view_read_test_nroff_back (view, &info)) {
2814 c-= 1;
2815 continue;
2817 if ((c == match_start) && (*search_start == INVALID_OFFSET))
2818 *search_start = info.actual;
2819 if (c == match_end) (*search_end) = info.actual;
2820 c+= !str_iscombiningmark (info.cact) || (c == 0);
2823 if ((c == match_start) && (*search_start == INVALID_OFFSET)) *search_start = info.next;
2824 if (c == match_end) (*search_end) = info.next;
2827 /* we have set view->search_start and view->search_end and must set
2828 * view->dpy_text_column, view->first_showed_line and view->dpy_start
2829 * try to displaye maximum of match */
2830 void
2831 view_moveto_match (WView *view)
2833 const screen_dimen height = view->data_area.height;
2834 const screen_dimen height3 = height / 3;
2835 const screen_dimen width = view->data_area.width;
2836 struct cache_line *line;
2837 struct cache_line *line_end, *line_start;
2838 struct cache_line *t;
2839 int start_off = -1;
2840 int end_off = -1;
2841 int off = 0;
2843 line = view_get_first_showed_line (view);
2844 if (view->text_wrap_mode) {
2845 if (line->start > view->search_start) {
2846 if (line->start <= view->search_start && line->end > view->search_start)
2847 start_off = 0;
2848 if (line->start <= view->search_end && line->end >= view->search_end)
2849 end_off = 0;
2850 t = view_get_previous_line (view, line);
2851 while ((t != NULL) && ((start_off == -1) || (end_off == -1))) {
2852 line = t;
2853 t = view_get_previous_line (view, line);
2854 off++;
2855 if (line->start <= view->search_start && line->end > view->search_start)
2856 start_off = off;
2857 if (line->start <= view->search_end && line->end >= view->search_end)
2858 end_off = off;
2861 line = view_get_first_showed_line (view);
2863 off = ((off_t)(start_off - end_off) < (off_t)(height - height3))
2864 ? (int)(start_off + height3)
2865 : (int)end_off;
2866 for (;off >= 0 && line->start > 0; off--)
2867 line = view_get_previous_line (view, line);
2868 } else {
2869 /* start_off, end_off - how many cache_lines far are
2870 * view->search_start, end from line */
2871 if (line->start <= view->search_start && line->end > view->search_start)
2872 start_off = 0;
2873 if (line->start <= view->search_end && line->end >= view->search_end)
2874 end_off = 0;
2875 t = view_get_next_line (view, line);
2876 while ((t != NULL) && ((start_off == -1) || (end_off == -1))) {
2877 line = t;
2878 t = view_get_next_line (view, line);
2879 off++;
2880 if (line->start <= view->search_start && line->end > view->search_start)
2881 start_off = off;
2882 if (line->start <= view->search_end && line->end >= view->search_end)
2883 end_off = off;
2886 line = view_get_first_showed_line (view);
2887 /* if view->search_end is farther then screen heigth */
2888 if ((off_t)end_off >= height) {
2889 off = ((off_t)(end_off - start_off) < (off_t)(height - height3))
2890 ? (int) (end_off - height + height3)
2891 : (int) start_off;
2893 for (;off >= 0; off--)
2894 line = view_get_next_line (view, line);
2897 } else {
2898 /* first part similar like in wrap mode,only wokrs with whole lines */
2899 line = view_get_first_showed_line (view);
2900 line = view_get_start_of_whole_line (view, line);
2901 if (line->start > view->search_start) {
2902 line_start = view_get_start_of_whole_line (view, line);
2903 if (line_start->start <= view->search_start && line->end > view->search_start)
2904 start_off = 0;
2905 if (line_start->start <= view->search_end && line->end >= view->search_end)
2906 end_off = 0;
2907 t = view_get_previous_whole_line (view, line_start);
2908 while ((t != NULL) && ((start_off == -1) || (end_off == -1))) {
2909 line = t;
2910 line_start = view_get_start_of_whole_line (view, line);
2911 t = view_get_previous_whole_line (view, line_start);
2912 off++;
2913 if (line_start->start <= view->search_start && line->end > view->search_start)
2914 start_off = off;
2915 if (line_start->start <= view->search_end && line->end >= view->search_end)
2916 end_off = off;
2919 line = view_get_first_showed_line (view);
2920 line = view_get_start_of_whole_line (view, line);
2921 off = ((off_t)(start_off - end_off) < (off_t)(height - height3))
2922 ? (int)(start_off + height3)
2923 : (int)end_off;
2924 for (;off >= 0 && line->start > 0; off--) {
2925 line = view_get_previous_whole_line (view, line);
2926 line = view_get_start_of_whole_line (view, line);
2928 } else {
2929 line_end = view_get_end_of_whole_line (view, line);
2930 if (line->start <= view->search_start && line_end->end > view->search_start)
2931 start_off = 0;
2932 if (line->start <= view->search_end && line_end->end >= view->search_end)
2933 end_off = 0;
2934 t = view_get_next_whole_line (view, line_end);
2935 while ((t != NULL) && ((start_off == -1) || (end_off == -1))) {
2936 line = t;
2937 line_end = view_get_end_of_whole_line (view, line);
2938 t = view_get_next_whole_line (view, line_end);
2939 off++;
2940 if (line->start <= view->search_start && line_end->end > view->search_start)
2941 start_off = off;
2942 if (line->start <= view->search_end && line_end->end >= view->search_end)
2943 end_off = off;
2946 line = view_get_first_showed_line (view);
2947 line = view_get_start_of_whole_line (view, line);
2948 if ((off_t)end_off >= height) {
2949 off = ((off_t)(end_off - start_off) < (off_t)(height - height3))
2950 ? (int)(end_off - height + height3)
2951 : (int)start_off;
2953 for (;off >= 0; off--)
2954 line = view_get_next_whole_line (view, line);
2957 /*now line point to begin of line, that we want show*/
2959 t = view_offset_to_line_from (view, view->search_start, line);
2960 start_off = view_offset_to_column (view, t, view->search_start);
2961 t = view_offset_to_line_from (view, view->search_end, line);
2962 end_off = view_offset_to_column (view, t, view->search_end);
2964 if ((off_t)(end_off - start_off) > width) end_off = start_off + width;
2965 if (view->dpy_text_column > (off_t)start_off) {
2966 view->dpy_text_column = start_off;
2967 } else {
2968 if (view->dpy_text_column + width < (off_t)end_off) {
2969 view->dpy_text_column = end_off - width;
2974 view_set_first_showed (view, line);
2977 static void
2978 search_update_steps (WView *view)
2980 offset_type filesize = view_get_filesize (view);
2981 if (filesize == 0)
2982 view->update_steps = 40000;
2983 else /* viewing a data stream, not a file */
2984 view->update_steps = filesize / 100;
2986 /* Do not update the percent display but every 20 ks */
2987 if (view->update_steps < 20000)
2988 view->update_steps = 20000;
2991 /* {{{ User-definable commands }}} */
2994 The functions in this section can be bound to hotkeys. They are all
2995 of the same type (taking a pointer to WView as parameter and
2996 returning void). TODO: In the not-too-distant future, these commands
2997 will become fully configurable, like they already are in the
2998 internal editor. By convention, all the function names end in
2999 "_cmd".
3002 static void
3003 view_help_cmd (void)
3005 interactive_display (NULL, "[Internal File Viewer]");
3008 /* Toggle between hexview and hexedit mode */
3009 static void
3010 view_toggle_hexedit_mode_cmd (WView *view)
3012 view_toggle_hexedit_mode (view);
3013 view_update (view);
3016 /* Toggle between wrapped and unwrapped view */
3017 static void
3018 view_toggle_wrap_mode_cmd (WView *view)
3020 view_toggle_wrap_mode (view);
3021 view_update (view);
3024 /* Toggle between hex view and text view */
3025 static void
3026 view_toggle_hex_mode_cmd (WView *view)
3028 view_toggle_hex_mode (view);
3029 view_update (view);
3032 static void
3033 view_moveto_line_cmd (WView *view)
3035 char *answer, *answer_end, prompt[BUF_SMALL];
3036 struct cache_line *line;
3037 offset_type row;
3039 line = view_get_first_showed_line (view);
3040 row = line->number + 1;
3042 g_snprintf (prompt, sizeof (prompt),
3043 _(" The current line number is %lu.\n"
3044 " Enter the new line number:"), line->number);
3045 answer = input_dialog (_(" Goto line "), prompt, MC_HISTORY_VIEW_GOTO_LINE, "");
3046 if (answer != NULL && answer[0] != '\0') {
3047 errno = 0;
3048 row = strtoul (answer, &answer_end, 10);
3049 if (*answer_end == '\0' && errno == 0 && row >= 1)
3050 view_moveto (view, row - 1, 0);
3052 g_free (answer);
3053 view->dirty++;
3054 view_update (view);
3057 static void
3058 view_moveto_addr_cmd (WView *view)
3060 char *line, *error, prompt[BUF_SMALL];
3061 offset_type addr;
3063 g_snprintf (prompt, sizeof (prompt),
3064 _(" The current address is 0x%08"OFFSETTYPE_PRIX".\n"
3065 " Enter the new address:"), view->hex_cursor);
3067 line = input_dialog (_(" Goto Address "), prompt, MC_HISTORY_VIEW_GOTO_ADDR, "");
3068 if (line != NULL) {
3069 if (*line != '\0') {
3070 addr = strtoul (line, &error, 0);
3071 if ((*error == '\0') && get_byte (view, addr) != -1) {
3072 view_moveto_offset (view, addr);
3073 } else {
3074 message (D_ERROR, _("Warning"), _(" Invalid address "));
3077 g_free (line);
3079 view->dirty++;
3080 view_update (view);
3083 static void
3084 view_hexedit_save_changes_cmd (WView *view)
3086 (void) view_hexedit_save_changes (view);
3090 static void
3091 do_search (WView *view)
3093 GString *buffer;
3094 offset_type search_start;
3095 int search_status;
3096 Dlg_head *d = NULL;
3098 offset_type line_start;
3099 offset_type line_end;
3100 size_t match_len;
3102 if (verbose) {
3103 d = create_message (D_NORMAL, _("Search"), _("Searching %s"), view->last_search_string);
3104 mc_refresh ();
3107 buffer = g_string_new ("");
3109 search_start = (view->search_backwards) ? view->search_start : view->search_end;
3111 /* Compute the percent steps */
3112 search_update_steps (view);
3113 view->update_activate = 0;
3115 enable_interrupt_key ();
3116 search_status = -1;
3118 while(1){
3119 if (search_start >= view->update_activate) {
3120 view->update_activate += view->update_steps;
3121 if (verbose) {
3122 view_percent (view, search_start);
3123 mc_refresh ();
3125 if (got_interrupt ())
3126 break;
3129 if (!view_get_line_at (view, search_start, buffer, &line_start, &line_end))
3130 break;
3132 if (! mc_search_run( view->search, buffer->str, 0, buffer->len, &match_len )){
3133 if (view->search->error != MC_SEARCH_E_NOTFOUND) {
3134 search_status = -2;
3135 break;
3138 if (! view->search_backwards) {
3139 search_start = line_end;
3140 } else {
3141 if (line_start > 0) search_start = line_start - 1;
3142 else break;
3144 continue;
3146 search_status = 1;
3148 view->search_start = view->search->normal_offset+search_start;
3149 view->search_end = view->search_start + match_len;
3151 if (view->hex_mode){
3152 view->hex_cursor = view->search_start;
3153 view->hexedit_lownibble = FALSE;
3155 view->dpy_start = view->search_start - view->search_start % view->bytes_per_line;
3156 view->dpy_end = view->search_end - view->search_end % view->bytes_per_line;
3160 view_moveto_match (view);
3162 break;
3164 disable_interrupt_key ();
3165 if (verbose) {
3166 dlg_run_done (d);
3167 destroy_dlg (d);
3169 switch (search_status)
3171 case -1:
3172 message (D_NORMAL, _("Search"), _(" Search string not found "));
3173 view->search_end = view->search_start;
3174 break;
3175 case -2:
3176 message (D_NORMAL, _("Search"), "%s", view->search->error_str);
3177 view->search_end = view->search_start;
3178 break;
3180 g_string_free (buffer, TRUE);
3182 view->dirty++;
3183 view_update (view);
3187 /* Both views */
3188 static void
3189 view_search_cmd (WView *view)
3191 enum {
3192 SEARCH_DLG_MIN_HEIGHT = 10,
3193 SEARCH_DLG_HEIGHT_SUPPLY = 3,
3194 SEARCH_DLG_WIDTH = 58
3197 char *defval = g_strdup (view->last_search_string != NULL ? view->last_search_string : "");
3198 char *exp = NULL;
3200 int ttype_of_search = (int) view->search_type;
3201 int tall_codepages = (int) view->search_all_codepages;
3202 int tsearch_case = (int) view->search_case;
3203 int tsearch_backwards = (int) view->search_backwards;
3205 gchar **list_of_types = mc_search_get_types_strings_array();
3206 int SEARCH_DLG_HEIGHT = SEARCH_DLG_MIN_HEIGHT + g_strv_length (list_of_types) - SEARCH_DLG_HEIGHT_SUPPLY;
3208 QuickWidget quick_widgets[] = {
3210 {quick_button, 6, 10, SEARCH_DLG_HEIGHT - 3, SEARCH_DLG_HEIGHT, N_("&Cancel"), 0,
3211 B_CANCEL, 0, 0, NULL, NULL, NULL},
3213 {quick_button, 2, 10, SEARCH_DLG_HEIGHT - 3, SEARCH_DLG_HEIGHT , N_("&OK"), 0, B_ENTER,
3214 0, 0, NULL, NULL, NULL},
3216 {quick_checkbox, SEARCH_DLG_WIDTH/2 + 3, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("All charsets"), 0, 0,
3217 &tall_codepages, 0, NULL, NULL, NULL},
3219 {quick_checkbox, SEARCH_DLG_WIDTH/2 + 3, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT,
3220 N_("&Backwards"), 0, 0, &tsearch_backwards, 0, NULL, NULL, NULL},
3222 {quick_checkbox, SEARCH_DLG_WIDTH/2 + 3, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT, N_("case &Sensitive"), 0, 0,
3223 &tsearch_case, 0, NULL, NULL, NULL},
3225 {quick_radio, 3, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT, 0, g_strv_length (list_of_types), ttype_of_search,
3226 (void *) &ttype_of_search, const_cast (char **, list_of_types), NULL, NULL, NULL},
3229 {quick_input, 3, SEARCH_DLG_WIDTH, 3, SEARCH_DLG_HEIGHT, defval, 52, 0,
3230 0, &exp, N_("Search"), NULL, NULL},
3232 {quick_label, 2, SEARCH_DLG_WIDTH, 2, SEARCH_DLG_HEIGHT,
3233 N_(" Enter search string:"), 0, 0, 0, 0, 0, NULL, NULL},
3235 NULL_QuickWidget
3238 QuickDialog Quick_input = {
3239 SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, N_("Search"),
3240 "[Input Line Keys]", quick_widgets, 0
3243 convert_to_display (defval);
3246 if (quick_dialog (&Quick_input) == B_CANCEL)
3247 goto cleanup;
3249 view->search_backwards = tsearch_backwards;
3250 view->search_type = (mc_search_type_t) ttype_of_search;
3252 view->search_all_codepages = (gboolean) tall_codepages;
3253 view->search_case = (gboolean) tsearch_case;
3255 if (exp == NULL || exp[0] == '\0')
3256 goto cleanup;
3258 convert_from_input (exp);
3260 g_free (view->last_search_string);
3261 view->last_search_string = exp;
3262 exp = NULL;
3264 if (view->search)
3265 mc_search_free(view->search);
3267 view->search = mc_search_new(view->last_search_string, -1);
3268 if (! view->search)
3269 return;
3271 view->search->search_type = view->search_type;
3272 view->search->is_all_charsets = view->search_all_codepages;
3273 view->search->is_case_sentitive = view->search_case;
3275 do_search (view);
3277 cleanup:
3278 g_free (exp);
3279 g_free (defval);
3282 static void
3283 view_toggle_magic_mode_cmd (WView *view)
3285 view_toggle_magic_mode (view);
3286 view_update (view);
3289 static void
3290 view_toggle_nroff_mode_cmd (WView *view)
3292 view_toggle_nroff_mode (view);
3293 view_update (view);
3296 static void
3297 view_quit_cmd (WView *view)
3299 if (view_ok_to_quit (view))
3300 dlg_stop (view->widget.parent);
3303 /* {{{ Miscellaneous functions }}} */
3305 /* Define labels and handlers for functional keys */
3306 static void
3307 view_labels (WView *view)
3309 const char *text;
3310 Dlg_head *h = view->widget.parent;
3312 buttonbar_set_label (h, 1, Q_("ButtonBar|Help"), view_help_cmd);
3314 my_define (h, 10, Q_("ButtonBar|Quit"), view_quit_cmd, view);
3315 text = view->hex_mode ? "ButtonBar|Ascii" : "ButtonBar|Hex";
3316 my_define (h, 4, Q_(text), view_toggle_hex_mode_cmd, view);
3317 text = view->hex_mode ?"ButtonBar|Goto": "ButtonBar|Line";
3318 my_define (h, 5, Q_(text),
3319 view->hex_mode ? view_moveto_addr_cmd : view_moveto_line_cmd, view);
3321 if (view->hex_mode) {
3322 if (view->hexedit_mode) {
3323 my_define (h, 2, Q_("ButtonBar|View"),
3324 view_toggle_hexedit_mode_cmd, view);
3325 } else if (view->datasource == DS_FILE) {
3326 my_define (h, 2, Q_("ButtonBar|Edit"),
3327 view_toggle_hexedit_mode_cmd, view);
3328 } else {
3329 buttonbar_clear_label (h, 2);
3331 my_define (h, 6, Q_("ButtonBar|Save"),
3332 view_hexedit_save_changes_cmd, view);
3333 } else {
3334 text = view->text_wrap_mode ? "ButtonBar|UnWrap" : "ButtonBar|Wrap";
3335 my_define (h, 2, Q_(text), view_toggle_wrap_mode_cmd, view);
3338 text = view->hex_mode ? "ButtonBar|HxSrch" : "ButtonBar|Search";
3339 my_define (h, 7, Q_(text), view_search_cmd, view);
3340 text = view->magic_mode ? "ButtonBar|Raw" : "ButtonBar|Parse";
3341 my_define (h, 8, Q_(text), view_toggle_magic_mode_cmd, view);
3343 /* don't override the key to access the main menu */
3344 if (!view_is_in_panel (view)) {
3345 text = view->text_nroff_mode ? "ButtonBar|Unform" : "ButtonBar|Format";
3346 my_define (h, 9, Q_(text), view_toggle_nroff_mode_cmd, view);
3347 my_define (h, 3, Q_("ButtonBar|Quit"), view_quit_cmd, view);
3351 /* {{{ Event handling }}} */
3353 /* Check for left and right arrows, possibly with modifiers */
3354 static cb_ret_t
3355 check_left_right_keys (WView *view, int c)
3357 if (c == KEY_LEFT) {
3358 view_move_left (view, 1);
3359 return MSG_HANDLED;
3362 if (c == KEY_RIGHT) {
3363 view_move_right (view, 1);
3364 return MSG_HANDLED;
3367 /* Ctrl with arrows moves by 10 postions in the unwrap mode */
3368 if (view->hex_mode || view->text_wrap_mode)
3369 return MSG_NOT_HANDLED;
3371 if (c == (KEY_M_CTRL | KEY_LEFT)) {
3372 if (view->dpy_text_column >= 10)
3373 view->dpy_text_column -= 10;
3374 else
3375 view->dpy_text_column = 0;
3376 view->dirty++;
3377 return MSG_HANDLED;
3380 if (c == (KEY_M_CTRL | KEY_RIGHT)) {
3381 if (view->dpy_text_column <= OFFSETTYPE_MAX - 10)
3382 view->dpy_text_column += 10;
3383 else
3384 view->dpy_text_column = OFFSETTYPE_MAX;
3385 view->dirty++;
3386 return MSG_HANDLED;
3389 return MSG_NOT_HANDLED;
3392 /* {{{ User-definable commands }}} */
3394 static void
3395 view_continue_search_cmd (WView *view)
3397 if (view->last_search_string!=NULL) {
3398 do_search(view);
3399 } else {
3400 /* if not... then ask for an expression */
3401 view_search_cmd (view);
3405 static void
3406 view_toggle_ruler_cmd (WView *view)
3408 static const enum ruler_type next[3] = {
3409 RULER_TOP,
3410 RULER_BOTTOM,
3411 RULER_NONE
3414 assert ((size_t) ruler < 3);
3415 ruler = next[(size_t) ruler];
3416 view->dirty++;
3419 /* {{{ Event handling }}} */
3421 static void view_cmk_move_up (void *w, int n) {
3422 view_move_up ((WView *) w, n);
3424 static void view_cmk_move_down (void *w, int n) {
3425 view_move_down ((WView *) w, n);
3427 static void view_cmk_moveto_top (void *w, int n) {
3428 (void) &n;
3429 view_moveto_top ((WView *) w);
3431 static void view_cmk_moveto_bottom (void *w, int n) {
3432 (void) &n;
3433 view_moveto_bottom ((WView *) w);
3436 static void
3437 view_select_encoding (WView *view)
3439 char *enc = NULL;
3440 GIConv conv;
3441 struct cache_line *line;
3443 #ifdef HAVE_CHARSET
3444 do_select_codepage ();
3445 enc = g_strdup( get_codepage_id ( source_codepage ) );
3446 #endif
3447 if ( enc ) {
3448 conv = str_crt_conv_from (enc);
3449 if (conv != INVALID_CONV) {
3450 if (view->converter != str_cnv_from_term)
3451 str_close_conv (view->converter);
3452 view->converter = conv;
3453 view_reset_cache_lines (view);
3454 line = view_offset_to_line (view, view->dpy_start);
3455 view_set_first_showed (view, line);
3457 g_free(enc);
3463 /* Both views */
3464 static cb_ret_t
3465 view_handle_key (WView *view, int c)
3467 c = convert_from_input_c (c);
3469 if (view->hex_mode) {
3470 switch (c) {
3471 case '\t':
3472 view->hexview_in_text = !view->hexview_in_text;
3473 view->dirty++;
3474 return MSG_HANDLED;
3476 case XCTRL ('a'):
3477 view_moveto_bol (view);
3478 view->dirty++;
3479 return MSG_HANDLED;
3481 case XCTRL ('b'):
3482 view_move_left (view, 1);
3483 return MSG_HANDLED;
3485 case XCTRL ('e'):
3486 view_moveto_eol (view);
3487 return MSG_HANDLED;
3489 case XCTRL ('f'):
3490 view_move_right (view, 1);
3491 return MSG_HANDLED;
3494 if (view->hexedit_mode
3495 && view_handle_editkey (view, c) == MSG_HANDLED)
3496 return MSG_HANDLED;
3499 if (check_left_right_keys (view, c))
3500 return MSG_HANDLED;
3502 if (check_movement_keys (c, view->data_area.height + 1, view,
3503 view_cmk_move_up, view_cmk_move_down,
3504 view_cmk_moveto_top, view_cmk_moveto_bottom))
3505 return MSG_HANDLED;
3507 switch (c) {
3509 case '?':
3510 case '/':
3511 view->search_type = MC_SEARCH_T_REGEX;
3512 view_search_cmd(view);
3513 return MSG_HANDLED;
3514 break;
3515 /* Continue search */
3516 case XCTRL ('r'):
3517 case XCTRL ('s'):
3518 case 'n':
3519 case KEY_F (17):
3520 view_continue_search_cmd (view);
3521 return MSG_HANDLED;
3523 /* toggle ruler */
3524 case ALT ('r'):
3525 view_toggle_ruler_cmd (view);
3526 return MSG_HANDLED;
3528 case 'h':
3529 view_move_left (view, 1);
3530 return MSG_HANDLED;
3532 case 'j':
3533 case '\n':
3534 case 'e':
3535 view_move_down (view, 1);
3536 return MSG_HANDLED;
3538 case 'd':
3539 view_move_down (view, (view->data_area.height + 1) / 2);
3540 return MSG_HANDLED;
3542 case 'u':
3543 view_move_up (view, (view->data_area.height + 1) / 2);
3544 return MSG_HANDLED;
3546 case 'k':
3547 case 'y':
3548 view_move_up (view, 1);
3549 return MSG_HANDLED;
3551 case 'l':
3552 view_move_right (view, 1);
3553 return MSG_HANDLED;
3555 case ' ':
3556 case 'f':
3557 view_move_down (view, view->data_area.height);
3558 return MSG_HANDLED;
3560 case XCTRL ('o'):
3561 view_other_cmd ();
3562 return MSG_HANDLED;
3564 /* Unlike Ctrl-O, run a new shell if the subshell is not running. */
3565 case '!':
3566 exec_shell ();
3567 return MSG_HANDLED;
3569 case 'b':
3570 view_move_up (view, view->data_area.height);
3571 return MSG_HANDLED;
3573 case KEY_IC:
3574 view_move_up (view, 2);
3575 return MSG_HANDLED;
3577 case KEY_DC:
3578 view_move_down (view, 2);
3579 return MSG_HANDLED;
3581 case 'm':
3582 view->marks[view->marker] = view->dpy_start;
3583 return MSG_HANDLED;
3585 case 'r':
3586 view->dpy_start = view->marks[view->marker];
3587 view->dirty++;
3588 return MSG_HANDLED;
3590 /* Use to indicate parent that we want to see the next/previous file */
3591 /* Does not work in panel mode */
3592 case XCTRL ('f'):
3593 case XCTRL ('b'):
3594 if (!view_is_in_panel (view))
3595 view->move_dir = c == XCTRL ('f') ? 1 : -1;
3596 /* FALLTHROUGH */
3597 case 'q':
3598 case XCTRL ('g'):
3599 case ESC_CHAR:
3600 if (view_ok_to_quit (view))
3601 view->want_to_quit = TRUE;
3602 return MSG_HANDLED;
3604 case XCTRL ('t'):
3605 view_select_encoding (view);
3606 view->dirty++;
3607 view_update (view);
3608 return MSG_HANDLED;
3610 #ifdef MC_ENABLE_DEBUGGING_CODE
3611 case 't': /* mnemonic: "test" */
3612 view_ccache_dump (view);
3613 return MSG_HANDLED;
3614 #endif
3616 if (c >= '0' && c <= '9')
3617 view->marker = c - '0';
3619 /* Key not used */
3620 return MSG_NOT_HANDLED;
3623 /* Both views */
3624 static int
3625 view_event (WView *view, Gpm_Event *event, int *result)
3627 screen_dimen y, x;
3629 *result = MOU_NORMAL;
3631 /* We are not interested in the release events */
3632 if (!(event->type & (GPM_DOWN | GPM_DRAG)))
3633 return 0;
3635 /* Wheel events */
3636 if ((event->buttons & GPM_B_UP) && (event->type & GPM_DOWN)) {
3637 view_move_up (view, 2);
3638 return 1;
3640 if ((event->buttons & GPM_B_DOWN) && (event->type & GPM_DOWN)) {
3641 view_move_down (view, 2);
3642 return 1;
3645 x = event->x;
3646 y = event->y;
3648 /* Scrolling left and right */
3649 if (!view->text_wrap_mode) {
3650 if (x < view->data_area.width * 1/4) {
3651 view_move_left (view, 1);
3652 goto processed;
3653 } else if (x < view->data_area.width * 3/4) {
3654 /* ignore the click */
3655 } else {
3656 view_move_right (view, 1);
3657 goto processed;
3661 /* Scrolling up and down */
3662 if (y < view->data_area.top + view->data_area.height * 1/3) {
3663 if (mouse_move_pages_viewer)
3664 view_move_up (view, view->data_area.height / 2);
3665 else
3666 view_move_up (view, 1);
3667 goto processed;
3668 } else if (y < view->data_area.top + view->data_area.height * 2/3) {
3669 /* ignore the click */
3670 } else {
3671 if (mouse_move_pages_viewer)
3672 view_move_down (view, view->data_area.height / 2);
3673 else
3674 view_move_down (view, 1);
3675 goto processed;
3678 return 0;
3680 processed:
3681 *result = MOU_REPEAT;
3682 return 1;
3685 /* Real view only */
3686 static int
3687 real_view_event (Gpm_Event *event, void *x)
3689 WView *view = (WView *) x;
3690 int result;
3692 if (view_event (view, event, &result))
3693 view_update (view);
3694 return result;
3697 static void
3698 view_adjust_size (Dlg_head *h)
3700 WView *view;
3701 WButtonBar *bar;
3703 /* Look up the viewer and the buttonbar, we assume only two widgets here */
3704 view = (WView *) find_widget_type (h, view_callback);
3705 bar = find_buttonbar (h);
3706 widget_set_size (&view->widget, 0, 0, LINES - 1, COLS);
3707 widget_set_size ((Widget *) bar, LINES - 1, 0, 1, COLS);
3709 view_compute_areas (view);
3710 view_update_bytes_per_line (view);
3713 /* Callback for the view dialog */
3714 static cb_ret_t
3715 view_dialog_callback (Dlg_head *h, dlg_msg_t msg, int parm)
3717 switch (msg) {
3718 case DLG_RESIZE:
3719 view_adjust_size (h);
3720 return MSG_HANDLED;
3722 default:
3723 return default_dlg_callback (h, msg, parm);
3727 /* {{{ External interface }}} */
3729 /* Real view only */
3731 mc_internal_viewer (const char *command, const char *file,
3732 int *move_dir_p, int start_line)
3734 gboolean succeeded;
3735 WView *wview;
3736 WButtonBar *bar;
3737 Dlg_head *view_dlg;
3739 /* Create dialog and widgets, put them on the dialog */
3740 view_dlg =
3741 create_dlg (0, 0, LINES, COLS, NULL, view_dialog_callback,
3742 "[Internal File Viewer]", NULL, DLG_WANT_TAB);
3744 wview = view_new (0, 0, COLS, LINES - 1, 0);
3746 bar = buttonbar_new (1);
3748 add_widget (view_dlg, bar);
3749 add_widget (view_dlg, wview);
3751 succeeded = view_load (wview, command, file, start_line);
3752 if (succeeded) {
3753 run_dlg (view_dlg);
3754 if (move_dir_p)
3755 *move_dir_p = wview->move_dir;
3756 } else {
3757 if (move_dir_p)
3758 *move_dir_p = 0;
3760 destroy_dlg (view_dlg);
3762 return succeeded;
3765 /* {{{ Miscellaneous functions }}} */
3767 static void
3768 view_hook (void *v)
3770 WView *view = (WView *) v;
3771 WPanel *panel;
3773 /* If the user is busy typing, wait until he finishes to update the
3774 screen */
3775 if (!is_idle ()) {
3776 if (!hook_present (idle_hook, view_hook))
3777 add_hook (&idle_hook, view_hook, v);
3778 return;
3781 delete_hook (&idle_hook, view_hook);
3783 if (get_current_type () == view_listing)
3784 panel = current_panel;
3785 else if (get_other_type () == view_listing)
3786 panel = other_panel;
3787 else
3788 return;
3790 view_load (view, 0, panel->dir.list[panel->selected].fname, 0);
3791 display (view);
3794 /* {{{ Event handling }}} */
3796 static cb_ret_t
3797 view_callback (Widget *w, widget_msg_t msg, int parm)
3799 WView *view = (WView *) w;
3800 cb_ret_t i;
3801 Dlg_head *h = view->widget.parent;
3803 view_compute_areas (view);
3804 view_update_bytes_per_line (view);
3806 switch (msg) {
3807 case WIDGET_INIT:
3808 if (view_is_in_panel (view))
3809 add_hook (&select_file_hook, view_hook, view);
3810 else
3811 view->dpy_bbar_dirty = TRUE;
3812 return MSG_HANDLED;
3814 case WIDGET_DRAW:
3815 display (view);
3816 return MSG_HANDLED;
3818 case WIDGET_CURSOR:
3819 if (view->hex_mode)
3820 view_place_cursor (view);
3821 return MSG_HANDLED;
3823 case WIDGET_KEY:
3824 i = view_handle_key ((WView *) view, parm);
3825 if (view->want_to_quit && !view_is_in_panel (view))
3826 dlg_stop (h);
3827 else {
3828 view_update (view);
3830 return i;
3832 case WIDGET_FOCUS:
3833 view->dpy_bbar_dirty = TRUE;
3834 view_update (view);
3835 return MSG_HANDLED;
3837 case WIDGET_DESTROY:
3838 view_done (view);
3839 if (view_is_in_panel (view))
3840 delete_hook (&select_file_hook, view_hook);
3841 return MSG_HANDLED;
3843 default:
3844 return default_proc (msg, parm);
3848 /* {{{ External interface }}} */
3850 WView *
3851 view_new (int y, int x, int cols, int lines, int is_panel)
3853 WView *view = g_new0 (WView, 1);
3854 size_t i;
3856 init_widget (&view->widget, y, x, lines, cols,
3857 view_callback,
3858 real_view_event);
3860 view->filename = NULL;
3861 view->command = NULL;
3863 view_set_datasource_none (view);
3865 view->growbuf_in_use = FALSE;
3866 /* leave the other growbuf fields uninitialized */
3868 view->hex_mode = FALSE;
3869 view->hexedit_mode = FALSE;
3870 view->hexview_in_text = FALSE;
3871 view->text_nroff_mode = FALSE;
3872 view->text_wrap_mode = FALSE;
3873 view->magic_mode = FALSE;
3875 view->hexedit_lownibble = FALSE;
3877 view->dpy_frame_size = is_panel ? 1 : 0;
3878 view->dpy_start = 0;
3879 view->dpy_text_column = 0;
3880 view->dpy_end = 0;
3881 view->hex_cursor = 0;
3882 view->cursor_col = 0;
3883 view->cursor_row = 0;
3884 view->change_list = NULL;
3885 view->converter = str_cnv_from_term;
3887 /* {status,ruler,data}_area are left uninitialized */
3889 view->dirty = 0;
3890 view->dpy_bbar_dirty = TRUE;
3891 view->bytes_per_line = 1;
3893 view->search_start = 0;
3894 view->search_end = 0;
3896 view->want_to_quit = FALSE;
3897 view->marker = 0;
3898 for (i = 0; i < sizeof(view->marks) / sizeof(view->marks[0]); i++)
3899 view->marks[i] = 0;
3901 view->move_dir = 0;
3902 view->update_steps = 0;
3903 view->update_activate = 0;
3905 if (default_hex_mode)
3906 view_toggle_hex_mode (view);
3907 if (default_nroff_flag)
3908 view_toggle_nroff_mode (view);
3909 if (global_wrap_mode)
3910 view_toggle_wrap_mode (view);
3911 if (default_magic_flag)
3912 view_toggle_magic_mode (view);
3914 return view;