4 * hed - Hexadecimal editor
5 * Copyright (C) 2004 Petr Baudis <pasky@ucw.cz>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of version 2 of the GNU General Public License as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * As Beren looked into her eyes
23 * Within the shadows of her hair,
24 * The trembling starlight of the skies
25 * He saw there mirrored shimmering.
26 * Tinuviel the elven-fair,
27 * Immortal maiden elven-wise,
28 * About him cast her shadowy hair
29 * And arms like silver glimmering.
32 /* Feature macros needed for:
49 #include <config/config.h>
50 #include <libhed/expr.h>
51 #include <libhed/file.h>
52 #include <term/term.h>
55 #include <ui/fileshow.h>
56 #include <ui/inputline.h>
57 #include <util/lists.h>
60 /* For now, we re-request the file fragment each time we redraw the thing.
61 * This might not be feasible in the future. */
63 /* Number of bytes to fetch at cursor position.
64 * Currently, at maximum 4 bytes are needed (for the s32/u32 display).
65 * If you need more in print_fileview_status, feel free to increase
66 * this constant and use the extra bytes.
68 #define CURSOR_LENGTH 4
70 /* TODO: Make the multiclipboard smarter - copy-on-write. */
71 /* TODO: Registers should work as in vim, esp. the unnamed (") register */
72 struct clipboard_register
{
76 static struct clipboard_register clipboard
[1 + 'z' - 'a' + 1 + 'Z' - 'A' + 1 + '9' - '0' + 1];
81 return ('A' <= ch
&& ch
<= 'Z') ? ch
- 'A' :
82 ('a' <= ch
&& ch
<= 'z') ? 26 + ch
- 'a' :
89 return ('0' <= ch
&& ch
<= '9') ? 52 + ch
- '0' :
94 static bool subst_all
;
97 struct fileshow_priv
{
98 struct hed_file
*file
;
99 hed_cursor_t offset
; /* Beginning of screen (file offset) */
101 int width
, height
; /* Width and height of the viewport */
102 ssize_t ssize
; /* Size (in bytes) of the viewport */
104 hed_cursor_t cursor
; /* Cursor position (file offset) */
105 hed_cursor_t visual
; /* Visual mark */
106 int digitpos
; /* Selected digit in the hexadecimal column. */
107 unsigned char curbyte
; /* The value of the current byte under the cursor. */
111 char idmark
; /* insertion/deletion mark at screen start */
113 struct hed_expr
*last_search
;
114 enum { SDIR_FORWARD
= 1, SDIR_BACK
= -1 } last_search_dir
;
116 enum { FSC_HEX
, FSC_ASC
} column
;
121 int le
; /* non-zero if little endian view */
124 static const char idmarks
[] = {
125 '>', ' ', '<', /* insert start, normal, insert end */
126 ')', '|', '(', /* dtto after erase */
129 #define IDMARK_ERASE_IDX 3
131 static enum handler_result
fileshow_handler(struct ui_component
*, const struct ui_event
*);
132 static long eval_reg_cb(void *hookdata
, char reg
, hed_off_t ofs
,
133 unsigned char *scramble
, size_t len
);
134 static long eval_mark_cb(void *hookdata
, char mark
,
135 unsigned char *scramble
, size_t len
);
137 enum ui_mode opt_defmode
= -1;
140 update_idmark(struct fileshow_priv
*data
)
144 /* marks can only happen at zero offset */
145 if (!data
->offset
.off
) {
146 struct hed_tree
*tree
= hed_file_blocks(data
->file
);
148 idx
= !hed_block_is_inserted(data
->offset
.block
);
149 if (hed_block_is_after_erase(tree
, data
->offset
.block
))
150 idx
+= IDMARK_ERASE_IDX
;
151 if (hed_block_is_after_insert(tree
, data
->offset
.block
))
154 data
->idmark
= idmarks
[idx
];
158 pos_column(struct fileshow_priv
*data
, hed_uoff_t pos
)
160 return pos
% data
->width
;
164 set_viewport(struct fileshow_priv
*data
, off_t pos
)
166 hed_get_cursor(data
->file
, pos
, &data
->offset
);
170 /* Update screen offset to make cursor fit into the current viewport */
172 fixup_viewport(struct fileshow_priv
*data
)
174 off_t curline
= data
->cursor
.pos
- pos_column(data
, data
->cursor
.pos
);
176 if (curline
< data
->offset
.pos
)
177 set_viewport(data
, curline
);
178 else if (curline
- data
->ssize
>= data
->offset
.pos
)
179 set_viewport(data
, curline
- data
->ssize
+ data
->width
);
183 set_cursor(struct fileshow_priv
*data
, off_t pos
)
185 hed_get_cursor(data
->file
, pos
, &data
->cursor
);
186 fixup_viewport(data
);
189 /* Update cursor position to fit into the current viewport */
191 fixup_cursor(struct fileshow_priv
*data
)
193 off_t curline
= data
->cursor
.pos
- pos_column(data
, data
->cursor
.pos
);
195 if (curline
< data
->offset
.pos
)
196 hed_move_relative(&data
->cursor
,
197 data
->offset
.pos
- curline
);
198 else if (curline
>= data
->offset
.pos
+ data
->ssize
)
199 hed_move_relative(&data
->cursor
,
200 data
->offset
.pos
+ data
->ssize
-
201 data
->width
- curline
);
205 slide_viewport(struct fileshow_priv
*data
, off_t num
)
207 hed_move_relative(&data
->offset
, num
);
212 /* Initialize the component. */
214 fileshow_init(struct hed_file
*file
)
216 struct fileshow_priv
*data
;
217 struct ui_component
*fileshow
;
220 data
= calloc(1, sizeof(struct fileshow_priv
));
221 if (!data
) goto nomem
;
223 data
->reg
= ch2reg('"');
224 data
->le
= arch_little_endian();
226 if ((int) opt_defmode
>= 0) {
227 data
->mode
= opt_defmode
;
229 char *defmode
= get_opt_str("default_mode", "normal");
230 if (!strcmp(defmode
, "normal")) {
231 data
->mode
= MODE_NORMAL
;
232 } else if (!strcmp(defmode
, "insert")) {
233 data
->mode
= MODE_INSERT
;
234 } else if (!strcmp(defmode
, "replace")) {
235 data
->mode
= MODE_REPLACE
;
237 data
->mode
= MODE_NORMAL
;
241 fileshow
= ui_register(fileshow_handler
, data
);
245 sched_exit(EX_OSERR
);
249 /* TODO: Better height calculation. */
250 data
->height
= term_get_max_y() - 2;
251 width
= (term_get_max_x() - /* ofs */ 8 - /* spaces */ 4);
253 if (!opt_spreadview
) {
254 width
= 1 << bitsize(width
);
255 width
= get_opt_int("bytes_per_line", width
);
258 data
->ssize
= data
->height
* data
->width
;
260 hed_get_cursor(data
->file
, 0, &data
->cursor
);
261 hed_dup_cursor(&data
->cursor
, &data
->offset
);
266 fileshow_done(struct ui_component
*comp
)
272 /* Check whether @pos is inside the visual block */
274 is_inside_visual(struct fileshow_priv
*data
, off_t pos
)
276 return (data
->cursor
.pos
<= pos
&& pos
<= data
->visual
.pos
) ||
277 (data
->visual
.pos
<= pos
&& pos
<= data
->cursor
.pos
);
280 /* Returns ZERO ON ERROR. */
282 visual_extents(struct fileshow_priv
*data
, hed_cursor_t
*base
)
284 hed_cursor_t
*basesrc
;
286 if (data
->visual
.pos
< data
->cursor
.pos
) {
287 ret
= data
->cursor
.pos
- data
->visual
.pos
+ 1;
288 basesrc
= &data
->visual
;
290 ret
= data
->visual
.pos
- data
->cursor
.pos
+ 1;
291 basesrc
= &data
->cursor
;
293 hed_dup2_cursor(basesrc
, base
);
297 #define BYTES_PER_LONG (sizeof(unsigned long))
299 #define DIGITS_MAX(num) ((num + 9)/10)
301 /* The decadic logarithm of 2^8 is approx. 2.408239965311849584,
302 * so 3 digits must be always sufficient to represent the largest
303 * value that can be held in one byte. */
304 #define DIGITS_PER_BYTE 3
307 print_status_num(unsigned char *cursdata
, int nbytes
,
308 int little_endian
, int x
, int y
, int width
)
313 flags
= little_endian
? HED_AEF_FORCELE
: HED_AEF_FORCEBE
;
317 flags
|= HED_AEF_SIGNED
;
320 assert(x
&& cursdata
);
321 assert(nbytes
* 8 <= _FILE_OFFSET_BITS
);
323 num
= hed_bytestr2off(cursdata
, nbytes
, flags
);
325 return term_printf(x
, y
, COLOR_STATUS
, flags
& HED_AEF_SIGNED
328 nbytes
* 8, width
, (long long) num
);
332 print_fileview_status(struct fileshow_priv
*data
,
333 unsigned char *cursdata
, int ofslen
)
335 static char modechar
[] = { 'N', 'R', 'I', 'V' };
337 const int y
= data
->height
;
339 /* First, fill the status line with the status color */
340 term_set_bg_color(COLOR_STATUS
);
341 term_clear_line(data
->height
);
343 /* We print the filename first so that it gets overwritten by
344 * the "more important" stuff. The most important part of the
345 * filename tends to be at the end anyway. */
346 term_print_string(term_get_max_x() - 1 - strlen(data
->file
->name
),
347 y
, COLOR_STATUS
, data
->file
->name
);
349 pos
+= term_printf(0, y
, COLOR_STATUS
, "%0*llx: %03d ", ofslen
,
350 (unsigned long long) data
->cursor
.pos
, *cursdata
);
352 term_print_string(pos
, y
, COLOR_STATUS
, data
->le
? "LE: " : "BE: ");
355 pos
+= print_status_num(cursdata
, 2, data
->le
, pos
, y
, 6);
356 pos
+= print_status_num(cursdata
, -2, data
->le
, pos
, y
, 6);
357 pos
+= print_status_num(cursdata
, 4, data
->le
, pos
, y
, 11);
358 pos
+= print_status_num(cursdata
, -4, data
->le
, pos
, y
, 11);
360 term_print_char(pos
, y
, COLOR_MODE
,
361 hed_file_is_modified(data
->file
) ? '*' : ' ');
362 term_print_char(pos
+ 1, y
, COLOR_MODE
, modechar
[data
->mode
]);
367 num_hex_width(hed_uoff_t num
)
369 /* Offset of the leftmost non-zero bit. */
373 for (i
= sizeof(hed_uoff_t
) * 8 / 2; i
> 0; i
/= 2) {
374 if (num
& (~(hed_uoff_t
)0 << i
)) {
375 num
>>= i
; width
+= i
;
378 /* Round the width up to the hexadecimal places. */
379 return width
/ 4 + 1;
383 fileshow_redraw(struct ui_component
*comp
)
385 struct fileshow_priv
*data
= comp
->data
;
387 char idmark
; /* insertion/deletion marks */
391 int xindent
, xascpos
;
395 unsigned char cursdata
[CURSOR_LENGTH
];
397 if (!hed_prepare_read(data
->file
, &data
->offset
, 1)) {
398 /* This is a fatal error (e.g. no memory) */
399 errmsg("Cannot display file content");
403 /* Offset width is chosen to accomodate the whole file in its current
404 * size as well as the current screen. */
405 maxofs
= data
->offset
.pos
+ data
->ssize
- 1;
406 if (maxofs
< hed_file_size(data
->file
))
407 maxofs
= hed_file_size(data
->file
);
408 ofslen
= num_hex_width(maxofs
);
409 /* Always print at least 4 digits. */
412 xindent
= ofslen
+ 2;
413 xascpos
= xindent
+ data
->width
* 3 + 1;
415 hed_dup_cursor(&data
->offset
, &pos
);
416 idmark
= data
->idmark
;
417 assert(pos
.pos
>= 0);
419 term_set_bg_color(COLOR_NEUTRAL
);
422 p
= hed_cursor_data(&pos
);
424 for (row
= 0; row
< data
->height
; ++row
) {
428 term_printf(0, row
, COLOR_OFFSET
, "%0*llx", ofslen
, pos
.pos
);
430 for (col
= 0; col
< data
->width
; ++col
) {
435 v
= (data
->mode
== MODE_VISUAL
&&
436 is_inside_visual(data
, pos
.pos
))
440 color
= COLOR_DATA_BASE
;
441 if (pos
.pos
>= hed_file_size(data
->file
))
442 color
|= COLOR_DATA_OUTSIDE
;
444 term_color markcolor
= (idmark
== ' ')
445 ? (hed_block_is_dirty(pos
.block
)
449 term_print_char(xindent
+ col
* 3 - 1, row
,
450 markcolor
| v
| oldv
, idmark
);
452 if (pos
.pos
== data
->cursor
.pos
) {
454 cursor_x
= data
->column
== FSC_HEX
455 ? xindent
+ col
* 3 + data
->digitpos
457 color
|= COLOR_DATA_CURSOR
;
458 } else if (hed_block_is_dirty(pos
.block
))
460 if (hed_block_is_bad(pos
.block
))
464 asc
= p
? *p
++ : 0x00;
466 hed_block_is_bad(pos
.block
) ? "XX" : "%02x",
468 asc
= isprint(asc
) ? asc
: asc
== '\0' ? '.' : ':';
470 term_print_string(xindent
+ col
* 3, row
,
472 if (color
>= COLOR_DATA_BASE
)
473 color
|= COLOR_DATA_ASCII
;
474 term_print_char(xascpos
+ col
, row
,
475 color
, (hed_block_is_bad(pos
.block
)
478 if (pos
.pos
>= OFF_MAX
) {
479 term_print_char(xindent
+ col
* 3 + 2, row
,
480 COLOR_MARK
| v
, '!');
484 int idx
= !!hed_block_is_inserted(pos
.block
);
485 hed_move_relative(&pos
, 1);
487 idx
-= !!hed_block_is_inserted(pos
.block
);
488 if (hed_block_is_after_erase(
489 hed_file_blocks(data
->file
),
491 idx
+= IDMARK_ERASE_IDX
;
492 idmark
= idmarks
[idx
+ 1];
494 if (!hed_prepare_read(data
->file
, &pos
, 1)) {
495 /* This is a fatal error */
496 errmsg("Cannot display file content");
500 p
= hed_cursor_data(&pos
);
507 if (idmark
== '<' || idmark
== '(') {
508 term_print_char(xascpos
- 2, row
,
509 COLOR_MARK
| oldv
, idmark
);
512 term_print_char(xascpos
- 2, row
,
513 COLOR_NEUTRAL
| COLOR_VISUAL
, ' ');
516 hed_put_cursor(&pos
);
518 assert(data
->cursor
.pos
>= 0);
520 memset(cursdata
, 0, sizeof(cursdata
));
521 hed_file_cpin(data
->file
, cursdata
, sizeof(cursdata
), &data
->cursor
);
522 data
->curbyte
= cursdata
[0];
524 print_fileview_status(data
, cursdata
, ofslen
);
525 term_unset_bg_color();
528 /* Jump to a position given by offset expression. */
530 jump_offset(struct inputline_data
*inpline
, void *hookdata
)
532 struct fileshow_priv
*data
= hookdata
;
533 struct hed_expr
*expr
= hed_expr_compile(inpline
->buf
);
538 errmsg("Invalid expression");
541 buf
= hed_expr_eval(expr
, eval_reg_cb
, eval_mark_cb
, data
);
543 errmsg("Cannot evaluate expression");
547 pos
= hed_bytestr2off(buf
, hed_expr_len(expr
), hed_expr_flags(expr
));
551 set_cursor(data
, pos
);
555 jump_relative(struct fileshow_priv
*data
, int num
)
557 /* Zero-length jump usually means an invalid jump out of bounds */
558 if (hed_move_relative(&data
->cursor
, num
) == 0)
562 fixup_viewport(data
);
567 finish_insert(struct fileshow_priv
*data
)
569 if (hed_is_a_cursor(&data
->insert
)) {
570 hed_file_insert_end(data
->file
, &data
->insert
);
571 if (data
->column
== FSC_HEX
&& data
->digitpos
)
572 jump_relative(data
, 1);
577 erase_at_cursor(struct fileshow_priv
*data
, hed_uoff_t len
)
579 hed_file_erase_block(data
->file
, &data
->cursor
, len
);
580 if (!hed_is_a_cursor(&data
->offset
)) {
581 hed_dup2_cursor(&data
->cursor
, &data
->offset
);
582 hed_move_relative(&data
->offset
,
583 -pos_column(data
, data
->offset
.pos
));
589 do_search(struct fileshow_priv
*data
)
594 hed_dup_cursor(&data
->cursor
, &off
);
595 hed_move_relative(&off
, data
->last_search_dir
);
596 res
= hed_file_find_expr(data
->file
, &off
, data
->last_search_dir
,
597 data
->last_search
, eval_reg_cb
, data
);
599 set_cursor(data
, off
.pos
);
601 errmsg("No match found");
603 hed_put_cursor(&off
);
607 /* Search for the given string. */
609 expr_search(struct inputline_data
*inpline
, void *hookdata
)
611 struct fileshow_priv
*data
= hookdata
;
613 if (data
->last_search
)
614 hed_expr_free(data
->last_search
);
615 data
->last_search
= hed_expr_compile(inpline
->buf
);
616 if (!data
->last_search
) {
617 errmsg("Invalid expression");
624 /* Search and substitute the given string. */
626 expr_subst(struct inputline_data
*inpline
, void *hookdata
)
628 struct fileshow_priv
*data
= hookdata
;
629 struct hed_expr
*subst
;
632 subst
= hed_expr_compile(inpline
->buf
);
634 errmsg("Invalid expression");
638 data
->last_search_dir
= SDIR_FORWARD
;
639 while (do_search(data
) != HED_FINDOFF_NO_MATCH
) {
640 buf
= hed_expr_eval(subst
, eval_reg_cb
, eval_mark_cb
, data
);
642 errmsg("Cannot evaluate expression");
645 /* Cursor at the match */
646 erase_at_cursor(data
, hed_expr_len(data
->last_search
));
647 hed_file_insert_once(data
->file
, &data
->cursor
,
648 buf
, hed_expr_len(subst
));
652 hed_expr_free(subst
);
656 expr_subst1(struct inputline_data
*inpline
, void *hookdata
)
658 struct fileshow_priv
*data
= hookdata
;
660 if (data
->last_search
)
661 hed_expr_free(data
->last_search
);
662 data
->last_search
= hed_expr_compile(inpline
->buf
);
663 if (!data
->last_search
) {
664 errmsg("Invalid expression");
667 inputline_init("s/.../", expr_subst
, data
);
671 clip_yank(struct fileshow_priv
*data
)
673 hed_cursor_t base
= HED_NULL_CURSOR
;
674 size_t len
= visual_extents(data
, &base
);
675 void *buf
= realloc(clipboard
[data
->reg
].data
, len
);
678 clipboard
[data
->reg
].data
= buf
;
679 len
-= hed_file_cpin(data
->file
, buf
, len
, &base
);
680 clipboard
[data
->reg
].len
= len
;
682 hed_put_cursor(&base
);
685 /* Those functions are totally horrible kludges. There should be specialized
686 * file methods for those operations. */
689 clip_delete(struct fileshow_priv
*data
)
694 len
= visual_extents(data
, &data
->cursor
);
695 erase_at_cursor(data
, len
);
699 clip_put(struct fileshow_priv
*data
)
701 if (!clipboard
[data
->reg
].data
) {
702 errmsg("Register empty");
705 hed_file_insert_once(data
->file
, &data
->cursor
,
706 clipboard
[data
->reg
].data
,
707 clipboard
[data
->reg
].len
);
708 if (data
->offset
.pos
== data
->cursor
.pos
)
709 hed_move_relative(&data
->offset
,
710 -clipboard
[data
->reg
].len
);
714 clip_overwrite(struct fileshow_priv
*data
)
716 hed_cursor_t start
= HED_NULL_CURSOR
;
720 if (!clipboard
[data
->reg
].data
) {
721 errmsg("Register empty");
725 if (data
->mode
== MODE_VISUAL
) {
727 len
= visual_extents(data
, &start
);
730 hed_dup_cursor(&data
->cursor
, &start
);
731 len
= clipboard
[data
->reg
].len
;
734 i
= min(len
, clipboard
[data
->reg
].len
);
735 hed_file_set_block(data
->file
, &start
, clipboard
[data
->reg
].data
, i
);
736 hed_file_set_bytes(data
->file
, &start
, 0, len
- i
);
737 hed_put_cursor(&start
);
741 clip_swap(struct fileshow_priv
*data
)
743 struct clipboard_register orig
, new;
745 new = clipboard
[data
->reg
];
746 clipboard
[data
->reg
].data
= NULL
;
748 orig
= clipboard
[data
->reg
];
749 clipboard
[data
->reg
] = new;
751 clipboard
[data
->reg
] = orig
;
756 set_width(struct inputline_data
*inpline
, void *hookdata
)
758 struct fileshow_priv
*data
= hookdata
;
761 w
= strtol(inpline
->buf
, &l
, 0);
763 errmsg("Invalid width");
771 read_region(struct inputline_data
*inpline
, void *hookdata
)
773 struct fileshow_priv
*data
= hookdata
;
774 char *fname
= inpline
->buf
;
775 int flen
= inpline
->buf_len
;
779 /* Trim leading/trailing whitespaces */
780 while (isspace(*fname
))
782 while (isspace(fname
[flen
- 1]) && flen
> 0)
786 f
= fopen(fname
, "r");
788 errmsg(strerror(errno
));
792 if (data
->mode
== MODE_VISUAL
) {
793 size_t len
= visual_extents(data
, &data
->cursor
);
794 erase_at_cursor(data
, len
);
798 hed_file_insert_begin(data
->file
, &data
->cursor
, &data
->insert
);
799 while ((ch
= fgetc(f
)) != EOF
)
800 hed_file_insert_byte(data
->file
, &data
->insert
, ch
);
806 write_to_file(struct hed_file
*in
, hed_cursor_t
*pos
, hed_uoff_t len
,
810 size_t blen
, written
;
813 if (! (blen
= hed_prepare_read(in
, pos
, len
)) )
816 if ( (p
= hed_cursor_data(pos
)) )
817 written
= fwrite(p
, 1, blen
, out
);
818 else for (written
= 0; written
< blen
; ++written
)
819 if (fputc(0, out
) < 0)
822 hed_move_relative(pos
, written
);
831 write_region(struct inputline_data
*inpline
, void *hookdata
)
833 struct fileshow_priv
*data
= hookdata
;
834 hed_cursor_t base
= HED_NULL_CURSOR
;
836 char *fname
= inpline
->buf
;
837 int flen
= inpline
->buf_len
;
840 /* Trim leading/trailing whitespaces */
841 while (isspace(*fname
))
843 while (isspace(fname
[flen
- 1]) && flen
> 0)
848 f
= fopen(fname
, "w");
850 errmsg(strerror(errno
));
853 len
= visual_extents(data
, &base
);
854 if (write_to_file(data
->file
, &base
, len
, f
))
855 errmsg(strerror(errno
));
856 hed_put_cursor(&base
);
860 /* Arbitrarily chosen... */
861 #define CHUNK_SIZE 1024
864 filter_select(struct fileshow_priv
*data
, hed_cursor_t
*base
,
865 ssize_t len
, int fd_in
, int fd_out
)
870 hed_file_insert_begin(data
->file
, &data
->cursor
, &data
->insert
);
871 while (fd_out
|| fd_in
) {
874 if (fd_in
) FD_SET(fd_in
, &fdr
);
875 if (fd_out
) FD_SET(fd_out
, &fdw
);
877 ret
= select(fd_in
+fd_out
+1, &fdr
, &fdw
, NULL
, NULL
);
882 if (FD_ISSET(fd_in
, &fdr
)) {
883 unsigned char buf
[CHUNK_SIZE
];
884 ret
= read(fd_in
, buf
, CHUNK_SIZE
);
891 hed_file_insert_block(data
->file
, &data
->insert
,
893 data
->cursor
.pos
+= ret
;
896 if (FD_ISSET(fd_out
, &fdw
)) {
897 size_t blen
= min(len
, CHUNK_SIZE
);
898 unsigned char *buf
= malloc(blen
);
899 blen
-= hed_file_cpin(data
->file
, buf
, blen
, base
);
900 if (blen
< CHUNK_SIZE
) {
905 ret
= write(fd_out
, buf
, blen
);
911 hed_move_relative(base
, ret
- blen
);
931 pipe_region(struct inputline_data
*inpline
, void *hookdata
)
933 struct fileshow_priv
*data
= hookdata
;
934 hed_cursor_t base
= HED_NULL_CURSOR
;
935 size_t len
= visual_extents(data
, &base
);
936 char *argv
[4] = { "/bin/sh", "-c", inpline
->buf
, NULL
};
937 int p_rd
[2], p_wr
[2];
940 hed_put_cursor(&data
->visual
);
941 data
->mode
= MODE_NORMAL
;
954 exit(execvp(argv
[0], argv
));
956 } else if (pid
> 0) {
962 set_cursor(data
, base
.pos
+ len
);
963 if (filter_select(data
, &base
, len
, p_wr
[0], p_rd
[1]) < 0) {
964 errmsg("Filter failed");
965 hed_put_cursor(&base
);
968 ret
= waitpid(pid
, &status
, 0);
970 errmsg("Funny zombie");
971 hed_put_cursor(&base
);
974 if (WIFEXITED(status
)) {
975 if (WEXITSTATUS(status
))
976 errmsg("Filter returned error");
977 set_cursor(data
, base
.pos
);
978 erase_at_cursor(data
, len
);
979 hed_put_cursor(&base
);
982 errmsg("Curious error");
983 hed_put_cursor(&base
);
987 errmsg("Fork failed");
988 hed_put_cursor(&base
);
991 hed_put_cursor(&base
);
994 static hed_cursor_t xre
;
997 eval_mark_cb(void *hookdata
, char mark
,
998 unsigned char *scramble
, size_t len
)
1000 struct fileshow_priv
*data
= hookdata
;
1001 int m
= ch2mark(mark
);
1003 if (hed_file_has_mark(data
->file
, m
)) {
1004 hed_off2bytestr(scramble
, len
,
1005 hed_file_mark(data
->file
, m
)->pos
);
1010 hed_off2bytestr(scramble
, len
, data
->cursor
.pos
);
1014 memset(scramble
, 0, len
);
1020 eval_reg_cb(void *hookdata
, char reg
, hed_off_t ofs
,
1021 unsigned char *scramble
, size_t len
)
1023 struct fileshow_priv
*data
= hookdata
;
1026 hed_cursor_t blkpos
= HED_NULL_CURSOR
;
1033 hed_dup_cursor(&data
->cursor
, &blkpos
);
1036 hed_get_cursor(data
->file
, 0, &blkpos
);
1039 if (!hed_is_a_cursor(&xre
))
1041 hed_dup_cursor(&xre
, &blkpos
);
1047 buf
= clipboard
[r
].data
; blen
= clipboard
[r
].len
;
1051 memset(scramble
, 0, -ofs
);
1062 memcpy(scramble
, buf
, blen
);
1064 memset(scramble
+ blen
, 0, len
- blen
);
1068 skip
= hed_move_relative(&blkpos
, ofs
) - ofs
;
1070 if (ofs
>= 0 || skip
>= len
)
1072 memset(scramble
, 0, skip
);
1076 ret
= hed_file_cpin(data
->file
, scramble
, len
, &blkpos
)
1077 ? HED_AEF_ERROR
: 0;
1078 hed_put_cursor(&blkpos
);
1082 memset(scramble
, 0, len
);
1088 ieval_expr(struct inputline_data
*inpline
, void *hookdata
)
1090 struct fileshow_priv
*data
= hookdata
;
1091 struct hed_expr
*expr
= hed_expr_compile(inpline
->buf
);
1095 errmsg("Invalid expression");
1098 buf
= hed_expr_eval(expr
, eval_reg_cb
, eval_mark_cb
, data
);
1100 errmsg("Cannot evaluate expression");
1101 hed_expr_free(expr
);
1104 hed_file_insert_once(data
->file
, &data
->cursor
, buf
, hed_expr_len(expr
));
1105 hed_expr_free(expr
);
1109 peval_expr(struct inputline_data
*inpline
, void *hookdata
)
1111 struct fileshow_priv
*data
= hookdata
;
1112 struct hed_expr
*expr
= hed_expr_compile(inpline
->buf
);
1113 static char *resbuf
;
1119 errmsg("Invalid expression");
1122 buf
= hed_expr_eval(expr
, eval_reg_cb
, eval_mark_cb
, data
);
1124 errmsg("Cannot evaluate expression");
1125 hed_expr_free(expr
);
1128 len
= hed_expr_len(expr
);
1129 p
= realloc(resbuf
, sizeof("Result: -") + 3 * len
);
1131 errmsg("Cannot allocate buffer");
1136 p
= stpcpy(resbuf
, "Result:");
1137 if (hed_expr_flags(expr
) & HED_AEF_NEGATIVE
)
1138 p
= stpcpy(p
, " -");
1139 for (i
= 0; i
< len
; ++i
)
1140 p
+= sprintf(p
, " %02x", buf
[i
]);
1141 hed_expr_free(expr
);
1147 reval_expr(struct inputline_data
*inpline
, void *hookdata
)
1149 struct fileshow_priv
*data
= hookdata
;
1150 struct hed_expr
*expr
= hed_expr_compile(inpline
->buf
);
1152 unsigned char *ebuf
;
1156 errmsg("Invalid expression");
1159 elen
= hed_expr_len(expr
);
1160 len
= visual_extents(data
, &xre
);
1161 for (i
= 0; i
< len
; i
+= elen
) {
1163 ebuf
= hed_expr_eval(expr
, eval_reg_cb
, eval_mark_cb
, data
);
1165 errmsg("Cannot evaluate expression");
1171 hed_file_set_block(data
->file
, &xre
, ebuf
, rlen
);
1173 hed_expr_free(expr
);
1174 hed_put_cursor(&xre
);
1177 if (data
->mode
!= MODE_VISUAL
)
1178 hed_put_cursor(&data
->visual
);
1181 static void exmode(struct inputline_data
*inpline
, void *hookdata
);
1183 /* Evaluate an exmode command. */
1185 exmode_cmd_write(struct fileshow_priv
*data
)
1187 if (data
->mode
== MODE_VISUAL
) {
1188 /* Write just the visual */
1189 inputline_init("File: ", write_region
, data
);
1191 hed_file_commit(data
->file
);
1194 exmode_cmd_read(struct fileshow_priv
*data
)
1196 inputline_init("File: ", read_region
, data
);
1199 exmode_cmd_pipe(struct fileshow_priv
*data
)
1201 inputline_init("Command: ", pipe_region
, data
);
1204 exmode_cmd_subst(struct fileshow_priv
*data
)
1207 inputline_init("s/", expr_subst1
, data
);
1210 exmode_cmd_Subst(struct fileshow_priv
*data
)
1213 inputline_init("S/", expr_subst1
, data
);
1216 exmode_cmd_swp(struct fileshow_priv
*data
)
1218 hed_file_write_swap(data
->file
);
1221 exmode_cmd_ieval(struct fileshow_priv
*data
)
1223 inputline_init("Expression: ", ieval_expr
, data
);
1226 exmode_cmd_reval(struct fileshow_priv
*data
)
1228 if (data
->mode
!= MODE_VISUAL
)
1229 hed_dup_cursor(&data
->cursor
, &data
->visual
);
1230 inputline_init("Expression: ", reval_expr
, data
);
1233 exmode_cmd_peval(struct fileshow_priv
*data
)
1235 inputline_init("Expression: ", peval_expr
, data
);
1238 exmode_cmd_width(struct fileshow_priv
*data
)
1240 inputline_init("Bytes per line: ", set_width
, data
);
1243 exmode_cmd_quit(struct fileshow_priv
*data
)
1248 exmode_cmd_exit(struct fileshow_priv
*data
)
1250 if (hed_file_is_modified(data
->file
))
1251 hed_file_commit(data
->file
);
1252 exmode_cmd_quit(data
);
1260 cmd_clip_paste_after
,
1303 static enum handler_result
1304 do_command(struct fileshow_priv
*data
, enum command cmd
)
1308 /* CURSOR MOVEMENT */
1311 jump_relative(data
, data
->width
);
1315 jump_relative(data
, -data
->width
);
1319 if (data
->column
== FSC_HEX
&& data
->digitpos
> 0) {
1321 } else if (!jump_relative(data
, -1) &&
1322 data
->column
== FSC_HEX
)
1327 if (data
->column
== FSC_HEX
&& data
->digitpos
< 1) {
1330 jump_relative(data
, 1);
1333 case cmd_line_begin
:
1334 if (!jump_relative(data
, -pos_column(data
, data
->cursor
.pos
)))
1339 if (!jump_relative(data
, data
->width
-
1340 pos_column(data
, data
->cursor
.pos
) - 1))
1345 if (hed_file_size(data
->file
)) {
1346 set_cursor(data
, hed_file_size(data
->file
) - 1);
1350 /* else fall-through */
1352 case cmd_file_begin
:
1353 set_cursor(data
, 0);
1358 slide_viewport(data
, data
->ssize
- data
->width
);
1362 slide_viewport(data
, -data
->ssize
+ data
->width
);
1365 case cmd_scroll_down
:
1366 slide_viewport(data
, data
->width
);
1370 slide_viewport(data
, -data
->width
);
1373 /* SIMPLE COMMANDS */
1375 case cmd_switch_column
:
1380 if (data
->mode
== MODE_VISUAL
) {
1381 hed_put_cursor(&data
->visual
);
1382 data
->mode
= MODE_NORMAL
;
1384 data
->mode
= MODE_VISUAL
;
1385 hed_dup_cursor(&data
->cursor
, &data
->visual
);
1389 case cmd_normal_mode
:
1390 if (data
->mode
== MODE_VISUAL
)
1391 hed_put_cursor(&data
->visual
);
1392 data
->mode
= MODE_NORMAL
;
1396 if (data
->mode
== MODE_INSERT
)
1397 data
->mode
= MODE_REPLACE
;
1399 data
->mode
= MODE_INSERT
;
1403 data
->mode
= MODE_REPLACE
;
1407 erase_at_cursor(data
, 1);
1410 case cmd_delchar_back
:
1411 if (data
->cursor
.pos
> 0) {
1412 jump_relative(data
, -1);
1413 erase_at_cursor(data
, 1);
1418 return do_command(data
, (cmd
= data
->mode
== MODE_INSERT
1419 ? cmd_delchar_back
: cmd_go_left
));
1424 if (data
->mode
!= MODE_VISUAL
) {
1425 errmsg("No region selected");
1426 return EVH_SILENTHOLD
;
1429 hed_put_cursor(&data
->visual
);
1430 data
->mode
= MODE_NORMAL
;
1433 case cmd_clip_delete
:
1434 if (data
->mode
!= MODE_VISUAL
) {
1435 errmsg("No region selected");
1436 return EVH_SILENTHOLD
;
1439 hed_put_cursor(&data
->visual
);
1440 data
->mode
= MODE_NORMAL
;
1443 case cmd_clip_paste_after
:
1444 if (data
->mode
!= MODE_VISUAL
)
1445 hed_move_relative(&data
->cursor
, 1);
1447 case cmd_clip_paste
:
1448 if (data
->mode
== MODE_VISUAL
) {
1450 hed_put_cursor(&data
->visual
);
1451 data
->mode
= MODE_NORMAL
;
1454 hed_move_relative(&data
->cursor
, -1);
1458 case cmd_clip_overwrite
:
1459 clip_overwrite(data
);
1460 if (data
->mode
== MODE_VISUAL
) {
1461 hed_put_cursor(&data
->visual
);
1462 data
->mode
= MODE_NORMAL
;
1469 inputline_init(":", exmode
, data
);
1470 return EVH_SILENTHOLD
;
1473 inputline_init("#", jump_offset
, data
);
1474 return EVH_SILENTHOLD
;
1477 data
->last_search_dir
= SDIR_FORWARD
;
1478 inputline_init("/", expr_search
, data
);
1479 return EVH_SILENTHOLD
;
1481 case cmd_search_back
:
1482 data
->last_search_dir
= SDIR_BACK
;
1483 inputline_init("?", expr_search
, data
);
1484 return EVH_SILENTHOLD
;
1486 case cmd_search_prev
:
1487 data
->last_search_dir
= -data
->last_search_dir
;
1489 case cmd_search_next
:
1490 if (data
->last_search
)
1493 errmsg("No previous search");
1495 if (cmd
== cmd_search_prev
)
1496 data
->last_search_dir
= -data
->last_search_dir
;
1502 exmode_cmd_quit(data
);
1506 exmode_cmd_exit(data
);
1510 exmode_cmd_write(data
);
1514 exmode_cmd_read(data
);
1518 if (data
-> mode
== MODE_VISUAL
)
1519 exmode_cmd_pipe(data
);
1523 exmode_cmd_subst(data
);
1527 exmode_cmd_Subst(data
);
1531 exmode_cmd_swp(data
);
1535 exmode_cmd_ieval(data
);
1539 exmode_cmd_reval(data
);
1543 exmode_cmd_peval(data
);
1547 exmode_cmd_width(data
);
1570 exmode(struct inputline_data
*inpline
, void *hookdata
)
1572 static const struct longcmd cmds
[] = {
1574 { "endian", cmd_endian
},
1575 { "exit", cmd_exit
},
1576 { "ie", cmd_ieval
},
1577 { "ieval", cmd_ieval
},
1578 { "pe", cmd_peval
},
1579 { "peval", cmd_peval
},
1580 { "pipe", cmd_pipe
},
1582 { "quit", cmd_quit
},
1584 { "read", cmd_read
},
1585 { "re", cmd_reval
},
1586 { "reval", cmd_reval
},
1588 { "S", cmd_substall
},
1589 { "substall", cmd_substall
},
1590 { "substitute", cmd_subst
},
1593 { "width", cmd_width
},
1594 { "write", cmd_write
},
1599 struct fileshow_priv
*data
= hookdata
;
1600 char *cmd
= inpline
->buf
;
1601 int len
= inpline
->buf_len
;
1602 const struct longcmd
*p
;
1604 /* Trim leading/trailing whitespaces */
1605 while (isspace(*cmd
))
1607 while (isspace(cmd
[len
- 1]) && len
> 0)
1610 for (p
= cmds
; p
->name
; ++p
)
1611 if (!strcmp(cmd
, p
->name
)) {
1612 do_command(data
, p
->cmd
);
1616 errmsg("Invalid command");
1624 #define CTRL(ch) ((ch) - 'A' + 1)
1626 static const struct shortcmd commoncmds
[] = {
1627 { '\e', cmd_normal_mode
},
1628 { KEY_NPAGE
, cmd_page_down
},
1629 { CTRL('F'), cmd_page_down
},
1630 { KEY_PPAGE
, cmd_page_up
},
1631 { CTRL('B'), cmd_page_up
},
1632 { CTRL('E'), cmd_scroll_down
},
1633 { CTRL('Y'), cmd_scroll_up
},
1634 { KEY_HOME
, cmd_file_begin
},
1635 { KEY_END
, cmd_file_end
},
1636 { '\t', cmd_switch_column
},
1637 { KEY_IC
, cmd_insert
},
1638 { KEY_DC
, cmd_delchar
},
1639 { KEY_BACKSPACE
, cmd_backspace
},
1640 { KEY_LEFT
, cmd_go_left
},
1641 { KEY_DOWN
, cmd_go_down
},
1642 { KEY_UP
, cmd_go_up
},
1643 { KEY_RIGHT
, cmd_go_right
},
1644 { KEY_F(2), cmd_write
},
1645 { KEY_F(10), cmd_exit
},
1649 static const struct shortcmd cmds
[] = {
1650 { 'G', cmd_file_end
},
1651 { '0', cmd_line_begin
},
1652 { '^', cmd_line_begin
},
1653 { '$', cmd_line_end
},
1654 { 'h', cmd_go_left
},
1655 { 'j', cmd_go_down
},
1657 { 'l', cmd_go_right
},
1661 { ':', cmd_exmode
},
1663 { '/', cmd_search
},
1664 { '?', cmd_search_back
},
1665 { 'n', cmd_search_next
},
1666 { 'N', cmd_search_prev
},
1667 { 'x', cmd_delchar
},
1668 { 'X', cmd_delchar_back
},
1669 { 'v', cmd_visual
},
1670 { 'y', cmd_clip_yank
},
1671 { 'd', cmd_clip_delete
},
1672 { 'p', cmd_clip_paste_after
},
1673 { 'P', cmd_clip_paste
},
1674 { 'o', cmd_clip_overwrite
},
1675 { 'i', cmd_insert
},
1676 { 'e', cmd_replace
},
1677 { 'R', cmd_replace
},
1678 { 'g', cmd_prefix
},
1679 { 'r', cmd_prefix
},
1680 { 'm', cmd_prefix
},
1681 { '\'', cmd_prefix
},
1682 { '\"', cmd_prefix
},
1686 static const struct shortcmd cmds_g
[] = {
1687 { 'g', cmd_file_begin
},
1688 { KEY_ENTER
, cmd_goto
},
1692 static enum handler_result
1693 handle_shortcmd(struct fileshow_priv
*data
, int ch
,
1694 const struct shortcmd
*table
)
1696 const struct shortcmd
*p
;
1698 for (p
= table
; p
->ch
; ++p
)
1700 return do_command(data
, p
->cmd
);
1705 static enum handler_result
1706 fileshow_normal_in(struct fileshow_priv
*data
, int ch
)
1708 enum handler_result ret
;
1710 finish_insert(data
);
1711 ret
= handle_shortcmd(data
, ch
, commoncmds
);
1712 if (ret
== EVH_PASS
)
1713 ret
= handle_shortcmd(data
, ch
, cmds
);
1717 static enum handler_result
1718 fileshow_normal_g_in(struct fileshow_priv
*data
, int ch
)
1720 return handle_shortcmd(data
, ch
, cmds_g
);
1724 hexdigit_in(struct fileshow_priv
*data
, int ch
, int *byte
)
1726 static const char hx
[] = "0123456789abcdef";
1729 nibble
= strchr(hx
, ch
) - hx
;
1730 assert(nibble
< 16);
1731 if (data
->digitpos
== 0) {
1732 *byte
= (data
->curbyte
& 0xf) | (nibble
<< 4);
1734 *byte
= (data
->curbyte
& 0xf0) | nibble
;
1739 static enum handler_result
1740 fileshow_normal_r_in(struct fileshow_priv
*data
, int ch
)
1742 enum handler_result res
= EVH_PASS
;
1745 if (data
->column
== FSC_HEX
) {
1747 hexdigit_in(data
, tolower(ch
), &byte
);
1748 if (data
->digitpos
> 1) {
1752 /* Keep @xprefix for the second digit. */
1753 res
= EVH_CUSTOM_XPREFIX
;
1756 } else if (ch
== KEY_BACKSPACE
&& data
->digitpos
> 0) {
1758 res
= EVH_CUSTOM_XPREFIX
;
1759 } else if (ch
== '\r' || ch
== '\033') {
1760 /* Cancel the replace. */
1764 /* Persist until the user types something sensible. */
1765 res
= EVH_CUSTOM_XPREFIX
;
1774 if (data
->mode
== MODE_VISUAL
) {
1775 hed_cursor_t base
= HED_NULL_CURSOR
;
1776 size_t len
= visual_extents(data
, &base
);
1777 hed_file_set_bytes(data
->file
, &base
, byte
, len
);
1778 hed_put_cursor(&base
);
1780 hed_file_set_byte(data
->file
, &data
->cursor
, byte
);
1782 data
->curbyte
= byte
;
1788 static enum handler_result
1789 fileshow_normal_m_in(struct fileshow_priv
*data
, int ch
)
1791 int mark
= ch2mark(ch
);
1793 hed_dup2_cursor(&data
->cursor
,
1794 hed_file_mark(data
->file
, mark
));
1796 errmsg("Invalid mark");
1797 return EVH_SILENTHOLD
;
1800 static enum handler_result
1801 fileshow_normal_Qq_in(struct fileshow_priv
*data
, int ch
)
1803 int mark
= ch2mark(ch
);
1805 if (hed_file_has_mark(data
->file
, mark
))
1807 hed_file_mark(data
->file
, mark
)->pos
);
1809 errmsg("No such mark");
1811 errmsg("Invalid mark");
1816 static enum handler_result
1817 fileshow_normal_QQ_in(struct fileshow_priv
*data
, int ch
)
1819 char reg
= ch2reg(ch
);
1823 errmsg("Invalid register");
1825 return EVH_SILENTHOLD
;
1828 static enum handler_result
1829 fileshow_byte_in(struct fileshow_priv
*data
, int ch
)
1831 enum handler_result ret
= EVH_PASS
;
1836 ret
= handle_shortcmd(data
, ch
, commoncmds
);
1837 if (ret
!= EVH_PASS
)
1840 insert
= (data
->mode
== MODE_INSERT
&& data
->cursor
.pos
< OFF_MAX
);
1842 if (insert
&& !hed_is_a_cursor(&data
->insert
)) {
1843 hed_file_insert_begin(data
->file
, &data
->cursor
, &data
->insert
);
1845 if (data
->offset
.pos
== data
->cursor
.pos
) {
1846 hed_dup2_cursor(&data
->insert
, &data
->offset
);
1847 update_idmark(data
);
1852 if (data
->column
== FSC_HEX
) {
1854 if (insert
&& data
->digitpos
== 0)
1856 hexdigit_in(data
, tolower(ch
), &byte
);
1857 if (data
->digitpos
> 1) {
1873 if (data
->column
!= FSC_HEX
) {
1874 hed_file_insert_byte(data
->file
, &data
->insert
, byte
);
1875 } else if (data
->digitpos
> 0) {
1876 hed_dup2_cursor(&data
->insert
, &data
->cursor
);
1877 hed_file_insert_byte(data
->file
, &data
->insert
, byte
);
1879 hed_file_set_byte(data
->file
, &data
->cursor
, byte
);
1880 hed_move_relative(&data
->cursor
, 1);
1882 data
->curbyte
= byte
;
1884 if (data
->cursor
.pos
- data
->ssize
>= data
->offset
.pos
)
1885 slide_viewport(data
, data
->width
);
1887 assert(data
->cursor
.pos
>= data
->offset
.pos
);
1889 hed_file_set_byte(data
->file
, &data
->cursor
, byte
);
1890 data
->curbyte
= byte
;
1892 jump_relative(data
, cursmove
);
1898 static enum handler_result
1899 fileshow_handler(struct ui_component
*comp
, const struct ui_event
*event
)
1901 struct fileshow_priv
*data
= comp
->data
;
1902 enum handler_result res
= EVH_PASS
;
1904 switch (event
->type
) {
1906 fileshow_redraw(comp
);
1910 int ch
= event
->v
.keyboard
.ch
;
1912 if (data
->mode
== MODE_NORMAL
|| data
->mode
== MODE_VISUAL
) {
1913 if (xprefix
== 'g') {
1914 res
= fileshow_normal_g_in(data
, ch
);
1915 } else if (xprefix
== 'r') {
1916 res
= fileshow_normal_r_in(data
, ch
);
1917 } else if (xprefix
== 'm') {
1918 res
= fileshow_normal_m_in(data
, ch
);
1919 } else if (xprefix
== '\'') {
1920 res
= fileshow_normal_Qq_in(data
, ch
);
1921 } else if (xprefix
== '"') {
1922 res
= fileshow_normal_QQ_in(data
, ch
);
1924 res
= fileshow_normal_in(data
, ch
);
1927 res
= fileshow_byte_in(data
, ch
);
1929 if (res
!= EVH_PASS
&& res
!= EVH_SILENTHOLD
)
1930 fileshow_redraw(comp
);
1934 fileshow_done(comp
);
1937 fprintf(stderr
, "Bug: fileshow_handler E.T. %d\n", event
->type
);