4
/* Copyright (c) 1995,1996 Sascha Demetrio
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* If you modify any part of HEXER and resitribute it, you must add
13
* a notice to the `README' file and the modified source files containing
14
* information about the changes you made. I do not want to take
15
* credit or be blamed for your modifications.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
* If you modify any part of HEXER and resitribute it in binary form,
20
* you must supply a `README' file containing information about the
22
* 3. The name of the developer may not be used to endorse or promote
23
* products derived from this software without specific prior written
26
* HEXER WAS DEVELOPED BY SASCHA DEMETRIO.
27
* THIS SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
28
* THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
29
* NOT MAKE USE OF THIS WORK.
32
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
33
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35
* ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE
36
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59
#include <sys/types.h>
60
#include <sys/param.h>
71
extern char *sys_errlist[];
76
const struct buffer_s NO_BUFFER = { 0, 0, 0, 0, 0, 0 };
78
const char *hexer_ext = ".hexer";
80
struct buffer_s *current_buffer;
81
struct buffer_s *buffer_list = 0;
82
struct he_message_s *he_messages;
83
char *alternate_buffer;
88
he_message(int beep, char *fmt, ...)
91
he_message(beep, fmt, va_alist)
98
struct he_message_s *m;
106
/* length = tio_nprintf(fmt, ap); */
107
m = (struct he_message_s *)malloc(sizeof(struct he_message_s));
108
m->next = he_messages;
110
/* m->message = (char *)malloc(length + 1); */
111
m->message = (char *)malloc(512); /* FIXME */
112
vsprintf(m->message, fmt, ap);
123
action_ascii(current_value)
126
he_refresh_all(current_buffer->hedit);
128
s_set_option("iso", "false", 1);
130
s_set_option("iso", "true", 1);
135
action_iso(current_value)
138
he_refresh_all(current_buffer->hedit);
140
s_set_option("ascii", "false", 1);
142
s_set_option("ascii", "true", 1);
147
action_maxmatch(current_value)
150
extern long rx_maxmatch;
152
rx_maxmatch = current_value;
154
/* action_maxmatch */
160
he_refresh_all(current_buffer->hedit);
165
action_specialnl(current_value)
168
extern rx_special_nl;
170
rx_special_nl = current_value;
172
/* action_specialnl */
175
action_mapmagic(current_value)
178
extern he_map_special;
180
he_map_special = current_value;
182
/* action_mapmagic */
184
static const struct hexer_options_s {
186
enum s_option_e type;
189
} hexer_options[] = {
190
{ "ascii", S_BOOL, "true", (set_fn)action_ascii },
191
{ "iso", S_BOOL, "false", (set_fn)action_iso },
192
{ "maxmatch", S_INTEGER, "1024", (set_fn)action_maxmatch },
193
{ "TERM", S_STRING, "", (set_fn)action_TERM }, /* set in `hexer_init()' */
194
{ "specialnl", S_BOOL, "false", (set_fn)action_specialnl },
195
{ "mapmagic", S_BOOL, "false", (set_fn)action_mapmagic },
196
{ "fg", S_INTEGER, "7", (set_fn)0 },
197
{ "bg", S_INTEGER, "4", (set_fn)0 },
198
{ 0, (enum s_option_e)0, 0, 0 }
206
he_open_buffer(name, path)
210
struct buffer_s *buffer;
211
int no_file_f = 0, read_only = 0;
214
char cwd[PATH_MAX + 1];
217
/* check out the read/write permissions */
218
if (access(path, R_OK))
220
case ENOENT: /* file doesn't exist */
224
if (errno >= sys_nerr) /* unknown error */
225
he_message(1, "`%s': @Abunknown error@~", path);
227
he_message(1, "`%s': @Ab%s@~", path, sys_errlist[errno]);
230
if (!no_file_f ? access(path, W_OK) : 0)
232
case EACCES: /* write permission denied */
236
if (errno >= sys_nerr) /* unknown error */
237
he_message(1, "`%s': @Abunknown error@~", path);
239
he_message(1, "`%s': @Ab%s@~", path, sys_errlist[errno]);
243
*(buffer = (struct buffer_s *)malloc(sizeof(struct buffer_s))) = NO_BUFFER;
244
buffer->hedit = (struct he_s *)malloc(sizeof(struct he_s));
245
memset(buffer->hedit, 0, sizeof (struct he_s));
246
buffer->hedit->begin_selection = -1;
247
buffer->hedit->end_selection = -1;
248
buffer->hedit->insert_position = -1;
249
buffer->hedit->buffer_name = strdup(name);
250
if (path && !no_file_f) {
251
if (!(fp = fopen(path, "r"))) {
252
if (errno >= sys_nerr) /* unknown error */
253
he_message(1, "`%s': @Abunknown error@~", path);
255
he_message(1, "`%s': @Ab%s@~", path, sys_errlist[errno]);
256
free((char *)buffer->hedit->buffer_name);
257
free((char *)buffer->hedit);
258
free((char *)buffer);
263
buffer->hedit->buffer = new_buffer(0);
264
buffer->loaded_f = 1;
265
buffer->visited_f = 1;
268
buffer->path = strdup(path);
269
if (!getcwd(cwd, PATH_MAX)) {
270
if (errno >= sys_nerr)
271
he_message(0, "@Abcan't get cwd: unknown error");
273
he_message(0, "@Abcan't get cwd: %s@~", sys_errlist[errno]);
274
buffer->fullpath = strdup(path);
277
(char *)malloc(strlen(path) + strlen(cwd) + 2);
278
sprintf(buffer->fullpath, "%s/%s", cwd, path);
281
buffer->hedit->read_only = read_only;
283
current_buffer = buffer_list = buffer;
285
for (i = buffer_list; i->next; i = i->next);
293
he_select_buffer_(buffer)
294
struct buffer_s *buffer;
295
/* Set `current_buffer' to `buffer'. The file for `buffer' is loaded if
300
char swap_template[8];
302
strcpy(swap_template, ".XXXXXX");
303
for (i = buffer_list; i != buffer && i; i = i->next);
307
i->hedit->buffer = new_buffer(0);
309
if (b_read_buffer_from_file(i->hedit->buffer, i->path) < 0) {
310
delete_buffer(i->hedit->buffer);
311
i->hedit->buffer = 0;
315
/* check out if we can open a swapfile */
317
(char *)malloc(strlen(hexer_ext) + strlen(i->path) + 1);
318
strcpy(i->hedit->swapfile, i->path);
319
strcat(i->hedit->swapfile, hexer_ext);
320
if (access(i->hedit->swapfile, R_OK)) {
321
if (errno == ENOENT) /* swapfile doesn't exist -- fine */
322
if (access(i->hedit->swapfile, W_OK))
323
if (errno == ENOENT) {
324
if ((i->hedit->undo.swapfile = fopen(i->hedit->swapfile, "w+")))
325
i->hedit->swapping = 1;
327
he_message(0, "@Abno swapfile@~");
329
/* a swapfile does exist */
331
he_message(1, "@Abwarning: swapfile@~ `%s' @Abexists@~",
334
(char *)realloc(i->hedit->swapfile,
335
strlen(i->hedit->swapfile) + strlen(swap_template) + 1);
336
strcat(i->hedit->swapfile, swap_template);
337
if ((swapfd = mkstemp(i->hedit->swapfile)) < 0)
338
he_message(0, "@Abno swapfile@~");
340
i->hedit->undo.swapfile = fdopen(swapfd, "r+");
341
i->hedit->swapping = 1;
342
he_message(0, "@Abswapping to@~ `%s'", i->hedit->swapfile);
345
if (i->hedit->swapping) {
346
/* write the swap-header to the swapfile */
347
fputs("hexer ", i->hedit->undo.swapfile);
348
fputs(HEXER_VERSION, i->hedit->undo.swapfile);
349
fputc('\n', i->hedit->undo.swapfile);
350
fputs(i->fullpath, i->hedit->undo.swapfile);
351
fputc('\n', i->hedit->undo.swapfile);
352
fwrite("\0\0\0\0", 4, 1, i->hedit->undo.swapfile);
353
fflush(i->hedit->undo.swapfile);
355
i->hedit->buffer->modified = 0;
361
/* he_select_buffer_ */
364
he_select_buffer(name)
369
for (i = buffer_list; i; i = i->next)
370
if (!strcmp(name, i->hedit->buffer_name)) break;
372
return he_select_buffer_(i);
374
/* he_select_buffer */
377
he_alternate_buffer()
379
char *ab = alternate_buffer;
381
alternate_buffer = current_buffer->hedit->buffer_name;
382
if (ab ? he_select_buffer(ab) < 0 : 0) {
383
alternate_buffer = 0;
388
/* he_alternate_buffer */
391
he_set_buffer_readonly(name)
394
* -1: no buffer named `name'
400
for (i = buffer_list; i; i = i->next)
401
if (!strcmp(name, i->hedit->buffer_name)) break;
403
i->hedit->read_only = 1;
406
/* he_set_buffer_readonly */
409
he_buffer_readonly(name)
412
* -1: no buffer named `name'
413
* 0: buffer is readwrite
414
* 1: buffer is readonly
419
for (i = buffer_list; i; i = i->next)
420
if (!strcmp(name, i->hedit->buffer_name)) break;
422
return !!i->hedit->read_only;
424
/* he_buffer_readonly */
427
he_buffer_modified(name)
430
* -1: no buffer named `name'
437
for (i = buffer_list; i; i = i->next)
438
if (!strcmp(name, i->hedit->buffer_name)) break;
440
return !!i->hedit->buffer->modified;
442
/* he_buffer_modified */
445
he_close_buffer(name)
447
/* Close the buffer named `name'. If `name == 0', the current buffer
448
* is closed. The return value is 0 if all goes well, 1 if the named
449
* buffer doesn't exist and -1 if the `buffer_list' is empty.
452
struct buffer_s *i, *j;
454
int buffer_switched = 0;
456
if (!buffer_list) return -1;
460
for (i = buffer_list; i; i = i->next)
461
if (!strcmp(i->hedit->buffer_name, name)) break;
464
if (i != buffer_list) {
465
for (j = buffer_list; j->next != i; j = j->next);
467
? !strcmp(alternate_buffer, i->hedit->buffer_name) : 0)
468
alternate_buffer = 0;
470
if (i == current_buffer) {
472
he_select_buffer_(i->next);
474
he_select_buffer_(j);
478
if (!(buffer_list = current_buffer = i->next))
481
he_select_buffer_(buffer_list);
484
if (i->hedit->buffer_name) free((char *)i->hedit->buffer_name);
485
if (i->hedit->buffer) delete_buffer(i->hedit->buffer);
486
if (i->hedit->swapping) {
487
fclose(i->hedit->undo.swapfile);
488
unlink(i->hedit->swapfile);
490
if (i->hedit->undo.list) he_free_command(i->hedit->undo.list);
491
free((char *)i->hedit);
492
if (i->path) free((char *)i->path);
494
if (empty) buffer_list = 0, current_buffer = 0;
495
if (buffer_switched) {
496
alternate_buffer = 0;
497
he_refresh_all(current_buffer->hedit);
499
return empty ? -1 : 0;
501
/* he_close_buffer */
508
he_status_message(verbose)
510
/* display name and size of the current buffer. if `verbose' is set,
511
* the current position is also displayed.
514
struct he_s *hedit = current_buffer->hedit;
516
if (hedit->buffer->size)
518
he_message(0, "\"%s\" %s%sat 0x%lx (%li) of 0x%lx (%li) bytes (%li %%)",
520
hedit->buffer->modified ? "[modified] " : "",
521
hedit->read_only ? "[readonly] " : "",
522
hedit->position, hedit->position,
523
hedit->buffer->size, hedit->buffer->size,
524
(hedit->position * 100) / hedit->buffer->size);
526
if (hedit->buffer->size)
527
he_message(0, "\"%s\" %s%s0x%lx (%li) bytes",
529
hedit->buffer->modified ? "[modified] " : "",
530
hedit->read_only ? "[readonly] " : "",
531
hedit->buffer->size, hedit->buffer->size);
533
he_message(0, "\"%s\" %s%s(empty)", hedit->buffer_name,
534
hedit->buffer->modified ? "[modified] " : "",
535
hedit->read_only ? "[readonly] " : "");
537
/* he_status_message */
540
he_query_command(prompt, dfl, context)
545
* `context == 0': exh-command;
546
* `context == 1': shell-command;
547
* `context == 2': search-command.
548
* `context == 3': calculator.
553
tio_goto_line(lines - 1);
555
return readline(prompt, dfl, context);
557
/* he_query_command */
561
he_query_yn(int dfl, char *fmt, ...)
564
he_query_yn(dfl, fmt, va_alist)
574
extern window_changed;
583
tio_goto_line(lines - 1);
586
if (dfl < 0) { /* no default answer */
587
tio_vprintf(fmt, ap);
591
tio_vprintf(fmt, ap);
592
tio_printf("? [%c] ", dfl ? 'y' : 'n');
598
if (window_changed) he_refresh_screen(current_buffer->hedit);
603
goto exit_he_query_yn;
605
tio_printf("escape");
607
goto exit_he_query_yn;
612
case -1: tio_printf("quit"); break;
613
case 0: tio_printf("no"); break;
614
case 1: tio_printf("yes"); break;
615
case 2: tio_printf("always"); break;
617
goto exit_he_query_yn;
621
goto exit_he_query_yn;
625
goto exit_he_query_yn;
627
tio_printf("always");
629
goto exit_he_query_yn;
645
/* I/O wrap-functions for `regex_match()':
648
static Buffer *rxwrap_current_buffer;
649
static long rxwrap_position;
652
rxwrap_read(buf, count)
656
long rval = b_read(rxwrap_current_buffer, buf, rxwrap_position, count);
657
rxwrap_position += rval;
663
rxwrap_seek(position)
666
return rxwrap_position = position;
673
return rxwrap_position;
678
he_search(hedit, exp, replace, direction, wrap, increment, end,
679
replace_str, replace_len, match_len)
682
/* regular expression.
685
/* replace template. the replace template may contain back references to
686
* the regular expression (`\0', ... `\9').
689
/* `direction >= 0': forward search.
690
* `direction < 0': reverse search.
693
/* if `wrap' is set, the search continues at the top of the buffer/file
694
* if the bottom has been (or vice versa, depending on `direction').
697
/* if `increment' is set, the search starts at `hedit->position + 1'
698
* rather than at `hedit->position'. if the direction is set to reverse
699
* search, the `increment'-flag has no effect.
702
/* if `wrap' is not set and `end' is not negative, the search ends at
706
/* if `replace_str' is non-zero and a match was found, the replace
707
* string generated from `replace' will be copied to `*replace_str'.
708
* the memory for that replace string will be allocated via `malloc()'.
709
* NOTE: the replace string wont be terminated with a null-character
710
* since it may contain null characters.
713
/* the length of the replace sting is written to `*replace_len'.
716
/* the length of the match is written to `*replace_len'.
718
/* RETURN VALUE: if a match was found, the position of the match is
719
* returned; -1: search failed; -2: illegal expression (check out
720
* `rx_error'/`rx_error_msg').
721
* NOTE: if the returned value is positive, `*replace_str' has to be
722
* `free()'d by the caller.
728
/* setup the regex I/O */
729
regex_init(rxwrap_read, rxwrap_seek, rxwrap_tell);
730
rxwrap_current_buffer = hedit->buffer;
733
if (wrap || end < 0) end = hedit->buffer->size;
735
if (exp) if (!(regex = regex_compile(exp, replace))) return -2;
736
if (direction < 0) direction = -1; else direction = 1;
738
position = regex_search(regex, 0, end, hedit->position + !!increment,
739
1, replace_str, replace_len, match_len);
740
if (wrap && position < 0) {
741
position = regex_search(regex, 0, end, 0, 1,
742
replace_str, replace_len, match_len);
743
if (position >= 0) he_message(0, "@Abwrapped@~");
746
position = regex_search(regex, 0, end, hedit->position - !!hedit->position,
747
-1, replace_str, replace_len, match_len);
748
if (wrap && position < 0) {
749
position = regex_search(regex, 0, end, hedit->buffer->size - 1,
750
-1, replace_str, replace_len, match_len);
751
if (position >= 0) he_message(0, "@Abwrapped@~");
759
he_search_command(hedit, exp, dir)
767
static char last_exp[4096] = "";
771
he_message(0, "@Abno previous expression@~");
775
switch ((position = he_search(hedit, exp, "", dir, 1, 1, -1,
777
case -2: /* invalid expression */
778
he_message(0, "@Abinvalid expression:@~ %s", rx_error_msg[rx_error]);
780
case -1: /* no match */
782
he_message(0, "no match");
784
he_message(0, "@Abregexp error:@~ %s", rx_error_msg[rx_error]);
787
hedit->position = position;
791
if (exp != last_exp) strcpy(last_exp, exp);
796
/* he_search_command */
799
exh_command( /* struct he_s *hedit, char *cmd */ );
804
he_refresh_all(current_buffer->hedit);
805
he_update_screen(current_buffer->hedit);
812
he_message(0, "@AbHexer@~ version @U%s@u", HEXER_VERSION);
818
/* this function is called by `process_args()' (main.c) before executing
819
* the commands given at the command line.
823
extern he_map_special;
824
extern char *terminal_name;
825
char *hexerinit, *home;
828
extern char *exh_initialize[];
831
he_pagerprg = getenv("PAGER");
832
if (!he_pagerprg) he_pagerprg = HE_DEFAULT_PAGER;
833
for (i = 0; hexer_options[i].option; ++i) {
834
s_set_type(hexer_options[i].option, hexer_options[i].type);
835
s_set_option(hexer_options[i].option, hexer_options[i].default_value, 1);
836
s_set_action(hexer_options[i].option, hexer_options[i].action);
838
s_set_option("TERM", terminal_name, 1);
841
for (i = 0; exh_initialize[i]; ++i)
842
exh_command(current_buffer->hedit, exh_initialize[i]);
844
if (!(home = getenv("HOME"))) home = ".";
845
if (!(hexerinit = getenv("HEXERRC"))) hexerinit = HEXERINIT_FILE;
848
strcat(path, hexerinit);
849
if ((fp = fopen(path, "r"))) {
851
fgets(line, 1024, fp);
853
line[strlen(line) - 1] = 0; /* discard the trailing newline */
854
if (*line && *line != '"')
855
exh_command(current_buffer->hedit, line, 0);
856
/* the command might have quit the editor, so we gotta check */
857
if (!current_buffer) {
859
"warning: a command in your `%s' causes the editor to quit.\n",
873
char dfl[256], buf[256];
875
int anchor, anchor_f = 0;
876
extern int rl_redisplay;
877
extern void (*rl_winch)( /* void */ );
878
extern void (*tio_winch)( /* void */ );
881
tio_winch = rl_winch = he_refresh;
882
he_refresh_part(current_buffer->hedit, 0, -1);
883
for (; buffer_list;) {
884
key = he_mainloop(current_buffer->hedit);
887
case '!': /* shell-command. */
888
he_cancel_selection(current_buffer->hedit);
889
he_update_screen(current_buffer->hedit);
890
cmd = he_query_command("!", "", 1);
891
redisplay = rl_redisplay;
893
strcpy(buf + 1, cmd);
895
exh_command(current_buffer->hedit, buf);
898
case ':': /* exh command. */
899
begin = current_buffer->hedit->begin_selection;
900
end = current_buffer->hedit->end_selection;
901
anchor = current_buffer->hedit->anchor_selection;
902
if (begin >= 0 && end >= begin) {
904
sprintf(dfl, "0x%lx,0x%lx ", begin, end);
909
he_cancel_selection(current_buffer->hedit);
910
cmd = he_query_command(":", dfl, 0);
911
redisplay = rl_redisplay;
913
exh_command(current_buffer->hedit, cmd);
914
if (current_buffer && anchor_f)
915
if (current_buffer->hedit->begin_selection >= 0)
916
current_buffer->hedit->anchor_selection = anchor;
918
case '^' & 0x1f: /* C-^ - switch to alternate buffer */
919
he_cancel_selection(current_buffer->hedit);
920
if (he_alternate_buffer() < 0)
921
he_message(0, "no alternate buffer");
923
he_refresh_part(current_buffer->hedit, 0, -1);
924
tio_ungetch('g' & 0x1f); /* FIXME */
928
he_cancel_selection(current_buffer->hedit);
929
he_update_screen(current_buffer->hedit);
931
if (current_buffer) {
932
if (redisplay) he_refresh_all(current_buffer->hedit);
933
he_update_screen(current_buffer->hedit);
943
/* VIM configuration: (do not delete this line)
945
* vim:aw:bk:bdir=./bak:ch=2:nodg:ef=make.log:efm=%f\:%l\:%m:et:hid:icon:
946
* vim:sw=2:sc:sm:si:textwidth=79:to:ul=1024:wh=12:wrap:wb: