ooops reverting mischanged option menu order...
[midnight-commander.git] / src / view.c
blob9b9c384ef40b9af561d0445c646652331ce48aba
1 /* {{{ Copyright notice */
3 /* View file module for the Midnight Commander
4 Copyright (C) 1994, 1995, 1996 The Free Software Foundation
5 Written by: 1994, 1995, 1998 Miguel de Icaza
6 1994, 1995 Janne Kukonlehto
7 1995 Jakub Jelinek
8 1996 Joseph M. Hinkle
9 1997 Norbert Warmuth
10 1998 Pavel Machek
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
26 /* }}} */
27 /* {{{ Declarations */
28 #include <config.h>
29 #include <stdio.h>
30 #include <sys/types.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #include <string.h>
35 #include <sys/stat.h>
36 #ifdef HAVE_MMAP
37 # include <sys/mman.h>
38 #endif
39 #include <fcntl.h>
40 #include <ctype.h> /* For toupper() */
41 #include <errno.h>
42 #include <limits.h>
44 #include "global.h"
45 #include "tty.h"
46 #include "cmd.h" /* For view_other_cmd */
47 #include "dlg.h" /* Needed by widget.h */
48 #include "widget.h" /* Needed for buttonbar_new */
49 #include "color.h"
50 #include "dialog.h"
51 #include "mouse.h"
52 #include "help.h"
53 #include "key.h" /* For mi_getch() */
54 #include "layout.h"
55 #include "setup.h"
56 #include "wtools.h" /* For query_set_sel() */
57 #include "../vfs/vfs.h"
58 #include "dir.h"
59 #include "panel.h" /* Needed for current_panel and other_panel */
60 #include "win.h"
61 #include "main.h" /* For the externs */
62 #define WANT_WIDGETS
63 #include "view.h"
65 #ifdef HAVE_CHARSET
66 #include "charsets.h"
67 #include "selcodepage.h"
68 #endif /* HAVE_CHARSET */
70 #ifndef MAP_FILE
71 #define MAP_FILE 0
72 #endif
74 /* Block size for reading files in parts */
75 #define READ_BLOCK 8192
76 #define VIEW_PAGE_SIZE 8192
78 #ifdef IS_AIX
79 # define IFAIX(x) case (x):
80 #else
81 # define IFAIX(x)
82 #endif
84 /* Maxlimit for skipping updates */
85 static int max_dirt_limit =
86 #ifdef NATIVE_WIN32
88 #else
89 10;
90 #endif
92 extern Hook *idle_hook;
94 /* If set, show a ruler */
95 static int ruler = 0;
97 /* Scrolling is done in pages or line increments */
98 int mouse_move_pages_viewer = 1;
100 /* Used to compute the bottom first variable */
101 int have_fast_cpu = 0;
103 /* wrap mode default */
104 int global_wrap_mode = 1;
106 int default_hex_mode = 0;
107 static int default_hexedit_mode = 0;
108 int default_magic_flag = 1;
109 int default_nroff_flag = 1;
110 int altered_hex_mode = 0;
111 int altered_magic_flag = 0;
112 int altered_nroff_flag = 0;
113 /* }}} */
115 /* "$Id$" */
117 static const char hex_char[] = "0123456789ABCDEF";
119 /* Our callback */
120 static int view_callback (Dlg_head *h, WView *view, int msg, int par);
122 static int regexp_view_search (WView *view, char *pattern, char *string, int match_type);
123 static void view_move_forward (WView *view, int i);
124 static void view_labels (WView *view);
125 static void set_monitor (WView *view, int set_on);
126 static void view_update (WView *view, gboolean update_gui);
128 /* }}} */
129 /* {{{ Clean-up functions */
131 static void
132 close_view_file (WView *view)
134 if (view->file != -1){
135 mc_close (view->file);
136 view->file = -1;
140 static void
141 free_file (WView *view)
143 int i;
145 #ifdef HAVE_MMAP
146 if (view->mmapping){
147 mc_munmap (view->data, view->s.st_size);
148 close_view_file (view);
149 } else
150 #endif /* HAVE_MMAP */
152 if (view->reading_pipe){
153 /* Check error messages */
154 if (!view->have_frame)
155 check_error_pipe ();
157 /* Close pipe */
158 pclose (view->stdfile);
159 view->stdfile = NULL;
161 /* Ignore errors because we don't want to hear about broken pipe */
162 close_error_pipe (-1, NULL);
163 } else
164 close_view_file (view);
166 /* Block_ptr may be zero if the file was a file with 0 bytes */
167 if (view->growing_buffer && view->block_ptr){
168 for (i = 0; i < view->blocks; i++){
169 g_free (view->block_ptr [i].data);
171 g_free (view->block_ptr);
175 /* Valid parameters for second parameter to set_monitor */
176 enum { off, on };
178 /* Both views */
179 static void
180 view_done (WView *view)
182 set_monitor (view, off);
184 /* alex: release core, used to replace mmap */
185 if (!view->mmapping && !view->growing_buffer && view->data != NULL){
186 g_free (view->data);
187 view->data = NULL;
190 if (view->view_active){
191 if (view->localcopy)
192 mc_ungetlocalcopy (view->filename, view->localcopy, 0);
193 free_file (view);
194 g_free (view->filename);
195 if (view->command)
196 g_free (view->command);
198 view->view_active = 0;
199 default_hex_mode = view->hex_mode;
200 default_nroff_flag = view->viewer_nroff_flag;
201 default_magic_flag = view->viewer_magic_flag;
202 global_wrap_mode = view->wrap_mode;
205 static void view_hook (void *);
207 static void
208 view_destroy (WView *view)
210 view_done (view);
211 if (view->have_frame)
212 delete_hook (&select_file_hook, view_hook);
215 static int
216 get_byte (WView *view, unsigned int byte_index)
218 int page = byte_index / VIEW_PAGE_SIZE + 1;
219 int offset = byte_index % VIEW_PAGE_SIZE;
220 int i, n;
222 if (view->growing_buffer){
223 if (page > view->blocks){
224 view->block_ptr = g_realloc (view->block_ptr,
225 sizeof (block_ptr_t) * page);
226 for (i = view->blocks; i < page; i++){
227 char *p = g_malloc (VIEW_PAGE_SIZE);
228 view->block_ptr [i].data = p;
229 if (!p)
230 return '\n';
231 if (view->stdfile != NULL)
232 n = fread (p, 1, VIEW_PAGE_SIZE, view->stdfile);
233 else
234 n = mc_read (view->file, p, VIEW_PAGE_SIZE);
236 * FIXME: Errors are ignored at this point
237 * Also should report preliminary EOF
239 if (n != -1)
240 view->bytes_read += n;
241 if (view->s.st_size < view->bytes_read){
242 view->bottom_first = -1; /* Invalidate cache */
243 view->s.st_size = view->bytes_read;
244 view->last_byte = view->bytes_read;
245 if (view->reading_pipe)
246 view->last_byte = view->first + view->bytes_read;
248 /* To force loading the next page */
249 if (n == VIEW_PAGE_SIZE && view->reading_pipe){
250 view->last_byte++;
253 view->blocks = page;
255 if (byte_index > view->bytes_read){
256 return -1;
257 } else
258 return view->block_ptr [page-1].data [offset];
259 } else {
260 if (byte_index >= view->last_byte)
261 return -1;
262 else
263 return view->data [byte_index];
267 static void
268 enqueue_change (struct hexedit_change_node **head, struct hexedit_change_node *node)
270 struct hexedit_change_node *curr = *head;
272 while (curr) {
273 if (node->offset < curr->offset) {
274 *head = node;
275 node->next = curr;
276 return;
278 head = (struct hexedit_change_node **) curr;
279 curr = curr->next;
281 *head = node;
282 node->next = curr;
285 static void move_right (WView *);
287 static void
288 put_editkey (WView *view, unsigned char key)
290 struct hexedit_change_node *node;
291 unsigned char byte_val;
293 if (!view->hexedit_mode || view->growing_buffer != 0)
294 return;
296 /* Has there been a change at this position ? */
297 node = view->change_list;
298 while (node && (node->offset != view->edit_cursor)) {
299 node = node->next;
302 if (view->view_side == view_side_left) {
303 /* Hex editing */
305 if (key >= '0' && key <= '9')
306 key -= '0';
307 else if (key >= 'A' && key <= 'F')
308 key -= '7';
309 else if (key >= 'a' && key <= 'f')
310 key -= 'W';
311 else
312 return;
314 if (node)
315 byte_val = node->value;
316 else
317 byte_val = get_byte(view, view->edit_cursor);
319 if (view->nib_shift == 0) {
320 byte_val = (byte_val & 0x0f) | (key << 4);
321 } else {
322 byte_val = (byte_val & 0xf0) | (key);
324 } else {
325 /* Text editing */
326 byte_val = key;
328 if (!node) {
329 node = (struct hexedit_change_node *)
330 g_new (struct hexedit_change_node, 1);
332 if (node) {
333 #ifndef HAVE_MMAP
334 /* alex@bcs.zaporizhzhe.ua: here we are using file copy
335 * completely loaded into memory, so we can replace bytes in
336 * view->data array to allow changes to be reflected when
337 * user switches back to ascii mode */
338 view->data[view->edit_cursor] = byte_val;
339 #endif /* !HAVE_MMAP */
340 node->offset = view->edit_cursor;
341 node->value = byte_val;
342 enqueue_change (&view->change_list, node);
344 } else {
345 node->value = byte_val;
347 view->dirty++;
348 view_update (view, TRUE);
349 move_right (view);
352 static void
353 free_change_list (WView *view)
355 struct hexedit_change_node *n = view->change_list;
357 while (n) {
358 view->change_list = n->next;
359 g_free (n);
360 n = view->change_list;
362 view->file_dirty = 0;
363 view->dirty++;
366 static void
367 save_edit_changes (WView *view)
369 struct hexedit_change_node *node = view->change_list;
370 int fp;
372 do {
373 fp = open (view->filename, O_WRONLY);
374 if (fp >= 0) {
375 while (node) {
376 if (lseek (fp, node->offset, SEEK_SET) == -1 ||
377 write (fp, &node->value, 1) != 1) {
378 close (fp);
379 fp = -1;
380 break;
382 node = node->next;
384 close (fp);
387 if (fp == -1) {
388 fp = query_dialog (_(" Save file "),
389 _(" Error trying to save file. "),
390 2, 2, _("&Retry"), _("&Cancel"));
391 if (fp == 0)
392 fp = -1;
394 } while (fp == -1);
396 free_change_list (view);
399 static int
400 view_ok_to_quit (WView *view)
402 int r;
403 char *text;
405 if (!view->change_list)
406 return 1;
408 query_set_sel (1);
409 text = g_strconcat (_("File: \n\n "), view->filename,
410 _("\n\nhas been modified, do you want to save the changes?\n"), NULL);
412 r = query_dialog (_(" Save changes "), text, 2, 3, _("&Yes"), _("&No"), _("&Cancel"));
413 g_free (text);
415 switch (r) {
416 case 0:
417 save_edit_changes (view);
418 return 1;
419 case 1:
420 free_change_list (view);
421 return 1;
422 default:
423 return 0;
427 static char *
428 set_view_init_error (WView *view, char *msg)
430 view->growing_buffer = 0;
431 view->reading_pipe = 0;
432 view->first = 0;
433 view->last_byte = 0;
434 if (msg){
435 view->bytes_read = strlen (msg);
436 return g_strdup (msg);
438 return 0;
441 /* return values: NULL for success, else points to error message */
442 static char *
443 init_growing_view (WView * view, char *name, char *filename)
445 char *err_msg = NULL;
447 view->growing_buffer = 1;
449 if (name) {
450 view->reading_pipe = 1;
451 view->s.st_size = 0;
453 open_error_pipe ();
454 if ((view->stdfile = popen (name, "r")) == NULL) {
455 /* Avoid two messages. Message from stderr has priority. */
456 if (!close_error_pipe (view->have_frame ? -1 : 1, view->data))
457 err_msg = _(" Cannot spawn child program ");
458 return set_view_init_error (view, err_msg);
461 /* First, check if filter produced any output */
462 get_byte (view, 0);
463 if (view->bytes_read <= 0) {
464 pclose (view->stdfile);
465 view->stdfile = NULL;
466 /* Avoid two messages. Message from stderr has priority. */
467 if (!close_error_pipe (view->have_frame ? -1 : 1, view->data))
468 err_msg = (" Empty output from child filter ");
469 return set_view_init_error (view, err_msg);
471 } else {
472 view->stdfile = NULL;
473 if ((view->file = mc_open (filename, O_RDONLY)) == -1)
474 return set_view_init_error (view, _(" Could not open file "));
476 return NULL;
479 /* Load filename into core */
480 /* returns:
481 -1 on failure.
482 if (have_frame), we return success, but data points to a
483 error message instead of the file buffer (quick_view feature).
485 static char *load_view_file (WView *view, int fd)
487 view->file = fd;
489 if (view->s.st_size == 0){
490 /* Must be one of those nice files that grow (/proc) */
491 close_view_file (view);
492 return init_growing_view (view, 0, view->filename);
495 #ifdef HAVE_MMAP
496 view->data = mc_mmap (0, view->s.st_size, PROT_READ, MAP_FILE | MAP_SHARED,
497 view->file, 0);
498 if ((caddr_t) view->data != (caddr_t) -1) {
499 /* mmap worked */
500 view->first = 0;
501 view->bytes_read = view->s.st_size;
502 view->mmapping = 1;
503 return NULL;
505 #endif /* HAVE_MMAP */
507 /* For those OS that dont provide mmap call. Try to load all the
508 * file into memory (alex@bcs.zaporizhzhe.ua). Also, mmap can fail
509 * for any reason, so we use this as fallback (pavel@ucw.cz) */
511 view->data = (unsigned char*) g_malloc (view->s.st_size);
512 if (view->data == NULL
513 || mc_lseek(view->file, 0, SEEK_SET) != 0
514 || mc_read(view->file, view->data, view->s.st_size) != view->s.st_size){
515 if (view->data != NULL)
516 g_free (view->data);
517 close_view_file (view);
518 return init_growing_view (view, 0, view->filename);
520 view->first = 0;
521 view->bytes_read = view->s.st_size;
522 return NULL;
525 /* Return zero on success, -1 on failure */
526 static int
527 do_view_init (WView *view, char *_command, const char *_file, int start_line)
529 char *error = 0;
530 int i, type;
531 int fd = -1;
532 char tmp[BUF_MEDIUM];
534 if (view->view_active)
535 view_done (view);
537 /* Set up the state */
538 view->block_ptr = 0;
539 view->data = NULL;
540 view->growing_buffer = 0;
541 view->reading_pipe = 0;
542 view->mmapping = 0;
543 view->blocks = 0;
544 view->block_ptr = 0;
545 view->first = view->bytes_read = 0;
546 view->last_byte = 0;
547 view->filename = g_strdup (_file);
548 view->localcopy = 0;
549 view->command = 0;
550 view->last = view->first + ((LINES-2) * view->bytes_per_line);
552 /* Clear the markers */
553 view->marker = 0;
554 for (i = 0; i < 10; i++)
555 view->marks [i] = 0;
557 if (!view->have_frame){
558 view->start_col = 0;
561 if (_command && (view->viewer_magic_flag || _file[0] == '\0')) {
562 error = init_growing_view (view, _command, view->filename);
563 } else if (_file[0]) {
564 /* Make sure we are working with a regular file */
565 if (mc_stat (view->filename, &view->s) == -1) {
566 g_snprintf (tmp, sizeof (tmp), _(" Cannot stat \"%s\"\n %s "),
567 _file, unix_error_string (errno));
568 error = set_view_init_error (view, tmp);
569 goto finish;
572 if (!S_ISREG (view->s.st_mode)) {
573 g_snprintf (tmp, sizeof (tmp),
574 _(" Cannot view: not a regular file "));
575 error = set_view_init_error (view, tmp);
576 goto finish;
579 /* Actually open the file */
580 if ((fd = mc_open(_file, O_RDONLY)) == -1) {
581 g_snprintf (tmp, sizeof (tmp), _(" Cannot open \"%s\"\n %s "),
582 _file, unix_error_string (errno));
583 error = set_view_init_error (view, tmp);
584 goto finish;
587 type = get_compression_type (fd);
589 if (view->viewer_magic_flag && (type != COMPRESSION_NONE)) {
590 g_free (view->filename);
591 view->filename = g_strconcat (_file, decompress_extension(type), NULL);
594 error = load_view_file (view, fd);
597 finish:
598 if (error){
599 if (!view->have_frame){
600 message (1, MSG_ERROR, "%s", error);
601 g_free (error);
602 return -1;
606 view->view_active = 1;
607 if (_command)
608 view->command = g_strdup (_command);
609 else
610 view->command = 0;
611 view->search_start = view->start_display = view->start_save = view->first;
612 view->found_len = 0;
613 view->start_col = 0;
614 view->last_search = 0; /* Start a new search */
616 /* Special case: The data points to the error message */
617 if (error){
618 view->data = error;
619 view->file = -1;
620 view->s.st_size = view->bytes_read = strlen (view->data);
622 view->last_byte = view->first + view->s.st_size;
624 if (start_line > 1 && !error){
625 int saved_wrap_mode = view->wrap_mode;
627 view->wrap_mode = 0;
628 get_byte (view, 0);
629 view_move_forward (view, start_line - 1);
630 view->wrap_mode = saved_wrap_mode;
632 view->edit_cursor = view->first;
633 view->file_dirty = 0;
634 view->nib_shift = 0;
635 view->view_side = view_side_left;
636 view->change_list = NULL;
638 return 0;
641 void
642 view_update_bytes_per_line(WView *view)
644 int cols;
646 if (view->have_frame)
647 cols = view->widget.cols - 2;
648 else
649 cols = view->widget.cols;
651 view->bottom_first = -1;
652 view->bytes_per_line = ((2 * (cols - 8) / 9) & 0xfffc);
654 if (view->bytes_per_line == 0)
655 view->bytes_per_line++; /* To avoid division by 0 */
657 view->dirty = max_dirt_limit + 1; /* To force refresh */
660 /* Both views */
661 /* Return zero on success, -1 on failure */
663 view_init (WView *view, char *_command, const char *_file, int start_line)
665 view_update_bytes_per_line(view);
667 if (!view->view_active || strcmp (_file, view->filename) || altered_magic_flag)
668 return do_view_init (view, _command, _file, start_line);
669 else
670 return 0;
673 /* }}} */
675 /* {{{ Screen update functions */
677 static void
678 view_percent (WView *view, int p, int w, gboolean update_gui)
680 int percent;
682 percent = (view->s.st_size == 0 || view->last_byte == view->last) ? 100 :
683 (p > (INT_MAX/100) ?
684 p / (view->s.st_size / 100) :
685 p * 100 / view->s.st_size);
687 #if 0
688 percent = view->s.st_size == 0 ? 100 :
689 (view->last_byte == view->last ? 100 :
690 (p)*100 / view->s.st_size);
691 #endif
693 widget_move (view, view->have_frame, w - 5);
694 printw ("%3d%%", percent);
697 static void
698 view_status (WView *view, gboolean update_gui)
700 static int i18n_adjust=0;
701 static char *file_label;
703 int w = view->widget.cols - (view->have_frame * 2);
704 int i;
706 attrset (SELECTED_COLOR);
707 widget_move (view, view->have_frame, view->have_frame);
708 hline (' ', w);
710 if (!i18n_adjust) {
711 file_label = _("File: %s");
712 i18n_adjust = strlen(file_label) - 2;
715 if (w < i18n_adjust + 6)
716 addstr (name_trunc (view->filename ? view->filename:
717 view->command ? view->command:"", w));
718 else{
719 i = (w > 22 ? 22 : w ) - i18n_adjust ;
720 printw (file_label, name_trunc (view->filename ? view->filename:
721 view->command ? view->command:"", i));
722 if (w > 46){
723 widget_move (view, view->have_frame, 24 + view->have_frame );
724 if (view->hex_mode)
725 printw (_("Offset 0x%08x"), view->edit_cursor);
726 else
727 printw (_("Col %d"), -view->start_col);
729 if (w > 62){
730 widget_move (view, view->have_frame, 43 + view->have_frame);
731 printw (_("%s bytes"), size_trunc (view->s.st_size));
733 if (w > 70){
734 printw (" ");
735 if (view->growing_buffer)
736 addstr (_(" [grow]"));
738 if (w > 26) {
739 view_percent (view,
740 view->hex_mode ? view->edit_cursor : view->start_display,
741 view->widget.cols - view->have_frame + 1,
742 update_gui);
745 attrset (SELECTED_COLOR);
748 static inline void
749 view_display_clean (WView *view, int height, int width)
751 /* FIXME: Should I use widget_erase only and repaint the box? */
752 if (view->have_frame){
753 int i;
755 draw_double_box (view->widget.parent, view->widget.y, view->widget.x,
756 view->widget.lines, view->widget.cols);
757 for (i = 1; i < height; i++){
758 widget_move (view, i, 1);
759 printw ("%*s", width-1, "");
761 } else
762 widget_erase ((Widget *) view);
765 #define view_add_character(view,c) addch (c)
766 #define view_add_one_vline() one_vline()
767 #define view_add_string(view,s) addstr (s)
768 #define view_gotoyx(v,r,c) widget_move (v,r,c)
770 #define view_freeze(view)
771 #define view_thaw(view)
773 #define STATUS_LINES 1
775 typedef enum {
776 MARK_NORMAL = 0,
777 MARK_SELECTED = 1,
778 MARK_CURSOR = 2,
779 MARK_CHANGED = 3
780 } mark_t;
782 /* Shows the file pointed to by *start_display on view_win */
783 static long
784 display (WView * view)
786 const int frame_shift = view->have_frame;
787 int col = 0 + frame_shift;
788 int row = STATUS_LINES + frame_shift;
789 int height, width;
790 unsigned long from;
791 int c;
792 mark_t boldflag = MARK_NORMAL;
793 struct hexedit_change_node *curr = view->change_list;
795 height = view->widget.lines - frame_shift;
796 width = view->widget.cols - frame_shift;
797 from = view->start_display;
798 attrset (NORMAL_COLOR);
800 view_freeze (view);
801 view_display_clean (view, height, width);
803 /* Optionally, display a ruler */
804 if ((!view->hex_mode) && (ruler)) {
805 char r_buff[10];
806 int cl;
808 attrset (MARKED_COLOR);
809 for (c = frame_shift; c < width; c++) {
810 cl = c - view->start_col;
811 if (ruler == 1)
812 view_gotoyx (view, row, c);
813 else
814 view_gotoyx (view, row + height - 2, c);
815 r_buff[0] = '-';
816 if ((cl % 10) == 0)
817 r_buff[0] = '|';
818 else if ((cl % 5) == 0)
819 r_buff[0] = '*';
820 view_add_character (view, r_buff[0]);
821 if ((cl != 0) && (cl % 10) == 0) {
822 g_snprintf (r_buff, sizeof (r_buff), "%03d", cl);
823 if (ruler == 1) {
824 widget_move (view, row + 1, c - 1);
825 } else {
826 widget_move (view, row + height - 3, c - 1);
828 view_add_string (view, r_buff);
831 attrset (NORMAL_COLOR);
832 if (ruler == 1)
833 row += 2;
834 else
835 height -= 2;
838 /* Find the first displayable changed byte */
839 while (curr && (curr->offset < from)) {
840 curr = curr->next;
842 if (view->hex_mode) {
843 char hex_buff[10]; /* A temporary buffer for sprintf and mvwaddstr */
844 int bytes; /* Number of bytes already printed on the line */
846 /* Start of text column */
847 int text_start = width - view->bytes_per_line - 1 + frame_shift;
849 for (; row < height && from < view->last_byte; row++) {
850 /* Print the hex offset */
851 attrset (MARKED_COLOR);
852 g_snprintf (hex_buff, sizeof (hex_buff), "%08X",
853 (int) (from - view->first));
854 view_gotoyx (view, row, frame_shift);
855 view_add_string (view, hex_buff);
856 attrset (NORMAL_COLOR);
858 /* Hex dump starts from column nine */
859 col = 9;
861 /* Each hex number is two digits */
862 hex_buff[2] = 0;
863 for (bytes = 0;
864 bytes < view->bytes_per_line && from < view->last_byte;
865 bytes++, from++) {
866 /* Display and mark changed bytes */
867 if (curr && from == curr->offset) {
868 c = curr->value;
869 curr = curr->next;
870 boldflag = MARK_CHANGED;
871 attrset (VIEW_UNDERLINED_COLOR);
872 } else
873 c = (unsigned char) get_byte (view, from);
875 if (view->found_len && from >= view->search_start
876 && from < view->search_start + view->found_len) {
877 boldflag = MARK_SELECTED;
878 attrset (MARKED_COLOR);
880 /* Display the navigation cursor */
881 if (from == view->edit_cursor) {
882 if (view->view_side == view_side_left) {
883 view->cursor_row = row;
884 view->cursor_col = col;
886 boldflag = MARK_CURSOR;
887 attrset (view->view_side ==
888 view_side_left ? VIEW_UNDERLINED_COLOR :
889 MARKED_SELECTED_COLOR);
892 /* Print a hex number (sprintf is too slow) */
893 hex_buff[0] = hex_char[(c >> 4)];
894 hex_buff[1] = hex_char[c & 15];
895 view_gotoyx (view, row, col);
896 view_add_string (view, hex_buff);
897 col += 3;
898 /* Turn off the cursor or changed byte highlighting here */
899 if (boldflag == MARK_CURSOR || boldflag == MARK_CHANGED)
900 attrset (NORMAL_COLOR);
901 if ((bytes & 3) == 3 && bytes + 1 < view->bytes_per_line) {
902 /* Turn off the search highlighting */
903 if (boldflag == MARK_SELECTED
904 && from ==
905 view->search_start + view->found_len - 1)
906 attrset (NORMAL_COLOR);
908 /* Hex numbers are printed in the groups of four */
909 /* Groups are separated by a vline */
911 view_gotoyx (view, row, col - 1);
912 view_add_character (view, ' ');
913 view_gotoyx (view, row, col);
914 view_add_one_vline ();
915 col += 2;
917 if (boldflag != MARK_NORMAL
918 && from ==
919 view->search_start + view->found_len - 1)
920 attrset (MARKED_COLOR);
923 if (boldflag != MARK_NORMAL
924 && from < view->search_start + view->found_len - 1
925 && bytes != view->bytes_per_line - 1) {
926 view_gotoyx (view, row, col);
927 view_add_character (view, ' ');
930 /* Print the corresponding ascii character */
931 view_gotoyx (view, row, text_start + bytes);
933 #ifdef HAVE_CHARSET
934 c = conv_displ[c];
935 #endif
937 if (!is_printable (c))
938 c = '.';
939 switch (boldflag) {
940 case MARK_NORMAL:
941 break;
942 case MARK_SELECTED:
943 attrset (MARKED_COLOR);
944 break;
945 case MARK_CURSOR:
946 if (view->view_side == view_side_right) {
947 /* Our side is active */
948 view->cursor_col = text_start + bytes;
949 view->cursor_row = row;
950 attrset (VIEW_UNDERLINED_COLOR);
951 } else {
952 /* Other side is active */
953 attrset (MARKED_SELECTED_COLOR);
955 break;
956 case MARK_CHANGED:
957 attrset (VIEW_UNDERLINED_COLOR);
958 break;
960 view_add_character (view, c);
962 if (boldflag != MARK_NORMAL) {
963 boldflag = MARK_NORMAL;
964 attrset (NORMAL_COLOR);
968 } else {
969 if (view->growing_buffer && from == view->last_byte)
970 get_byte (view, from);
971 for (; row < height && from < view->last_byte; from++) {
972 c = get_byte (view, from);
973 if ((c == '\n') || (col == width && view->wrap_mode)) {
974 col = frame_shift;
975 row++;
976 if (c == '\n' || row >= height)
977 continue;
979 if (c == '\r')
980 continue;
981 if (c == '\t') {
982 col = ((col - frame_shift) / 8) * 8 + 8 + frame_shift;
983 continue;
985 if (view->viewer_nroff_flag && c == '\b') {
986 int c_prev;
987 int c_next;
989 if (from + 1 < view->last_byte
990 && is_printable ((c_next = get_byte (view, from + 1)))
991 && from > view->first
992 && is_printable ((c_prev = get_byte (view, from - 1)))
993 && (c_prev == c_next || c_prev == '_')) {
994 if (col <= frame_shift) {
995 /* So it has to be wrap_mode - do not need to check for it */
996 if (row == 1 + frame_shift) {
997 from++;
998 continue; /* There had to be a bold character on the rightmost position
999 of the previous undisplayed line */
1001 row--;
1002 col = width;
1004 col--;
1005 boldflag = MARK_SELECTED;
1006 if (c_prev == '_' && c_next != '_')
1007 attrset (VIEW_UNDERLINED_COLOR);
1008 else
1009 attrset (MARKED_COLOR);
1010 continue;
1013 if (view->found_len && from >= view->search_start
1014 && from < view->search_start + view->found_len) {
1015 boldflag = MARK_SELECTED;
1016 attrset (SELECTED_COLOR);
1018 if (col >= frame_shift - view->start_col
1019 && col < width - view->start_col) {
1020 view_gotoyx (view, row, col + view->start_col);
1022 #ifdef HAVE_CHARSET
1023 c = conv_displ[c];
1024 #endif
1026 if (!is_printable (c))
1027 c = '.';
1029 view_add_character (view, c);
1031 col++;
1032 if (boldflag != MARK_NORMAL) {
1033 boldflag = MARK_NORMAL;
1034 attrset (NORMAL_COLOR);
1037 /* Very last thing */
1038 if (view->growing_buffer && from + 1 == view->last_byte)
1039 get_byte (view, from + 1);
1042 view->last = from;
1043 view_thaw (view);
1044 return from;
1047 static void
1048 view_place_cursor (WView *view)
1050 int shift;
1052 if (view->view_side == view_side_left)
1053 shift = view->nib_shift;
1054 else
1055 shift = 0;
1057 widget_move (&view->widget, view->cursor_row, view->cursor_col + shift);
1060 static void
1061 view_update (WView *view, gboolean update_gui)
1063 static int dirt_limit = 1;
1065 if (view->dirty > dirt_limit){
1066 /* Too many updates skipped -> force a update */
1067 display (view);
1068 view_status (view, update_gui);
1069 view->dirty = 0;
1070 /* Raise the update skipping limit */
1071 dirt_limit++;
1072 if (dirt_limit > max_dirt_limit)
1073 dirt_limit = max_dirt_limit;
1075 if (view->dirty){
1076 if (is_idle ()){
1077 /* We have time to update the screen properly */
1078 display (view);
1079 view_status (view, update_gui);
1080 view->dirty = 0;
1081 if (dirt_limit > 1)
1082 dirt_limit--;
1083 } else {
1084 /* We are busy -> skipping full update,
1085 only the status line is updated */
1086 view_status (view, update_gui);
1088 /* Here we had a refresh, if fast scrolling does not work
1089 restore the refresh, although this should not happen */
1093 static inline void
1094 my_define (Dlg_head *h, int idx, char *text,
1095 void (*fn)(WView *), WView *view)
1097 define_label_data (h, (Widget *) view, idx, text, (buttonbarfn) fn, view);
1100 /* }}} */
1101 /* {{{ Movement functions */
1102 /* If the last parameter is nonzero, it means we want get the count of lines
1103 from current up to the the upto position inclusive */
1104 static long
1105 move_forward2 (WView *view, long current, int lines, long upto)
1107 unsigned long q, p;
1108 int line;
1109 int col = 0;
1111 if (view->hex_mode){
1112 p = current + lines * view->bytes_per_line;
1113 p = (p >= view->last_byte) ? current : p;
1114 if (lines == 1) {
1115 q = view->edit_cursor + view->bytes_per_line;
1116 line = q / view->bytes_per_line;
1117 col = (view->last_byte-1) / view->bytes_per_line;
1118 view->edit_cursor = (line > col) ? view->edit_cursor : q;
1119 view->edit_cursor = (view->edit_cursor < view->last_byte) ?
1120 view->edit_cursor : view->last_byte-1;
1121 q = current + ((LINES-2) * view->bytes_per_line);
1122 p = (view->edit_cursor < q) ? current : p;
1123 } else {
1124 view->edit_cursor = (view->edit_cursor < p) ?
1125 p : view->edit_cursor;
1127 return p;
1128 } else {
1129 if (upto){
1130 lines = -1;
1131 q = upto;
1132 } else
1133 q = view->last_byte;
1134 if (get_byte (view, q) != '\n')
1135 q++;
1136 for (line = col = 0, p = current; p < q; p++){
1137 int c;
1139 if (lines != -1 && line >= lines)
1140 return p;
1142 c = get_byte (view, p);
1144 if (view->wrap_mode){
1145 if (c == '\r')
1146 continue; /* This characters is never displayed */
1147 else if (c == '\t')
1148 col = ((col - view->have_frame)/8)*8 +8+ view->have_frame;
1149 else
1150 col++;
1151 if (view->viewer_nroff_flag && c == '\b'){
1152 if (p + 1 < view->last_byte
1153 && is_printable (get_byte (view, p + 1))
1154 && p > view->first
1155 && is_printable (get_byte (view, p - 1)))
1156 col -= 2;
1157 } else if (col == vwidth){
1158 /* FIXME: the c in is_printable was a p, that is a bug,
1159 I suspect I got that fix from Jakub, same applies
1160 for d. */
1161 int d = get_byte (view, p+2);
1163 if (p + 2 >= view->last_byte || !is_printable (c) ||
1164 !view->viewer_nroff_flag || get_byte (view, p + 1) != '\b' ||
1165 !is_printable (d)){
1166 col = 0;
1168 if (c == '\n' || get_byte (view, p+1) != '\n')
1169 line++;
1171 } else if (c == '\n'){
1172 line++;
1173 col = 0;
1175 } else if (c == '\n')
1176 line++;
1178 if (upto)
1179 return line;
1181 return current;
1184 /* returns the new current pointer */
1185 /* Cause even the forward routine became very complex, we in the wrap_mode
1186 just find the nearest '\n', use move_forward2(p, 0, q) to get the count
1187 of lines up to there and then use move_forward2(p, something, 0), which we
1188 return */
1189 static long
1190 move_backward2 (WView *view, unsigned long current, int lines)
1192 long p, q, pm;
1193 int line;
1195 if (!view->hex_mode && current == view->first)
1196 return current;
1198 if (view->hex_mode){
1199 p = current - lines * view->bytes_per_line;
1200 p = (p < view->first) ? view->first : p;
1201 if (lines == 1) {
1202 q = view->edit_cursor - view->bytes_per_line;
1203 view->edit_cursor = (q < view->first) ? view->edit_cursor : q;
1204 p = (view->edit_cursor >= current) ? current : p;
1205 } else {
1206 q = p + ((LINES-2) * view->bytes_per_line);
1207 view->edit_cursor = (view->edit_cursor >= q) ?
1208 p : view->edit_cursor;
1210 return p;
1211 } else {
1212 if (current == view->last_byte
1213 && get_byte (view, current - 1) != '\n')
1214 /* There is one virtual '\n' at the end,
1215 so that the last line is shown */
1216 line = 1;
1217 else
1218 line = 0;
1219 for (q = p = current - 1; p >= view->first; p--)
1220 if (get_byte (view, p) == '\n' || p == view->first) {
1221 pm = p > view->first ? p + 1 : view->first;
1222 if (!view->wrap_mode){
1223 if (line == lines)
1224 return pm;
1225 line++;
1226 } else {
1227 line += move_forward2 (view, pm, 0, q);
1228 if (line >= lines){
1229 if (line == lines)
1230 return pm;
1231 else
1232 return move_forward2 (view, pm, line - lines, 0);
1234 q = p + 1;
1238 return p > view->first ? p : view->first;
1241 static void
1242 view_move_backward (WView *view, int i)
1244 view->search_start = view->start_display =
1245 move_backward2 (view, view->start_display, i);
1246 view->found_len = 0;
1247 view->last = view->first + ((LINES-2) * view->bytes_per_line);
1248 view->dirty++;
1251 static long
1252 get_bottom_first (WView *view, int do_not_cache, int really)
1254 int bottom_first;
1256 if (!have_fast_cpu && !really)
1257 return INT_MAX;
1259 if (!do_not_cache && view->bottom_first != -1)
1260 return view->bottom_first;
1262 /* Force loading */
1263 if (view->growing_buffer){
1264 int old_last_byte;
1266 old_last_byte = -1;
1267 while (old_last_byte != view->last_byte){
1268 old_last_byte = view->last_byte;
1269 get_byte (view, view->last_byte+VIEW_PAGE_SIZE);
1273 bottom_first = move_backward2 (view, view->last_byte, vheight - 1);
1275 if (view->hex_mode)
1276 bottom_first = (bottom_first + view->bytes_per_line - 1)
1277 / view->bytes_per_line * view->bytes_per_line;
1278 view->bottom_first = bottom_first;
1280 return view->bottom_first;
1283 static void
1284 view_move_forward (WView *view, int i)
1286 view->start_display = move_forward2 (view, view->start_display, i, 0);
1287 if (!view->reading_pipe && view->start_display > get_bottom_first (view, 0, 0))
1288 view->start_display = view->bottom_first;
1289 view->search_start = view->start_display;
1290 view->found_len = 0;
1291 view->last = view->first + ((LINES-2) * view->bytes_per_line);
1292 view->dirty++;
1296 static void
1297 move_to_top (WView *view)
1299 view->search_start = view->start_display = view->first;
1300 view->found_len = 0;
1301 view->last = view->first + ((LINES-2) * view->bytes_per_line);
1302 view->nib_shift = 0;
1303 view->edit_cursor = view->start_display;
1304 view->dirty++;
1307 static void
1308 move_to_bottom (WView *view)
1310 view->search_start = view->start_display = get_bottom_first (view, 0, 1);
1311 view->found_len = 0;
1312 view->last = view->first + ((LINES-2) * view->bytes_per_line);
1313 view->edit_cursor = (view->edit_cursor < view->start_display) ?
1314 view->start_display : view->edit_cursor;
1315 view->dirty++;
1318 /* Scroll left/right the view panel functions */
1319 static void
1320 move_right (WView *view)
1322 if (view->wrap_mode && !view->hex_mode)
1323 return;
1324 if (view->hex_mode) {
1325 view->last = view->first + ((LINES-2) * view->bytes_per_line);
1327 if (view->hex_mode && view->view_side == view_side_left) {
1328 view->nib_shift = 1 - view->nib_shift;
1329 if (view->nib_shift == 1)
1330 return;
1332 view->edit_cursor = (++view->edit_cursor < view->last_byte) ?
1333 view->edit_cursor : view->last_byte - 1;
1334 if (view->edit_cursor >= view->last) {
1335 view->edit_cursor -= view->bytes_per_line;
1336 view_move_forward(view, 1);
1338 } else
1339 if (--view->start_col > 0)
1340 view->start_col = 0;
1341 view->dirty++;
1344 static void
1345 move_left (WView *view)
1347 if (view->wrap_mode && !view->hex_mode)
1348 return;
1349 if (view->hex_mode) {
1350 if (view->hex_mode && view->view_side == view_side_left) {
1351 view->nib_shift = 1 - view->nib_shift;
1352 if (view->nib_shift == 0)
1353 return;
1355 if (view->edit_cursor > view->first)
1356 --view->edit_cursor;
1357 if (view->edit_cursor < view->start_display) {
1358 view->edit_cursor += view->bytes_per_line;
1359 view_move_backward(view, 1);
1361 } else
1362 if (++view->start_col > 0)
1363 view->start_col = 0;
1364 view->dirty++;
1367 /* }}} */
1368 /* {{{ Search routines */
1370 /* Case insensitive search of text in data */
1371 static int
1372 icase_search_p (WView *view, char *text, char *data, int nothing)
1374 char *q;
1375 int lng;
1377 if ((q = _icase_search (text, data, &lng)) != 0) {
1378 view->found_len = lng;
1379 view->search_start = q - data - lng;
1380 return 1;
1382 return 0;
1385 static char *
1386 grow_string_buffer (char *text, int *size)
1388 char *new;
1390 /* The grow steps */
1391 *size += 160;
1392 new = g_realloc (text, *size);
1393 if (!text){
1394 *new = 0;
1396 return new;
1399 static char *
1400 get_line_at (WView *view, unsigned long *p, unsigned long *skipped)
1402 char *buffer = 0;
1403 int buffer_size = 0;
1404 int usable_size = 0;
1405 int ch;
1406 int direction = view->direction;
1407 unsigned long pos = *p;
1408 long i = 0;
1409 int prev = 0;
1411 /* skip over all the possible zeros in the file */
1412 while ((ch = get_byte (view, pos)) == 0) {
1413 pos += direction; i++;
1415 *skipped = i;
1417 if (pos) {
1418 prev = get_byte (view, pos - 1);
1419 if ((prev == -1) || (prev == '\n'))
1420 prev = 0;
1423 for (i = 0; ch != -1; ch = get_byte (view, pos)){
1425 if (i == usable_size){
1426 buffer = grow_string_buffer (buffer, &buffer_size);
1427 usable_size = buffer_size - 2;
1430 pos += direction; i++;
1432 if (ch == '\n' || !ch){
1433 break;
1435 buffer [i] = ch;
1437 if (buffer){
1438 buffer [0] = prev;
1439 buffer [i] = 0;
1441 /* If we are searching backwards, reverse the string */
1442 if (direction < 0) {
1443 reverse_string (buffer + 1);
1447 *p = pos;
1448 return buffer;
1451 /** Search status optmizations **/
1453 /* The number of bytes between percent increments */
1454 static int update_steps;
1456 /* Last point where we updated the status */
1457 static long update_activate;
1459 static void
1460 search_update_steps (WView *view)
1462 if (view->s.st_size)
1463 update_steps = 40000;
1464 else
1465 update_steps = view->last_byte / 100;
1467 /* Do not update the percent display but every 20 ks */
1468 if (update_steps < 20000)
1469 update_steps = 20000;
1472 static void
1473 search (WView *view, char *text, int (*search)(WView *, char *, char *, int))
1475 int w = view->widget.cols - view->have_frame + 1;
1477 char *s = NULL; /* The line we read from the view buffer */
1478 long p, beginning;
1479 int found_len, search_start;
1480 int search_status;
1481 Dlg_head *d = 0;
1483 /* Used to keep track of where the line starts, when looking forward */
1484 /* is the index before transfering the line; the reverse case uses */
1485 /* the position returned after the line has been read */
1486 long forward_line_start;
1487 long reverse_line_start;
1488 long t;
1489 /* Clear interrupt status */
1490 got_interrupt ();
1492 if (verbose){
1493 d = message (D_INSERT, _(" Search "), _("Searching %s"), text);
1494 mc_refresh ();
1497 found_len = view->found_len;
1498 search_start = view->search_start;
1500 if (view->direction == 1){
1501 p = found_len ? search_start + 1 : search_start;
1502 } else {
1503 p = (found_len ? search_start : view->last) - 1;
1505 beginning = p;
1507 /* Compute the percent steps */
1508 search_update_steps (view);
1509 update_activate = 0;
1511 for (; ; g_free (s)){
1512 if (p >= update_activate){
1513 update_activate += update_steps;
1514 if (verbose){
1515 view_percent (view, p, w, TRUE);
1516 mc_refresh ();
1518 if (got_interrupt ())
1519 break;
1521 forward_line_start = p;
1522 disable_interrupt_key ();
1523 s = get_line_at (view, &p, &t);
1524 reverse_line_start = p;
1525 enable_interrupt_key ();
1527 if (!s)
1528 break;
1530 search_status = (*search) (view, text, s + 1, match_normal);
1531 if (search_status < 0){
1532 g_free (s);
1533 break;
1536 if (search_status == 0)
1537 continue;
1539 /* We found the string */
1541 if (*s && !view->search_start && (search == regexp_view_search) && (*text == '^')){
1543 /* We do not want to match a
1544 * ^ regexp when not at the real
1545 * beginning of some line
1547 continue;
1549 /* Record the position used to continue the search */
1550 if (view->direction == 1)
1551 t += forward_line_start;
1552 else
1553 t += reverse_line_start ? reverse_line_start + 3 : 0;
1554 view->search_start += t;
1556 if (t != beginning){
1557 if (t > get_bottom_first (view, 0, 0))
1558 view->start_display = view->bottom_first;
1559 else
1560 view->start_display = t;
1563 g_free (s);
1564 break;
1566 disable_interrupt_key ();
1567 if (verbose) {
1568 dlg_run_done (d);
1569 destroy_dlg (d);
1571 if (!s) {
1572 message (0, _(" Search "), _(" Search string not found "));
1573 view->found_len = 0;
1577 /* Search buffer (it's size is len) in the complete buffer */
1578 /* returns the position where the block was found or -1 if not found */
1579 static long
1580 block_search (WView *view, char *buffer, int len)
1582 int w = view->widget.cols - view->have_frame + 1;
1584 char *d = buffer, b;
1585 unsigned long e;
1587 /* clear interrupt status */
1588 got_interrupt ();
1589 enable_interrupt_key ();
1590 e = view->found_len ? view->search_start + 1 : view->search_start;
1592 search_update_steps (view);
1593 update_activate = 0;
1595 while (e < view->last_byte){
1596 if (e >= update_activate){
1597 update_activate += update_steps;
1598 if (verbose){
1599 view_percent (view, e, w, TRUE);
1600 mc_refresh ();
1602 if (got_interrupt ())
1603 break;
1605 b = get_byte (view, e++);
1607 if (*d == b){
1608 d++;
1609 if (d - buffer == len){
1610 disable_interrupt_key ();
1611 return e - len;
1613 } else {
1614 e -= d - buffer;
1615 d = buffer;
1618 disable_interrupt_key ();
1619 return -1;
1623 * Search in the hex mode. Supported input:
1624 * - numbers (oct, dec, hex). Each of them matches one byte.
1625 * - strings in double quotes. Matches exactly without quotes.
1627 static void
1628 hex_search (WView *view, char *text)
1630 char *buffer; /* Parsed search string */
1631 char *cur; /* Current position in it */
1632 int block_len; /* Length of the search string */
1633 long pos; /* Position of the string in the file */
1634 int parse_error = 0;
1636 if (!*text) {
1637 view->found_len = 0;
1638 return;
1641 /* buffer will never be longer that text */
1642 buffer = g_new (char, strlen (text));
1643 cur = buffer;
1645 /* First convert the string to a stream of bytes */
1646 while (*text) {
1647 int val;
1648 int ptr;
1650 /* Skip leading spaces */
1651 if (*text == ' ' || *text == '\t') {
1652 text++;
1653 continue;
1656 /* %i matches octal, decimal, and hexadecimal numbers */
1657 if (sscanf (text, "%i%n", &val, &ptr) > 0) {
1658 /* Allow signed and unsigned char in the user input */
1659 if (val < -128 || val > 255) {
1660 parse_error = 1;
1661 break;
1664 *cur++ = (char) val;
1665 text += ptr;
1666 continue;
1669 /* Try quoted string, strip quotes */
1670 if (*text == '"') {
1671 char *next_quote;
1673 text++;
1674 next_quote = strchr (text, '"');
1675 if (next_quote) {
1676 memcpy (cur, text, next_quote - text);
1677 cur += next_quote - text;
1678 text = next_quote + 1;
1679 continue;
1681 /* fall through */
1684 parse_error = 1;
1685 break;
1688 block_len = cur - buffer;
1690 /* No valid bytes in the user input */
1691 if (block_len <= 0 || parse_error) {
1692 message (0, _(" Search "), _("Invalid hex search expression"));
1693 g_free (buffer);
1694 view->found_len = 0;
1695 return;
1698 /* Then start the search */
1699 pos = block_search (view, buffer, block_len);
1701 g_free (buffer);
1703 if (pos == -1){
1704 message (0, _(" Search "), _(" Search string not found "));
1705 view->found_len = 0;
1706 return;
1709 view->search_start = pos;
1710 view->found_len = block_len;
1711 /* Set the edit cursor to the search position, left nibble */
1712 view->edit_cursor = view->search_start;
1713 view->nib_shift = 0;
1715 /* Adjust the file offset */
1716 view->start_display = (pos & (~(view->bytes_per_line-1)));
1717 if (view->start_display > get_bottom_first (view, 0, 0))
1718 view->start_display = view->bottom_first;
1721 static int regexp_view_search (WView *view, char *pattern, char *string, int match_type)
1723 static regex_t r;
1724 static char *old_pattern = NULL;
1725 static int old_type;
1726 regmatch_t pmatch[1];
1727 int i, flags = REG_ICASE;
1729 if (!old_pattern || strcmp (old_pattern, pattern) || old_type != match_type){
1730 if (old_pattern){
1731 regfree (&r);
1732 g_free (old_pattern);
1733 old_pattern = 0;
1735 for (i = 0; pattern[i] != 0; i++){
1736 if (isupper ((unsigned char) pattern[i])){
1737 flags = 0;
1738 break;
1741 flags |= REG_EXTENDED;
1742 if (regcomp (&r, pattern, flags)){
1743 message (1, MSG_ERROR, _(" Invalid regular expression "));
1744 return -1;
1746 old_pattern = g_strdup (pattern);
1747 old_type = match_type;
1749 if (regexec (&r, string, 1, pmatch, 0) != 0)
1750 return 0;
1751 view->found_len = pmatch[0].rm_eo - pmatch[0].rm_so;
1752 view->search_start = pmatch[0].rm_so;
1753 return 1;
1756 static void do_regexp_search (void *xview, char *regexp)
1758 WView *view = (WView *) xview;
1760 view->search_exp = regexp;
1761 search (view, regexp, regexp_view_search);
1762 /* Had a refresh here */
1763 view->dirty++;
1764 view_update (view, TRUE);
1767 static void do_normal_search (void *xview, char *text)
1769 WView *view = (WView *) xview;
1771 view->search_exp = text;
1772 if (view->hex_mode)
1773 hex_search (view, text);
1774 else
1775 search (view, text, icase_search_p);
1776 /* Had a refresh here */
1777 view->dirty++;
1778 view_update (view, TRUE);
1781 /* }}} */
1782 /* {{{ Mouse and keyboard handling */
1784 /* Real view only */
1785 static void view_help_cmd (void)
1787 interactive_display (NULL, "[Internal File Viewer]");
1789 view_refresh (0);
1793 /* Both views */
1794 static void toggle_wrap_mode (WView *view)
1796 if (view->hex_mode) {
1797 if (view->growing_buffer != 0) {
1798 return;
1800 get_bottom_first (view, 1, 1);
1801 if (view->hexedit_mode) {
1802 view->view_side = 1 - view->view_side;
1803 } else {
1804 view->hexedit_mode = 1 - view->hexedit_mode;
1806 view_labels (view);
1807 view->dirty++;
1808 view_update (view, TRUE);
1809 return;
1811 view->wrap_mode = 1 - view->wrap_mode;
1812 get_bottom_first (view, 1, 1);
1813 if (view->wrap_mode)
1814 view->start_col = 0;
1815 else {
1816 if (have_fast_cpu){
1817 if (view->bottom_first < view->start_display)
1818 view->search_start = view->start_display = view->bottom_first;
1819 view->found_len = 0;
1822 view_labels (view);
1823 view->dirty++;
1824 view_update (view, TRUE);
1827 /* Both views */
1828 static void
1829 toggle_hex_mode (WView *view)
1831 view->hex_mode = 1 - view->hex_mode;
1833 if (view->hex_mode){
1834 /* Shift the line start to 0x____0 on entry, restore it for Ascii */
1835 view->start_save = view->start_display;
1836 view->start_display -= view->start_display % view->bytes_per_line;
1837 view->edit_cursor = view->start_display;
1838 view->widget.options |= W_WANT_CURSOR;
1839 view->widget.parent->flags |= DLG_WANT_TAB;
1840 } else {
1841 view->start_display = view->start_save;
1842 view->widget.parent->flags &= ~DLG_WANT_TAB;
1843 view->widget.options &= ~W_WANT_CURSOR;
1845 altered_hex_mode = 1;
1846 get_bottom_first (view, 1, 1);
1847 view_labels (view);
1848 view->dirty++;
1849 view_update (view, TRUE);
1852 /* Ascii view */
1853 static void
1854 goto_line (WView *view)
1856 char *line, prompt [BUF_SMALL];
1857 int oldline = 1;
1858 int saved_wrap_mode = view->wrap_mode;
1859 unsigned long i;
1861 view->wrap_mode = 0;
1862 for (i = view->first; i < view->start_display; i++)
1863 if (get_byte (view, i) == '\n')
1864 oldline ++;
1865 g_snprintf (prompt, sizeof (prompt), _(" The current line number is %d.\n"
1866 " Enter the new line number:"), oldline);
1867 line = input_dialog (_(" Goto line "), prompt, "");
1868 if (line){
1869 if (*line){
1870 move_to_top (view);
1871 view_move_forward (view, atol (line) - 1);
1873 g_free (line);
1875 view->dirty++;
1876 view->wrap_mode = saved_wrap_mode;
1877 view_update (view, TRUE);
1880 /* Hex view */
1881 static void
1882 goto_addr (WView *view)
1884 char *line, *error, prompt [BUF_SMALL];
1885 unsigned long addr;
1887 g_snprintf (prompt, sizeof (prompt), _(" The current address is 0x%lx.\n"
1888 " Enter the new address:"), view->edit_cursor);
1889 line = input_dialog (_(" Goto Address "), prompt, "");
1890 if (line){
1891 if (*line) {
1892 addr = strtol (line, &error, 0);
1893 if ((*error == '\0') && (addr <= view->last_byte)) {
1894 move_to_top (view);
1895 view_move_forward (view, addr/view->bytes_per_line);
1896 view->edit_cursor = addr;
1899 g_free (line);
1901 view->dirty++;
1902 view_update (view, TRUE);
1905 /* Both views */
1906 static void
1907 regexp_search (WView *view, int direction)
1909 char *regexp = "";
1910 static char *old = 0;
1912 /* This is really an F6 key handler */
1913 if (view->hex_mode){
1914 /* Save it without a confirmation prompt */
1915 if (view->change_list)
1916 save_edit_changes(view);
1917 return;
1920 regexp = old ? old : regexp;
1921 regexp = input_dialog (_(" Search "), _(" Enter regexp:"), regexp);
1922 if ((!regexp)){
1923 return;
1925 if ((!*regexp)){
1926 g_free (regexp);
1927 return;
1929 if (old)
1930 g_free (old);
1931 old = regexp;
1932 #if 0
1933 /* Mhm, do we really need to load all the file in the core? */
1934 if (view->bytes_read < view->last_byte)
1935 get_byte (view, view->last_byte-1);/* Get the whole file in to memory */
1936 #endif
1937 view->direction = direction;
1938 do_regexp_search (view, regexp);
1940 view->last_search = do_regexp_search;
1943 static void
1944 regexp_search_cmd (WView *view)
1946 regexp_search (view, 1);
1949 /* Both views */
1950 static void
1951 normal_search (WView *view, int direction)
1953 static char *old;
1954 char *exp = "";
1956 exp = old ? old : exp;
1958 #ifdef HAVE_CHARSET
1959 if ( *exp )
1960 convert_to_display( exp );
1961 #endif
1963 exp = input_dialog (_(" Search "), _(" Enter search string:"), exp);
1964 if ((!exp)){
1965 return;
1967 if ((!*exp)){
1968 g_free (exp);
1969 return;
1971 if (old)
1972 g_free (old);
1973 old = exp;
1975 #ifdef HAVE_CHARSET
1976 convert_from_input( exp );
1977 #endif
1979 view->direction = direction;
1980 do_normal_search (view, exp);
1981 view->last_search = do_normal_search;
1984 static void
1985 normal_search_cmd (WView *view)
1987 normal_search (view, 1);
1990 static void
1991 change_viewer (WView *view)
1993 char *s;
1994 char *t;
1997 if (*view->filename) {
1998 altered_magic_flag = 1;
1999 view->viewer_magic_flag = !view->viewer_magic_flag;
2000 s = g_strdup (view->filename);
2001 if (view->command)
2002 t = g_strdup (view->command);
2003 else
2004 t = 0;
2006 view_done (view);
2007 view_init (view, t, s, 0);
2008 g_free (s);
2009 if (t)
2010 g_free (t);
2011 view_labels (view);
2012 view->dirty++;
2013 view_update (view, TRUE);
2017 static void
2018 change_nroff (WView *view)
2020 view->viewer_nroff_flag = !view->viewer_nroff_flag;
2021 altered_nroff_flag = 1;
2022 view_labels (view);
2023 view->dirty++;
2024 view_update (view, TRUE);
2027 /* Real view only */
2028 static void
2029 view_quit_cmd (WView *view)
2031 if (view_ok_to_quit (view))
2032 dlg_stop (view->widget.parent);
2035 /* Both views */
2036 static void
2037 view_labels (WView *view)
2039 Dlg_head *h = view->widget.parent;
2041 define_label (h, (Widget *) view, 1, _("Help"), view_help_cmd);
2043 my_define (h, 10, _("Quit"), view_quit_cmd, view);
2044 my_define (h, 4, view->hex_mode ? _("Ascii"): _("Hex"), toggle_hex_mode, view);
2045 my_define (h, 5, view->hex_mode ? _("Goto") : _("Line"),
2046 view->hex_mode ? goto_addr : goto_line,
2047 view);
2048 my_define (h, 6, view->hex_mode ? _("Save") : _("RxSrch"), regexp_search_cmd, view);
2050 my_define (h, 2, view->hex_mode ? view->hexedit_mode ?
2051 view->view_side == view_side_left ? _("EdText") : _("EdHex") :
2052 view->growing_buffer ? "" : _("Edit") :
2053 view->wrap_mode ? _("UnWrap") : _("Wrap"),
2054 toggle_wrap_mode, view);
2056 my_define (h, 7, view->hex_mode ? _("HxSrch") : _("Search"),
2057 normal_search_cmd, view);
2059 my_define (h, 8, view->viewer_magic_flag ? _("Raw") : _("Parse"),
2060 change_viewer, view);
2062 if (!view->have_frame){
2063 my_define (h, 9, view->viewer_nroff_flag ? _("Unform") : _("Format"),
2064 change_nroff, view);
2065 my_define (h, 3, _("Quit"), view_quit_cmd, view);
2068 redraw_labels (h, (Widget *) view);
2071 /* Both views */
2072 static int
2073 check_left_right_keys (WView *view, int c)
2075 if (c == KEY_LEFT)
2076 move_left (view);
2077 else if (c == KEY_RIGHT)
2078 move_right (view);
2079 else return 0;
2081 return 1;
2084 static void
2085 set_monitor (WView *view, int set_on)
2087 int old = view->monitor;
2089 view->monitor = set_on;
2091 if (view->monitor){
2092 move_to_bottom (view);
2093 view->bottom_first = -1;
2094 set_idle_proc (view->widget.parent, 1);
2095 } else {
2096 if (old)
2097 set_idle_proc (view->widget.parent, 0);
2101 static void
2102 continue_search (WView *view)
2104 if (view->last_search){
2105 (*view->last_search)(view, view->search_exp);
2106 } else {
2107 /* if not... then ask for an expression */
2108 normal_search (view, 1);
2112 /* Both views */
2113 static int
2114 view_handle_key (WView *view, int c)
2116 int prev_monitor = view->monitor;
2118 set_monitor (view, off);
2120 #ifdef HAVE_CHARSET
2121 if (c >= 128 && c <= 255) {
2122 c = conv_input[ c ];
2124 #endif
2126 if (view->hex_mode) {
2127 switch (c) {
2128 case 0x09: /* Tab key */
2129 view->view_side = 1 - view->view_side;
2130 view->dirty++;
2131 return 1;
2133 case XCTRL('a'): /* Beginning of line */
2134 view->edit_cursor -= view->edit_cursor % view->bytes_per_line;
2135 view->dirty++;
2136 return 1;
2138 case XCTRL('b'): /* Character back */
2139 move_left(view);
2140 return 1;
2142 case XCTRL('e'): /* End of line */
2143 view->edit_cursor -= view->edit_cursor % view->bytes_per_line;
2144 view->edit_cursor += view->bytes_per_line - 1;
2145 view->dirty++;
2146 return 1;
2148 case XCTRL('f'): /* Character forward */
2149 move_right(view);
2150 return 1;
2153 /* Trap 0-9,A-F,a-f for left side data entry (hex editing) */
2154 if (view->view_side == view_side_left){
2155 if ((c >= '0' && c <= '9') ||
2156 (c >= 'A' && c <= 'F') ||
2157 (c >= 'a' && c <= 'f')){
2159 put_editkey (view, c);
2160 return 1;
2164 /* Trap all printable characters for right side data entry */
2165 /* Also enter the value of the Enter key */
2166 if (view->view_side == view_side_right){
2167 if (c < 256 && (is_printable (c) || (c == '\n'))){
2168 put_editkey(view, c);
2169 return 1;
2174 if (check_left_right_keys (view, c))
2175 return 1;
2177 if (check_movement_keys (c, 1, vheight, view, (movefn) view_move_backward, (movefn) view_move_forward,
2178 (movefn) move_to_top, (movefn) move_to_bottom)){
2179 return 1;
2181 switch (c){
2183 case '?':
2184 regexp_search (view, -1);
2185 return 1;
2187 case '/':
2188 regexp_search (view, 1);
2189 return 1;
2191 /* Continue search */
2192 case XCTRL('s'):
2193 case 'n':
2194 case KEY_F(17):
2195 continue_search (view);
2196 return 1;
2198 case XCTRL('r'):
2199 if (view->last_search){
2200 (*view->last_search)(view, view->search_exp);
2201 } else {
2202 normal_search (view, -1);
2204 return 1;
2206 /* toggle ruler */
2207 case ALT('r'):
2208 switch (ruler){
2209 case 0:
2210 ruler = 1; break;
2211 case 1:
2212 ruler = 2; break;
2213 default:
2214 ruler = 0; break;
2216 view->dirty++;
2217 return 1;
2219 case 'h':
2220 move_left (view);
2221 return 1;
2223 case 'j':
2224 case '\n':
2225 case 'e':
2226 view_move_forward (view, 1);
2227 return 1;
2229 case 'd':
2230 view_move_forward (view, vheight / 2);
2231 return 1;
2233 case 'u':
2234 view_move_backward (view, vheight / 2);
2235 return 1;
2237 case 'k':
2238 case 'y':
2239 view_move_backward (view, 1);
2240 return 1;
2242 case 'l':
2243 move_right (view);
2244 return 1;
2246 case ' ':
2247 case 'f':
2248 view_move_forward (view, vheight - 1);
2249 return 1;
2251 case XCTRL('o'):
2252 view_other_cmd ();
2253 return 1;
2255 /* Unlike Ctrl-O, run a new shell if the subshell is not running. */
2256 case '!':
2257 exec_shell ();
2258 return 1;
2260 case 'F':
2261 set_monitor (view, on);
2262 return 1;
2264 case 'b':
2265 view_move_backward (view, vheight - 1);
2266 return 1;
2268 case KEY_IC:
2269 view_move_backward (view, 2);
2270 return 1;
2272 case KEY_DC:
2273 view_move_forward (view, 2);
2274 return 1;
2276 case 'm':
2277 view->marks [view->marker] = view->start_display;
2278 return 1;
2280 case 'r':
2281 view->start_display = view->marks [view->marker];
2282 view->dirty++;
2283 return 1;
2285 /* Use to indicate parent that we want to see the next/previous file */
2286 /* Only works on full screen mode */
2287 case XCTRL('f'):
2288 case XCTRL('b'):
2289 if (!view->have_frame)
2290 view->move_dir = c == XCTRL('f') ? 1 : -1;
2291 /* fall */
2293 case 'q':
2294 case XCTRL('g'):
2295 case ESC_CHAR:
2296 if (view_ok_to_quit (view))
2297 view->view_quit = 1;
2298 return 1;
2300 #ifdef HAVE_CHARSET
2301 case XCTRL('t'):
2302 do_select_codepage();
2303 view->dirty++;
2304 view_update( view, TRUE );
2305 return 1;
2306 #endif /* HAVE_CHARSET */
2309 if (c >= '0' && c <= '9')
2310 view->marker = c - '0';
2312 /* Restore the monitor status */
2313 set_monitor (view, prev_monitor);
2315 /* Key not used */
2316 return 0;
2319 /* Both views */
2320 static int
2321 view_event (WView *view, Gpm_Event *event, int *result)
2323 *result = MOU_NORMAL;
2324 if (event->type & (GPM_DOWN|GPM_DRAG)){
2325 if (!view->wrap_mode){
2326 if (event->x < view->widget.cols / 4){
2327 move_left (view);
2328 *result = MOU_REPEAT;
2329 return 1;
2331 if (event->x > 3 * vwidth / 4){
2332 move_right (view);
2333 *result = MOU_REPEAT;
2334 return 1;
2337 if (event->y < view->widget.lines / 3){
2338 if (mouse_move_pages_viewer)
2339 view_move_backward (view, view->widget.lines / 2 - 1);
2340 else
2341 view_move_backward (view, 1);
2342 *result = MOU_REPEAT;
2343 return 1;
2345 else if (event->y > 2 * vheight /3){
2346 if (mouse_move_pages_viewer)
2347 view_move_forward (view, vheight / 2 - 1);
2348 else
2349 view_move_forward (view, 1);
2350 *result = MOU_REPEAT;
2351 return 1;
2354 return 0;
2357 /* Real view only */
2358 static int
2359 real_view_event (Gpm_Event *event, void *x)
2361 int result;
2363 if (view_event ((WView *) x, event, &result))
2364 view_update ((WView *) x, TRUE);
2365 return result;
2368 /* }}} */
2369 /* {{{ Window creation, destruction and a driver stub for real view */
2371 void
2372 view_adjust_size (Dlg_head *h)
2374 WView *view;
2375 WButtonBar *bar;
2377 /* Look up the viewer and the buttonbar, we assume only two widgets here */
2378 view = (WView *) find_widget_type (h, (callback_fn) view_callback);
2379 bar = (WButtonBar *) view->widget.parent->current->next->widget;
2380 widget_set_size (&view->widget, 0, 0, LINES-1, COLS);
2381 widget_set_size (&bar->widget, LINES-1, 0, 1, COLS);
2383 view_update_bytes_per_line(view);
2386 /* Only the text mode edition uses this */
2387 Dlg_head *view_dlg;
2389 /* Real view only */
2391 view (char *_command, const char *_file, int *move_dir_p, int start_line)
2393 int error;
2394 WView *wview;
2395 WButtonBar *bar;
2396 Dlg_head *our_dlg;
2398 /* Create dialog and widgets, put them on the dialog */
2399 our_dlg =
2400 create_dlg (0, 0, LINES, COLS, NULL, NULL,
2401 "[Internal File Viewer]", NULL, DLG_NONE);
2403 view_dlg = our_dlg;
2404 wview = view_new (0, 0, COLS, LINES - 1, 0);
2406 bar = buttonbar_new (1);
2408 add_widget (our_dlg, wview);
2409 add_widget (our_dlg, bar);
2411 error = view_init (wview, _command, _file, start_line);
2412 if (move_dir_p)
2413 *move_dir_p = 0;
2415 /* Please note that if you add another widget,
2416 * you have to modify view_adjust_size to
2417 * be aware of it
2419 if (!error) {
2420 run_dlg (our_dlg);
2421 if (move_dir_p)
2422 *move_dir_p = wview->move_dir;
2424 destroy_dlg (our_dlg);
2426 return !error;
2429 static void
2430 view_hook (void *v)
2432 WView *view = (WView *) v;
2433 WPanel *panel;
2435 /* If the user is busy typing, wait until he finishes to update the
2436 screen */
2437 if (!is_idle ()){
2438 if (!hook_present (idle_hook, view_hook))
2439 add_hook (&idle_hook, view_hook, v);
2440 return;
2443 delete_hook (&idle_hook, view_hook);
2445 if (get_current_type () == view_listing)
2446 panel = cpanel;
2447 else if (get_other_type () == view_listing)
2448 panel = other_panel;
2449 else
2450 return;
2452 view_init (view, 0, panel->dir.list [panel->selected].fname, 0);
2453 display (view);
2454 view_status (view, TRUE);
2457 static int
2458 view_callback (Dlg_head *h, WView *view, int msg, int par)
2460 int i;
2462 switch (msg){
2463 case WIDGET_INIT:
2464 if (view->have_frame)
2465 add_hook (&select_file_hook, view_hook, view);
2466 else
2467 view_labels (view);
2468 break;
2470 case WIDGET_DRAW:
2471 display (view);
2472 view_status (view, TRUE);
2473 break;
2475 case WIDGET_CURSOR:
2476 if (view->hex_mode)
2477 view_place_cursor (view);
2478 break;
2480 case WIDGET_KEY:
2481 i = view_handle_key ((WView *)view, par);
2482 if (view->view_quit)
2483 dlg_stop (h);
2484 else {
2485 view_update (view, TRUE);
2487 return i;
2489 case WIDGET_IDLE:
2490 /* This event is generated when the user is using the 'F' flag */
2491 view->bottom_first = -1;
2492 move_to_bottom (view);
2493 display (view);
2494 view_status (view, TRUE);
2495 sleep (1);
2496 return 1;
2498 case WIDGET_FOCUS:
2499 view_labels (view);
2500 return 1;
2503 return default_proc (h, msg, par);
2506 WView *
2507 view_new (int y, int x, int cols, int lines, int is_panel)
2509 WView *view = g_new0 (WView, 1);
2511 init_widget (&view->widget, y, x, lines, cols,
2512 (callback_fn) view_callback,
2513 (destroy_fn) view_destroy,
2514 (mouse_h) real_view_event, NULL);
2516 view->hex_mode = default_hex_mode;
2517 view->hexedit_mode = default_hexedit_mode;
2518 view->viewer_magic_flag = default_magic_flag;
2519 view->viewer_nroff_flag = default_nroff_flag;
2520 view->have_frame = is_panel;
2521 view->last_byte = -1;
2522 view->wrap_mode = global_wrap_mode;
2524 widget_want_cursor (view->widget, 0);
2526 return view;
2529 /* }}} */
2530 /* {{{ Emacs local variables */
2532 Cause emacs to enter folding mode for this file:
2533 Local variables:
2534 end:
2536 /* }}} */