1 /* hexer.c 8/19/1995
2 */
3
4 /* Copyright (c) 1995,1996 Sascha Demetrio
5 * Copyright (c) 2009, 2010, 2015, 2016 Peter Pentchev
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * If you modify any part of HEXER and redistribute it, you must add
14 * a notice to the `README' file and the modified source files containing
15 * information about the changes you made. I do not want to take
16 * credit or be blamed for your modifications.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * If you modify any part of HEXER and redistribute it in binary form,
21 * you must supply a `README' file containing information about the
22 * changes you made.
23 * 3. The name of the developer may not be used to endorse or promote
24 * products derived from this software without specific prior written
25 * permission.
26 *
27 * HEXER WAS DEVELOPED BY SASCHA DEMETRIO.
28 * THIS SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
29 * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
30 * NOT MAKE USE OF THIS WORK.
31 *
32 * DISCLAIMER:
33 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
34 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36 * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE
37 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 * SUCH DAMAGE.
44 */
45
46 #include "config.h"
47
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <errno.h>
53 #include <assert.h>
54
55 #include <sys/types.h>
56 #include <sys/param.h>
57
58 #ifndef PATH_MAX
59 #define PATH_MAX 63
60 #endif
61
62 #include "buffer.h"
63 #include "hexer.h"
64 #include "commands.h"
65 #include "exh.h"
66 #include "readline.h"
67 #include "regex.h"
68 #include "set.h"
69 #include "tio.h"
70 #include "util.h"
71
72 const struct buffer_s NO_BUFFER = { 0, 0, 0, 0, 0, 0 };
73
74 const char *hexer_ext = ".hexer";
75
76 struct buffer_s *current_buffer;
77 struct buffer_s *buffer_list = 0;
78 struct he_message_s *he_messages;
79 char *alternate_buffer;
80 const char *he_pagerprg;
81
82 void
he_message(const int beep,const char * const fmt,...)83 he_message(const int beep, const char * const fmt, ...)
84 {
85 va_list ap;
86 struct he_message_s *m;
87 /* int length; */
88
89 va_start(ap, fmt);
90 /* length = tio_nprintf(fmt, ap); */
91 m = (struct he_message_s *)malloc_fatal(sizeof(struct he_message_s));
92 m->next = he_messages;
93 m->beep = beep;
94 /* m->message = (char *)malloc_fatal(length + 1); */
95 m->message = (char *)malloc_fatal(512); /* FIXME */
96 vsprintf(m->message, fmt, ap);
97 he_messages = m;
98 va_end(ap);
99 }
100 /* he_message */
101
102
103 /* hexer options
104 */
105
106 static void
action_ascii(int current_value)107 action_ascii(int current_value)
108 {
109 he_refresh_all(current_buffer->hedit);
110 if (current_value)
111 s_set_option("iso", "false", 1);
112 else
113 s_set_option("iso", "true", 1);
114 }
115 /* action_ascii */
116
117 static void
action_iso(int current_value)118 action_iso(int current_value)
119 {
120 he_refresh_all(current_buffer->hedit);
121 if (current_value)
122 s_set_option("ascii", "false", 1);
123 else
124 s_set_option("ascii", "true", 1);
125 }
126 /* action_iso */
127
128 static void
action_maxmatch(long current_value)129 action_maxmatch(long current_value)
130 {
131 rx_maxmatch = current_value;
132 }
133 /* action_maxmatch */
134
135 static void
action_TERM(void)136 action_TERM(void)
137 {
138 tio_init(0);
139 he_refresh_all(current_buffer->hedit);
140 }
141 /* action_TERM */
142
143 static void
action_specialnl(int current_value)144 action_specialnl(int current_value)
145 {
146 rx_special_nl = current_value;
147 }
148 /* action_specialnl */
149
150 static void
action_mapmagic(int current_value)151 action_mapmagic(int current_value)
152 {
153 he_map_special = current_value;
154 }
155 /* action_mapmagic */
156
157 static const struct hexer_options_s {
158 const char *option;
159 enum s_option_e type;
160 const char *default_value;
161 set_fn action;
162 } hexer_options[] = {
163 { "ascii", S_BOOL, "true", (set_fn)action_ascii },
164 { "iso", S_BOOL, "false", (set_fn)action_iso },
165 { "maxmatch", S_INTEGER, "1024", (set_fn)action_maxmatch },
166 { "TERM", S_STRING, "", (set_fn)action_TERM }, /* set in `hexer_init()' */
167 { "specialnl", S_BOOL, "false", (set_fn)action_specialnl },
168 { "mapmagic", S_BOOL, "false", (set_fn)action_mapmagic },
169 { "fg", S_INTEGER, "7", (set_fn)0 },
170 { "bg", S_INTEGER, "4", (set_fn)0 },
171 { 0, (enum s_option_e)0, 0, 0 }
172 };
173
174
175 /* hexer buffers
176 */
177
178 int
he_open_buffer(const char * const name,const char * const path)179 he_open_buffer(const char * const name, const char * const path)
180 {
181 struct buffer_s *buffer;
182 int no_file_f = 0, read_only = 0;
183 struct buffer_s *i;
184 FILE *fp;
185 char cwd[PATH_MAX + 1];
186
187 if (path) {
188 /* check out the read/write permissions */
189 if (access(path, R_OK))
190 switch (errno) {
191 case ENOENT: /* file doesn't exist */
192 no_file_f = 1;
193 break;
194 default:
195 he_message(1, "`%s': @Ab%s@~", path, strerror(errno));
196 return -1;
197 }
198 if (!no_file_f ? access(path, W_OK) : 0)
199 switch (errno) {
200 case EACCES: /* write permission denied */
201 read_only = 1;
202 break;
203 default:
204 he_message(1, "`%s': @Ab%s@~", path, strerror(errno));
205 return -1;
206 }
207 }
208 *(buffer = (struct buffer_s *)malloc_fatal(sizeof(struct buffer_s))) = NO_BUFFER;
209 buffer->hedit = (struct he_s *)malloc_fatal(sizeof(struct he_s));
210 memset(buffer->hedit, 0, sizeof (struct he_s));
211 buffer->hedit->begin_selection = -1;
212 buffer->hedit->end_selection = -1;
213 buffer->hedit->insert_position = -1;
214 buffer->hedit->buffer_name = strdup_fatal(name);
215 if (path && !no_file_f) {
216 if (!(fp = fopen(path, "r"))) {
217 he_message(1, "`%s': @Ab%s@~", path, strerror(errno));
218 free((char *)buffer->hedit->buffer_name);
219 free((char *)buffer->hedit);
220 free((char *)buffer);
221 return -1;
222 } else
223 fclose(fp);
224 } else {
225 buffer->hedit->buffer = new_buffer(0);
226 buffer->loaded_f = 1;
227 buffer->visited_f = 1;
228 }
229 if (path) {
230 buffer->path = strdup_fatal(path);
231 if (!getcwd(cwd, PATH_MAX)) {
232 he_message(0, "@Abcan't get cwd: %s@~", strerror(errno));
233 buffer->fullpath = strdup_fatal(path);
234 } else {
235 buffer->fullpath =
236 (char *)malloc_fatal(strlen(path) + strlen(cwd) + 2);
237 sprintf(buffer->fullpath, "%s/%s", cwd, path);
238 }
239 }
240 buffer->hedit->read_only = read_only;
241 if (!buffer_list)
242 current_buffer = buffer_list = buffer;
243 else {
244 for (i = buffer_list; i->next; i = i->next);
245 i->next = buffer;
246 }
247 return 0;
248 }
249 /* he_open_buffer */
250
251 int
he_select_buffer_(const struct buffer_s * const buffer)252 he_select_buffer_(const struct buffer_s * const buffer)
253 /* Set `current_buffer' to `buffer'. The file for `buffer' is loaded if
254 * necessary.
255 */
256 {
257 struct buffer_s *i;
258 char swap_template[8];
259
260 strcpy(swap_template, ".XXXXXX");
261 for (i = buffer_list; i != buffer && i; i = i->next);
262 if (!i) return -1;
263 current_buffer = i;
264 if (!i->loaded_f) {
265 i->hedit->buffer = new_buffer(0);
266 /* read the file */
267 if (b_read_buffer_from_file(i->hedit->buffer, i->path) < 0) {
268 delete_buffer(i->hedit->buffer);
269 i->hedit->buffer = 0;
270 he_close_buffer(0);
271 return -1;
272 }
273 /* check out if we can open a swapfile */
274 i->hedit->swapfile =
275 (char *)malloc_fatal(strlen(hexer_ext) + strlen(i->path) + 1);
276 strcpy(i->hedit->swapfile, i->path);
277 strcat(i->hedit->swapfile, hexer_ext);
278 if (access(i->hedit->swapfile, R_OK)) {
279 if (errno == ENOENT) /* swapfile doesn't exist -- fine */
280 if (access(i->hedit->swapfile, W_OK)) {
281 if (errno == ENOENT) {
282 if ((i->hedit->undo.swapfile = fopen(i->hedit->swapfile, "w+")))
283 i->hedit->swapping = 1;
284 } else
285 he_message(0, "@Abno swapfile@~");
286 }
287 } else {
288 /* a swapfile does exist */
289 int swapfd;
290 he_message(1, "@Abwarning: swapfile@~ `%s' @Abexists@~",
291 i->hedit->swapfile);
292 i->hedit->swapfile =
293 (char *)realloc_fatal(i->hedit->swapfile,
294 strlen(i->hedit->swapfile) + strlen(swap_template) + 1);
295 strcat(i->hedit->swapfile, swap_template);
296 if ((swapfd = mkstemp(i->hedit->swapfile)) < 0)
297 he_message(0, "@Abno swapfile@~");
298 else {
299 i->hedit->undo.swapfile = fdopen(swapfd, "r+");
300 i->hedit->swapping = 1;
301 he_message(0, "@Abswapping to@~ `%s'", i->hedit->swapfile);
302 }
303 }
304 if (i->hedit->swapping) {
305 /* write the swap-header to the swapfile */
306 size_t len;
307 char *buf;
308
309 len = 6 + strlen(HEXER_VERSION) + 1 + strlen(i->fullpath) + 1 + 4;
310 buf = alloca(len + 1);
311 if (snprintf(buf, len + 1, "hexer %s\n%s\n%c%c%c%c",
312 HEXER_VERSION, i->fullpath, 0, 0, 0, 0) != (int)len ||
313 fwrite(buf, 1, len, i->hedit->undo.swapfile) != len ||
314 fflush(i->hedit->undo.swapfile) == EOF) {
315 /* TODO: some kind of error output */
316 fclose(i->hedit->undo.swapfile);
317 i->hedit->swapping = 0;
318 }
319 }
320 i->hedit->buffer->modified = 0;
321 i->loaded_f = 1;
322 i->visited_f = 1;
323 }
324 return 0;
325 }
326 /* he_select_buffer_ */
327
328 int
he_select_buffer(const char * const name)329 he_select_buffer(const char * const name)
330 {
331 struct buffer_s *i;
332
333 for (i = buffer_list; i; i = i->next)
334 if (!strcmp(name, i->hedit->buffer_name)) break;
335 if (!i) return -1;
336 return he_select_buffer_(i);
337 }
338 /* he_select_buffer */
339
340 int
he_alternate_buffer(void)341 he_alternate_buffer(void)
342 {
343 char *ab = alternate_buffer;
344
345 alternate_buffer = current_buffer->hedit->buffer_name;
346 if (ab ? he_select_buffer(ab) < 0 : 0) {
347 alternate_buffer = 0;
348 return -1;
349 }
350 return 0;
351 }
352 /* he_alternate_buffer */
353
354 int
he_set_buffer_readonly(const char * const name)355 he_set_buffer_readonly(const char * const name)
356 /* Return values:
357 * -1: no buffer named `name'
358 * 0: ok
359 */
360 {
361 struct buffer_s *i;
362
363 for (i = buffer_list; i; i = i->next)
364 if (!strcmp(name, i->hedit->buffer_name)) break;
365 if (!i) return -1;
366 i->hedit->read_only = 1;
367 return 0;
368 }
369 /* he_set_buffer_readonly */
370
371 int
he_buffer_readonly(char * name)372 he_buffer_readonly(char *name)
373 /* Return values:
374 * -1: no buffer named `name'
375 * 0: buffer is read/write
376 * 1: buffer is read-only
377 */
378 {
379 struct buffer_s *i;
380
381 for (i = buffer_list; i; i = i->next)
382 if (!strcmp(name, i->hedit->buffer_name)) break;
383 if (!i) return -1;
384 return !!i->hedit->read_only;
385 }
386 /* he_buffer_readonly */
387
388 int
he_buffer_modified(char * name)389 he_buffer_modified(char *name)
390 /* Return values:
391 * -1: no buffer named `name'
392 * 0: buffer saved
393 * 1: buffer modified
394 */
395 {
396 struct buffer_s *i;
397
398 for (i = buffer_list; i; i = i->next)
399 if (!strcmp(name, i->hedit->buffer_name)) break;
400 if (!i) return -1;
401 return !!i->hedit->buffer->modified;
402 }
403 /* he_buffer_modified */
404
405 int
he_close_buffer(const char * const name)406 he_close_buffer(const char * const name)
407 /* Close the buffer named `name'. If `name == 0', the current buffer
408 * is closed. The return value is 0 if all goes well, 1 if the named
409 * buffer doesn't exist and -1 if the `buffer_list' is empty.
410 */
411 {
412 struct buffer_s *i, *j;
413 int empty = 0;
414 int buffer_switched = 0;
415
416 if (!buffer_list) return -1;
417 if (!name)
418 i = current_buffer;
419 else {
420 for (i = buffer_list; i; i = i->next)
421 if (!strcmp(i->hedit->buffer_name, name)) break;
422 if (!i) return -1;
423 }
424 if (i != buffer_list) {
425 for (j = buffer_list; j->next != i; j = j->next);
426 if (alternate_buffer
427 ? !strcmp(alternate_buffer, i->hedit->buffer_name) : 0)
428 alternate_buffer = 0;
429 j->next = i->next;
430 if (i == current_buffer) {
431 if (i->next)
432 he_select_buffer_(i->next);
433 else
434 he_select_buffer_(j);
435 buffer_switched = 1;
436 }
437 } else
438 if (!(buffer_list = current_buffer = i->next))
439 empty = 1;
440 else {
441 he_select_buffer_(buffer_list);
442 buffer_switched = 1;
443 }
444 if (i->hedit->buffer_name) free((char *)i->hedit->buffer_name);
445 if (i->hedit->buffer) delete_buffer(i->hedit->buffer);
446 if (i->hedit->swapping) {
447 fclose(i->hedit->undo.swapfile);
448 unlink(i->hedit->swapfile);
449 } else
450 if (i->hedit->undo.list) he_free_command(i->hedit->undo.list);
451 free((char *)i->hedit);
452 if (i->path) free((char *)i->path);
453 free((char *)i);
454 if (empty) buffer_list = 0, current_buffer = 0;
455 if (buffer_switched) {
456 alternate_buffer = 0;
457 he_refresh_all(current_buffer->hedit);
458 }
459 return empty ? -1 : 0;
460 }
461 /* he_close_buffer */
462
463
464 /* misc
465 */
466
467 void
he_status_message(int verbose)468 he_status_message(int verbose)
469 /* display name and size of the current buffer. if `verbose' is set,
470 * the current position is also displayed.
471 */
472 {
473 struct he_s *hedit = current_buffer->hedit;
474
475 if (hedit->buffer->size) {
476 if (verbose) {
477 he_message(0, "\"%s\" %s%sat 0x%lx (%li) of 0x%lx (%li) bytes (%li %%)",
478 hedit->buffer_name,
479 hedit->buffer->modified ? "[modified] " : "",
480 hedit->read_only ? "[readonly] " : "",
481 hedit->position, hedit->position,
482 hedit->buffer->size, hedit->buffer->size,
483 (hedit->position * 100) / hedit->buffer->size);
484 } else {
485 if (hedit->buffer->size)
486 he_message(0, "\"%s\" %s%s0x%lx (%li) bytes",
487 hedit->buffer_name,
488 hedit->buffer->modified ? "[modified] " : "",
489 hedit->read_only ? "[readonly] " : "",
490 hedit->buffer->size, hedit->buffer->size);
491 else
492 he_message(0, "\"%s\" %s%s(empty)", hedit->buffer_name,
493 hedit->buffer->modified ? "[modified] " : "",
494 hedit->read_only ? "[readonly] " : "");
495 }
496 }
497 }
498 /* he_status_message */
499
500 char *
he_query_command(const char * prompt,const char * dfl,int context)501 he_query_command(const char *prompt, const char *dfl, int context)
502 /* Convention:
503 * `context == 0': exh-command;
504 * `context == 1': shell-command;
505 * `context == 2': search-command.
506 * `context == 3': calculator.
507 */
508 {
509 tio_goto_line(hx_lines - 1);
510 tio_return();
511 return readline(prompt, dfl, context);
512 }
513 /* he_query_command */
514
515 int
he_query_yn(int dfl,const char * fmt,...)516 he_query_yn(int dfl, const char *fmt, ...)
517 {
518 va_list ap;
519 int key;
520 int choice;
521
522 va_start(ap, fmt);
523 tio_keypad(0);
524 restart:
525 tio_goto_line(hx_lines - 1);
526 tio_return();
527 tio_clear_to_eol();
528 if (dfl < 0) { /* no default answer */
529 tio_vprintf(fmt, ap);
530 tio_printf("? ");
531 } else {
532 dfl = !!dfl;
533 tio_vprintf(fmt, ap);
534 tio_printf("? [%c] ", dfl ? 'y' : 'n');
535 }
536 for (;;) {
537 key = tio_getch();
538 switch (key) {
539 case HXKEY_NONE:
540 if (window_changed) he_refresh_screen(current_buffer->hedit);
541 goto restart;
542 case 'q': case 'Q':
543 tio_printf("quit");
544 choice = -1;
545 goto exit_he_query_yn;
546 case HXKEY_ESCAPE:
547 tio_printf("escape");
548 choice = -1;
549 goto exit_he_query_yn;
550 case HXKEY_RETURN:
551 if (dfl < 0) break;
552 choice = dfl;
553 switch (choice) {
554 case -1: tio_printf("quit"); break;
555 case 0: tio_printf("no"); break;
556 case 1: tio_printf("yes"); break;
557 case 2: tio_printf("always"); break;
558 }
559 goto exit_he_query_yn;
560 case 'y': case 'Y':
561 tio_printf("yes");
562 choice = 1;
563 goto exit_he_query_yn;
564 case 'n': case 'N':
565 tio_printf("no");
566 choice = 0;
567 goto exit_he_query_yn;
568 case 'a': case 'A':
569 tio_printf("always");
570 choice = 2;
571 goto exit_he_query_yn;
572 default:
573 break;
574 }
575 }
576
577 exit_he_query_yn:
578 tio_return();
579 tio_keypad(1);
580 tio_flush();
581 va_end(ap);
582 return choice;
583 }
584 /* he_query_yn */
585
586
587 /* I/O wrap-functions for `regex_match()':
588 */
589
590 static Buffer *rxwrap_current_buffer;
591 static long rxwrap_position;
592
593 static long
rxwrap_read(char * buf,long count)594 rxwrap_read(char *buf, long count)
595 {
596 long rval = b_read(rxwrap_current_buffer, buf, rxwrap_position, count);
597 rxwrap_position += rval;
598 return rval;
599 }
600 /* rxwrap_read */
601
602 static long
rxwrap_seek(long position)603 rxwrap_seek(long position)
604 {
605 return rxwrap_position = position;
606 }
607 /* rxwrap_seek */
608
609 static long
rxwrap_tell(void)610 rxwrap_tell(void)
611 {
612 return rxwrap_position;
613 }
614 /* rxwrap_tell */
615
616 long
he_search(struct he_s * hedit,const char * exp,const char * replace,int direction,int wrap,int increment,long end,char ** replace_str,long * replace_len,long * match_len)617 he_search(struct he_s *hedit, const char *exp, const char *replace, int direction, int wrap, int increment, long end,
618 char **replace_str, long *replace_len, long *match_len)
619 /* regular expression.
620 */
621 /* replace template. the replace template may contain back references to
622 * the regular expression (`\0', ... `\9').
623 */
624 /* `direction >= 0': forward search.
625 * `direction < 0': reverse search.
626 */
627 /* if `wrap' is set, the search continues from the top of the buffer/file
628 * once the bottom has been passed (or vice versa, depending on `direction').
629 */
630 /* if `increment' is set, the search starts at `hedit->position + 1'
631 * rather than at `hedit->position'. if the direction is set to reverse
632 * search, the `increment' flag has no effect.
633 */
634 /* if `wrap' is not set and `end' is not negative, the search ends at
635 * position `end'.
636 */
637 /* if `replace_str' is non-zero and a match was found, the replace
638 * string generated from `replace' will be copied to `*replace_str'.
639 * the memory for that replace string will be allocated via `malloc()'.
640 * NOTE: the replace string won't be terminated with a null character
641 * since it may contain null characters.
642 */
643 /* the length of the replace sting is written to `*replace_len'.
644 */
645 /* the length of the match is written to `*replace_len'.
646 */
647 /* RETURN VALUE: if a match was found, the position of the match is
648 * returned; -1: search failed; -2: illegal expression (check out
649 * `rx_error'/`rx_error_msg').
650 * NOTE: if the returned value is positive, `*replace_str' has to be
651 * `free()'d by the caller.
652 */
653 {
654 static long *regex;
655 long position;
656
657 /* setup the regex I/O */
658 regex_init(rxwrap_read, rxwrap_seek, rxwrap_tell);
659 rxwrap_current_buffer = hedit->buffer;
660 regex_reset();
661
662 if (wrap || end < 0) end = hedit->buffer->size;
663
664 if (exp) if (!(regex = regex_compile(exp, replace))) return -2;
665 if (direction < 0) direction = -1; else direction = 1;
666 if (direction > 0) {
667 position = regex_search(regex, 0, end, hedit->position + !!increment,
668 1, replace_str, replace_len, match_len);
669 if (wrap && position < 0) {
670 position = regex_search(regex, 0, end, 0, 1,
671 replace_str, replace_len, match_len);
672 if (position >= 0) he_message(0, "@Abwrapped@~");
673 }
674 } else {
675 position = regex_search(regex, 0, end, hedit->position - !!hedit->position,
676 -1, replace_str, replace_len, match_len);
677 if (wrap && position < 0) {
678 position = regex_search(regex, 0, end, hedit->buffer->size - 1,
679 -1, replace_str, replace_len, match_len);
680 if (position >= 0) he_message(0, "@Abwrapped@~");
681 }
682 }
683 return position;
684 }
685 /* he_search */
686
687 void
he_search_command(struct he_s * hedit,char * exp,int dir)688 he_search_command(struct he_s *hedit, char *exp, int dir)
689 {
690 long position;
691 char *rs;
692 long rl, ml;
693 static char last_exp[4096] = "";
694
695 if (!*exp) {
696 if (!*last_exp) {
697 he_message(0, "@Abno previous expression@~");
698 goto exit;
699 } else
700 exp = last_exp;
701 }
702 switch ((position = he_search(hedit, exp, "", dir, 1, 1, -1,
703 &rs, &rl, &ml))) {
704 case -2: /* invalid expression */
705 he_message(0, "@Abinvalid expression:@~ %s", rx_error_msg[rx_error]);
706 goto exit;
707 case -1: /* no match */
708 if (!rx_error)
709 he_message(0, "no match");
710 else
711 he_message(0, "@Abregexp error:@~ %s", rx_error_msg[rx_error]);
712 break;
713 default: /* match */
714 hedit->position = position;
715 free((char *)rs);
716 break;
717 }
718 if (exp != last_exp) strcpy(last_exp, exp);
719
720 exit:
721 return;
722 }
723 /* he_search_command */
724
725 static void
he_refresh(void)726 he_refresh(void)
727 {
728 he_refresh_all(current_buffer->hedit);
729 he_update_screen(current_buffer->hedit);
730 }
731 /* he_refresh */
732
733 void
hexer_version(void)734 hexer_version(void)
735 {
736 he_message(0, "@AbHexer@~ version @U%s@u", HEXER_VERSION);
737 }
738 /* hexer_version */
739
740 void
hexer_init(void)741 hexer_init(void)
742 /* this function is called by `process_args()' (main.c) before executing
743 * the commands given at the command line.
744 */
745 {
746 int i;
747 const char *hexerinit, *home;
748 char path[1024];
749 char line[1024];
750 FILE *fp;
751
752 he_pagerprg = getenv("PAGER");
753 if (!he_pagerprg) he_pagerprg = HE_DEFAULT_PAGER;
754 for (i = 0; hexer_options[i].option; ++i) {
755 s_set_type(hexer_options[i].option, hexer_options[i].type);
756 s_set_option(hexer_options[i].option, hexer_options[i].default_value, 1);
757 s_set_action(hexer_options[i].option, hexer_options[i].action);
758 }
759 s_set_option("TERM", terminal_name, 1);
760
761 he_map_special = 1;
762 for (i = 0; exh_initialize[i]; ++i)
763 exh_command(current_buffer->hedit, exh_initialize[i]);
764
765 if (!(home = getenv("HOME"))) home = ".";
766 if (!(hexerinit = getenv("HEXERRC"))) hexerinit = HEXERINIT_FILE;
767 strcpy(path, home);
768 strcat(path, "/");
769 strcat(path, hexerinit);
770 if ((fp = fopen(path, "r"))) {
771 while (!feof(fp)) {
772 if (fgets(line, 1024, fp) == NULL || !*line)
773 break;
774 line[strlen(line) - 1] = 0; /* discard the trailing newline */
775 if (*line && *line != '"')
776 exh_command(current_buffer->hedit, line);
777 /* the command might have quit the editor, so we gotta check */
778 if (!current_buffer) {
779 fprintf(stderr,
780 "warning: a command in your `%s' causes the editor to quit.\n",
781 hexerinit);
782 break;
783 }
784 }
785 fclose(fp);
786 }
787 }
788 /* hexer_init */
789
790 int
hexer(void)791 hexer(void)
792 {
793 int key;
794 char *cmd;
795 char dfl[256], buf[256];
796 long begin, end;
797 int anchor, anchor_f = 0;
798 int redisplay;
799
800 tio_winch = rl_winch = he_refresh;
801 he_refresh_part(current_buffer->hedit, 0, -1);
802 for (; buffer_list;) {
803 key = he_mainloop(current_buffer->hedit);
804 redisplay = 0;
805 switch (key) {
806 case '!': /* shell-command. */
807 he_cancel_selection(current_buffer->hedit);
808 he_update_screen(current_buffer->hedit);
809 cmd = he_query_command("!", "", 1);
810 redisplay = rl_redisplay;
811 if (cmd) {
812 strcpy(buf + 1, cmd);
813 *buf = '!';
814 exh_command(current_buffer->hedit, buf);
815 }
816 break;
817 case ':': /* exh command. */
818 begin = current_buffer->hedit->begin_selection;
819 end = current_buffer->hedit->end_selection;
820 anchor = current_buffer->hedit->anchor_selection;
821 if (begin >= 0 && end >= begin) {
822 anchor_f = 1;
823 sprintf(dfl, "0x%lx,0x%lx ", begin, end);
824 } else {
825 *dfl = 0;
826 anchor_f = 0;
827 }
828 he_cancel_selection(current_buffer->hedit);
829 cmd = he_query_command(":", dfl, 0);
830 redisplay = rl_redisplay;
831 if (cmd ? *cmd : 0)
832 exh_command(current_buffer->hedit, cmd);
833 if (current_buffer && anchor_f)
834 if (current_buffer->hedit->begin_selection >= 0)
835 current_buffer->hedit->anchor_selection = anchor;
836 break;
837 case '^' & 0x1f: /* C-^ - switch to alternate buffer */
838 he_cancel_selection(current_buffer->hedit);
839 if (he_alternate_buffer() < 0)
840 he_message(0, "no alternate buffer");
841 else {
842 he_refresh_part(current_buffer->hedit, 0, -1);
843 tio_ungetch('g' & 0x1f); /* FIXME */
844 }
845 break;
846 default:
847 he_cancel_selection(current_buffer->hedit);
848 he_update_screen(current_buffer->hedit);
849 }
850 if (current_buffer) {
851 if (redisplay) he_refresh_all(current_buffer->hedit);
852 he_update_screen(current_buffer->hedit);
853 }
854 }
855 return 0;
856 }
857 /* hexer */
858
859 /* end of hexer.c */
860
861
862 /* VIM configuration: (do not delete this line)
863 *
864 * vim:bk:nodg:efm=%f\:%l\:%m:hid:icon:
865 * vim:sw=2:sm:textwidth=79:ul=1024:wrap:
866 */
867