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