1 /*
2 * hist - interactive readline module
3 *
4 * Copyright (C) 1999-2007,2021 David I. Bell
5 *
6 * Calc is open software; you can redistribute it and/or modify it under
7 * the terms of the version 2.1 of the GNU Lesser General Public License
8 * as published by the Free Software Foundation.
9 *
10 * Calc is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
13 * Public License for more details.
14 *
15 * A copy of version 2.1 of the GNU Lesser General Public License is
16 * distributed with calc under the filename COPYING-LGPL. You should have
17 * received a copy with calc; if not, write to Free Software Foundation, Inc.
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 * Under source code control: 1993/05/02 20:09:19
21 * File existed as early as: 1993
22 *
23 * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
24 */
25
26 /*
27 * Adapted from code written by Stephen Rothwell.
28 *
29 * GNU readline support added by Martin Buck <mbuck at debian dot org>
30 *
31 * Interactive readline module. This is called to read lines of input,
32 * while using emacs-like editing commands within a command stack.
33 * The key bindings for the editing commands are (slightly) configurable.
34 */
35
36
37 #include <stdio.h>
38 #include <ctype.h>
39 #if !defined(_WIN32) && !defined(_WIN64)
40 # include <pwd.h>
41 #endif
42
43 #include "have_unistd.h"
44 #if defined(HAVE_UNISTD_H)
45 #include <unistd.h>
46 #endif
47
48 #include "have_stdlib.h"
49 #if defined(HAVE_STDLIB_H)
50 #include <stdlib.h>
51 #endif
52
53 #include "calc.h"
54 #include "lib_calc.h"
55 #include "alloc.h"
56 #include "hist.h"
57 #include "strl.h"
58
59 #include "have_strdup.h"
60 #if !defined(HAVE_STRDUP)
61 # define strdup(x) calc_strdup((CONST char *)(x))
62 #endif /* HAVE_STRDUP */
63
64 #include "have_string.h"
65 #ifdef HAVE_STRING_H
66 # include <string.h>
67 #endif
68
69 #include "have_unused.h"
70
71
72 #include "banned.h" /* include after system header <> includes */
73
74
75 #if !defined(USE_READLINE)
76
77 E_FUNC FILE *curstream(void);
78
79 #define STDIN 0
80 #define SAVE_SIZE 256 /* size of save buffer */
81 #define MAX_KEYS 60 /* number of key bindings */
82
83
84 #define CONTROL(x) ((char)(((int)(x)) & 0x1f))
85
86 STATIC struct {
87 char *prompt;
88 char *buf;
89 char *pos;
90 char *end;
91 char *mark;
92 int bufsize;
93 int linelen;
94 int histcount; /* valid history entries */
95 int curhist;
96 BOOL virgin_line; /* 1 => never typed chars, 0 => chars typed */
97 } HS;
98
99
100 typedef void (*FUNCPTR)();
101
102 typedef struct {
103 char *name;
104 FUNCPTR func;
105 } FUNC;
106
107 /* declare binding functions */
108 S_FUNC void flush_input(void);
109 S_FUNC void start_of_line(void);
110 S_FUNC void end_of_line(void);
111 S_FUNC void forward_char(void);
112 S_FUNC void backward_char(void);
113 S_FUNC void forward_word(void);
114 S_FUNC void backward_word(void);
115 S_FUNC void delete_char(void);
116 S_FUNC void forward_kill_char(void);
117 S_FUNC void backward_kill_char(void);
118 S_FUNC void forward_kill_word(void);
119 S_FUNC void kill_line(void);
120 S_FUNC void new_line(void);
121 S_FUNC void save_line(void);
122 S_FUNC void forward_history(void);
123 S_FUNC void backward_history(void);
124 S_FUNC void insert_char(int key);
125 S_FUNC void goto_line(void);
126 S_FUNC void list_history(void);
127 S_FUNC void refresh_line(void);
128 S_FUNC void swap_chars(void);
129 S_FUNC void set_mark(void);
130 S_FUNC void yank(void);
131 S_FUNC void save_region(void);
132 S_FUNC void kill_region(void);
133 S_FUNC void reverse_search(void);
134 S_FUNC void quote_char(void);
135 S_FUNC void uppercase_word(void);
136 S_FUNC void lowercase_word(void);
137 S_FUNC void ignore_char(void);
138 S_FUNC void arrow_key(void);
139 S_FUNC void quit_calc(void);
140
141
142 STATIC FUNC funcs[] =
143 {
144 {"ignore-char", ignore_char},
145 {"flush-input", flush_input},
146 {"start-of-line", start_of_line},
147 {"end-of-line", end_of_line},
148 {"forward-char", forward_char},
149 {"backward-char", backward_char},
150 {"forward-word", forward_word},
151 {"backward-word", backward_word},
152 {"delete-char", delete_char},
153 {"forward-kill-char", forward_kill_char},
154 {"backward-kill-char", backward_kill_char},
155 {"forward-kill-word", forward_kill_word},
156 {"uppercase-word", uppercase_word},
157 {"lowercase-word", lowercase_word},
158 {"kill-line", kill_line},
159 {"goto-line", goto_line},
160 {"new-line", new_line},
161 {"save-line", save_line},
162 {"forward-history", forward_history},
163 {"backward-history", backward_history},
164 {"insert-char", insert_char},
165 {"list-history", list_history},
166 {"refresh-line", refresh_line},
167 {"swap-chars", swap_chars},
168 {"set-mark", set_mark},
169 {"yank", yank},
170 {"save-region", save_region},
171 {"kill-region", kill_region},
172 {"reverse-search", reverse_search},
173 {"quote-char", quote_char},
174 {"arrow-key", arrow_key},
175 {"quit", quit_calc},
176 {NULL, NULL}
177 };
178
179
180 typedef struct key_ent KEY_ENT;
181 typedef struct key_map KEY_MAP;
182
183 struct key_ent {
184 FUNCPTR func;
185 KEY_MAP *next;
186 };
187
188
189 struct key_map {
190 char *name;
191 KEY_ENT default_ent;
192 KEY_ENT *map[256];
193 };
194
195
196 STATIC char base_map_name[] = "base-map";
197 STATIC char esc_map_name[] = "esc-map";
198
199
200 STATIC KEY_MAP maps[] = {
201 {base_map_name, {NULL, NULL}, {NULL, NULL}},
202 {esc_map_name, {NULL, NULL}, {NULL, NULL}},
203 };
204
205 typedef struct history_entry {
206 int len; /* length of data */
207 char *data; /* varying length data */
208 struct history_entry* prev;
209 struct history_entry* next;
210 } HIST;
211
212 STATIC int inited;
213 STATIC int canedit;
214 STATIC int key_count;
215 STATIC int save_len;
216 STATIC KEY_MAP *cur_map;
217 STATIC KEY_MAP *base_map;
218 STATIC KEY_ENT key_table[MAX_KEYS];
219 STATIC HIST* hist_first = NULL;
220 STATIC HIST* hist_last = NULL;
221 STATIC char save_buffer[SAVE_SIZE];
222
223 /* declare other static functions */
224 S_FUNC FUNCPTR find_func(char *name);
225 S_FUNC HIST *get_event(int n);
226 S_FUNC HIST *find_event(char *pat, int len);
227 S_FUNC void read_key(void);
228 S_FUNC void erasechar(void);
229 S_FUNC void newline(void);
230 S_FUNC void backspace(void);
231 S_FUNC void beep(void);
232 S_FUNC void echo_char(int ch);
233 S_FUNC void echo_string(char *str, int len);
234 S_FUNC void savetext(char *str, int len);
235 S_FUNC void memrcpy(char *dest, char *src, int len);
236 S_FUNC int read_bindings(FILE *fp);
237 S_FUNC int in_word(int ch);
238 S_FUNC KEY_MAP *find_map(char *map);
239 S_FUNC void unbind_key(KEY_MAP *map, int key);
240 S_FUNC void raw_bind_key(KEY_MAP *map, int key,
241 FUNCPTR func, KEY_MAP *next_map);
242 S_FUNC KEY_MAP *do_map_line(char *line);
243 S_FUNC void do_default_line(KEY_MAP *map, char *line);
244 S_FUNC void do_bind_line(KEY_MAP *map, char *line);
245 S_FUNC void back_over_char(int ch);
246 S_FUNC void echo_rest_of_line(void);
247 S_FUNC void goto_start_of_line(void);
248 S_FUNC void goto_end_of_line(void);
249 S_FUNC void remove_char(int ch);
250 S_FUNC void decrement_end(int n);
251 S_FUNC void insert_string(char *str, int len);
252
253
254 /*
255 * Read a line into the specified buffer. The line ends in a newline,
256 * and is NULL terminated. Returns the number of characters read, or
257 * zero on an end of file or error. The prompt is printed before reading
258 * the line.
259 */
260 size_t
hist_getline(char * prompt,char * buf,size_t len)261 hist_getline(char *prompt, char *buf, size_t len)
262 {
263 /*
264 * initialize if we have not already done so
265 */
266 if (!inited)
267 (void) hist_init(calcbindings);
268
269 /*
270 * establish the beginning of a line condition
271 */
272 HS.prompt = prompt;
273 HS.bufsize = len - 2;
274 HS.buf = buf;
275 HS.pos = buf;
276 HS.end = buf;
277 HS.mark = NULL;
278 HS.linelen = -1;
279 HS.virgin_line = TRUE;
280
281 /*
282 * prep the I/O
283 */
284 fputs(prompt, stdout);
285 fflush(stdout);
286
287 /*
288 * special case: non-interactive editing
289 */
290 if (!canedit) {
291 if (fgets(buf, len, stdin) == NULL)
292 return 0;
293 return strlen(buf);
294 }
295
296 /*
297 * get the line
298 */
299 while (HS.linelen < 0) {
300
301 /* get the next char */
302 read_key();
303
304 /* chars typed, no longer virgin */
305 HS.virgin_line = FALSE;
306 }
307
308 /*
309 * return the line
310 */
311 return HS.linelen;
312 }
313
314
315 /*
316 * Initialize the module by reading in the key bindings from the specified
317 * filename, and then setting the terminal modes for noecho and cbreak mode.
318 * If the supplied filename is NULL, then a default filename will be used.
319 * We will search the CALCPATH for the file.
320 *
321 * Returns zero if successful, or a nonzero error code if unsuccessful.
322 * If this routine fails, hist_getline, hist_saveline, and hist_term can
323 * still be called but all fancy editing is disabled.
324 */
325 int
hist_init(char * filename)326 hist_init(char *filename)
327 {
328 /*
329 * prevent multiple initializations
330 */
331 if (inited) {
332 if (conf->calc_debug & CALCDBG_TTY)
333 printf("hist_init: inited already set\n");
334 return HIST_INITED;
335 }
336
337 /*
338 * setup
339 */
340 inited = 1;
341 canedit = 0;
342 if (conf->calc_debug & CALCDBG_TTY)
343 printf("hist_init: Set inited, cleared canedit\n");
344
345 /*
346 * open the bindings file
347 */
348 if (filename == NULL)
349 filename = HIST_BINDING_FILE;
350 if (opensearchfile(filename, calcpath, NULL, FALSE) > 0)
351 return HIST_NOFILE;
352
353 /*
354 * load the bindings
355 */
356 if (read_bindings(curstream()))
357 return HIST_NOFILE;
358
359 /*
360 * close the bindings
361 */
362 closeinput();
363
364 /*
365 * setup the calc TTY on STDIN
366 */
367 if (!calc_tty(STDIN)) {
368 return HIST_NOTTY;
369 }
370
371 canedit = 1;
372 if (conf->calc_debug & CALCDBG_TTY)
373 printf("hist_init: Set canedit\n");
374
375 return HIST_SUCCESS;
376 }
377
378
379 /*
380 * Reset the terminal modes just before exiting.
381 */
382 void
hist_term(void)383 hist_term(void)
384 {
385 if (!inited || !canedit) {
386 if (conf->calc_debug & CALCDBG_TTY) {
387 if (!inited)
388 printf("hist_term: inited already cleared\n");
389 if (!canedit)
390 printf("hist_term: canedit already cleared\n");
391 }
392 inited = 0;
393 if (conf->calc_debug & CALCDBG_TTY)
394 printf("hist_term: Cleared inited\n");
395 return;
396 }
397
398 if (hist_first) {
399 HIST* hp = hist_first;
400 HIST* next;
401 while(hp) {
402 next = hp->next;
403 free(hp->data);
404 free(hp);
405 hp = next;
406 }
407 }
408 hist_first = NULL;
409 hist_last = NULL;
410
411 /*
412 * restore original tty state
413 */
414 (void) orig_tty(STDIN);
415 }
416
417
418 S_FUNC KEY_MAP *
find_map(char * map)419 find_map(char *map)
420 {
421 unsigned int i;
422
423 for (i = 0; i < sizeof(maps) / sizeof(maps[0]); i++) {
424 if (strcmp(map, maps[i].name) == 0)
425 return &maps[i];
426 }
427 return NULL;
428 }
429
430
431 S_FUNC void
unbind_key(KEY_MAP * map,int key)432 unbind_key(KEY_MAP *map, int key)
433 {
434 map->map[key] = NULL;
435 }
436
437
438 S_FUNC void
raw_bind_key(KEY_MAP * map,int key,FUNCPTR func,KEY_MAP * next_map)439 raw_bind_key(KEY_MAP *map, int key, FUNCPTR func, KEY_MAP *next_map)
440 {
441 if (map->map[key] == NULL) {
442 if (key_count >= MAX_KEYS)
443 return;
444 map->map[key] = &key_table[key_count++];
445 }
446 map->map[key]->func = func;
447 map->map[key]->next = next_map;
448 }
449
450
451 S_FUNC KEY_MAP *
do_map_line(char * line)452 do_map_line(char *line)
453 {
454 char *cp;
455 char *map_name;
456
457 cp = line;
458 while (isspace((int)*cp))
459 cp++;
460 if (*cp == '\0')
461 return NULL;
462 map_name = cp;
463 while ((*cp != '\0') && !isspace((int)*cp))
464 cp++;
465 *cp = '\0';
466 return find_map(map_name);
467 }
468
469
470 S_FUNC void
do_bind_line(KEY_MAP * map,char * line)471 do_bind_line(KEY_MAP *map, char *line)
472 {
473 char *cp;
474 char key;
475 char *func_name;
476 char *next_name;
477 KEY_MAP *next;
478 FUNCPTR func;
479
480 if (map == NULL)
481 return;
482 cp = line;
483 key = *cp++;
484 if (*cp == '\0') {
485 unbind_key(map, key);
486 return;
487 }
488 if (key == '^') {
489 if (*cp == '?') {
490 key = 0177;
491 cp++;
492 } else {
493 key = CONTROL(*cp++);
494 }
495 } else if (key == '\\') {
496 key = *cp++;
497 }
498
499 while (isspace((int)*cp))
500 cp++;
501 if (*cp == '\0') {
502 unbind_key(map, key);
503 return;
504 }
505
506 func_name = cp;
507 while ((*cp != '\0') && !isspace((int)*cp))
508 cp++;
509 if (*cp) {
510 *cp++ = '\0';
511 while (isspace((int)*cp))
512 cp++;
513 }
514 func = find_func(func_name);
515 if (func == NULL) {
516 fprintf(stderr, "Unknown function \"%s\"\n", func_name);
517 return;
518 }
519
520 if (*cp == '\0') {
521 next = map->default_ent.next;
522 if (next == NULL)
523 next = base_map;
524 } else {
525 next_name = cp;
526 while ((*cp != '\0') && !isspace((int)*cp))
527 cp++;
528 if (*cp) {
529 *cp++ = '\0';
530 while (isspace((int)*cp))
531 cp++;
532 }
533 next = find_map(next_name);
534 if (next == NULL)
535 return;
536 }
537 raw_bind_key(map, key, func, next);
538 }
539
540
541 S_FUNC void
do_default_line(KEY_MAP * map,char * line)542 do_default_line(KEY_MAP *map, char *line)
543 {
544 char *cp;
545 char *func_name;
546 char *next_name;
547 KEY_MAP *next;
548 FUNCPTR func;
549
550 if (map == NULL)
551 return;
552 cp = line;
553 while (isspace((int)*cp))
554 cp++;
555 if (*cp == '\0')
556 return;
557
558 func_name = cp;
559 while ((*cp != '\0') && !isspace((int)*cp))
560 cp++;
561 if (*cp != '\0') {
562 *cp++ = '\0';
563 while (isspace((int)*cp))
564 cp++;
565 }
566 func = find_func(func_name);
567 if (func == NULL)
568 return;
569
570 if (*cp == '\0') {
571 next = map;
572 } else {
573 next_name = cp;
574 while ((*cp != '\0') && !isspace((int)*cp))
575 cp++;
576 if (*cp != '\0') {
577 *cp++ = '\0';
578 while (isspace((int)*cp))
579 cp++;
580 }
581 next = find_map(next_name);
582 if (next == NULL)
583 return;
584 }
585
586 map->default_ent.func = func;
587 map->default_ent.next = next;
588 }
589
590
591 /*
592 * Read bindings from specified open file.
593 *
594 * Returns nonzero on error.
595 */
596 S_FUNC int
read_bindings(FILE * fp)597 read_bindings(FILE *fp)
598 {
599 char *cp;
600 KEY_MAP *input_map;
601 char line[BUFSIZ+1];
602
603 base_map = find_map(base_map_name);
604 cur_map = base_map;
605 input_map = base_map;
606
607 if (fp == NULL)
608 return 1;
609
610 while (fgets(line, sizeof(line) - 1, fp)) {
611 line[BUFSIZ] = '\0';
612 cp = line;
613 while (isspace((int)*cp))
614 cp++;
615
616 if ((*cp == '\0') || (*cp == '#') || (*cp == '\n'))
617 continue;
618
619 if (cp[strlen(cp) - 1] == '\n')
620 cp[strlen(cp) - 1] = '\0';
621
622 if (memcmp(cp, "map", 3) == 0)
623 input_map = do_map_line(&cp[3]);
624 else if (memcmp(cp, "default", 7) == 0)
625 do_default_line(input_map, &cp[7]);
626 else
627 do_bind_line(input_map, cp);
628 }
629 return 0;
630 }
631
632
633 S_FUNC void
read_key(void)634 read_key(void)
635 {
636 KEY_ENT *ent;
637 int key;
638
639 fflush(stdout);
640 key = fgetc(stdin);
641 if (key == EOF) {
642 HS.linelen = 0;
643 HS.buf[0] = '\0';
644 return;
645 }
646
647 ent = cur_map->map[key];
648 if (ent == NULL)
649 ent = &cur_map->default_ent;
650 if (ent->next)
651 cur_map = ent->next;
652 if (ent->func)
653 /* ignore Saber-C warning #65 - has 1 arg, expecting 0 */
654 /* OK to ignore in proc read_key */
655 (*ent->func)(key);
656 else
657 insert_char(key);
658 }
659
660
661 /*
662 * Return the Nth history event, indexed from zero.
663 * Earlier history events are lower in number.
664 */
665 S_FUNC HIST *
get_event(int n)666 get_event(int n)
667 {
668 register HIST * hp = hist_first;
669 int i = 0;
670
671 do {
672 if(!hp)
673 break;
674 if(i == n)
675 return hp;
676 ++i;
677 hp = hp->next;
678 } while(hp);
679 return NULL;
680 }
681
682
683 /*
684 * Search the history list for the specified pattern.
685 * Returns the found history, or NULL.
686 */
687 S_FUNC HIST *
find_event(char * pat,int len)688 find_event(char *pat, int len)
689 {
690 register HIST * hp = hist_first;
691
692 for(hp = hist_first; hp != NULL; hp = hp->next) {
693 if ((hp->len == len) && (memcmp(hp->data, pat, len) == 0))
694 return hp;
695 }
696 return NULL;
697 }
698
699
700 /*
701 * Insert a line into the end of the history table.
702 * If the line already appears in the table, then it is moved to the end.
703 * If the table is full, then the earliest commands are deleted as necessary.
704 * Warning: the incoming line cannot point into the history table.
705 */
706 void
hist_saveline(char * line,int len)707 hist_saveline(char *line, int len)
708 {
709 HIST * hp;
710
711 if ((len > 0) && (line[len - 1] == '\n'))
712 len--;
713 if (len <= 0)
714 return;
715
716 /*
717 * See if the line is already present in the history table.
718 * If so, and it is already at the end, then we are all done.
719 * Otherwise delete it since we will reinsert it at the end.
720 */
721 hp = find_event(line, len);
722 if (hp) {
723 if (hp == hist_last)
724 return;
725 if (hp->prev)
726 hp->prev->next = hp->next;
727 hp->next->prev = hp->prev;
728
729 hist_last->next = hp;
730 hp->next = NULL;
731 hp->prev = hist_last;
732 hist_last = hp;
733 return;
734 }
735
736 /*
737 * If there is not enough room left in the history buffer to add
738 * the new command, then repeatedly delete the earliest command
739 * as many times as necessary in order to make enough room.
740 */
741 if (HS.histcount >= HIST_SIZE) {
742 HIST *new_first = hist_first->next;
743 free(hist_first->data);
744 free(hist_first);
745 new_first->prev = NULL;
746 hist_first = new_first;
747 HS.histcount--;
748 }
749
750 /*
751 * Add the line to the end of the history table.
752 */
753 hp = malloc(sizeof(HIST));
754 if (hp == NULL) {
755 fprintf(stderr,
756 "Out of memory adding line to the history table #0\n");
757 return;
758 }
759 hp->next = NULL;
760 hp->prev = NULL;
761 hp->len = len;
762 hp->data = malloc(len);
763 if (hp->data == NULL) {
764 fprintf(stderr,
765 "Out of memory adding line to the history table #1\n");
766 return;
767 }
768 memcpy(hp->data, line, len);
769 HS.curhist = ++HS.histcount;
770 if (!hist_first)
771 hist_first = hp;
772 if (!hist_last)
773 hist_last = hp;
774 else {
775 hist_last->next = hp;
776 hp->prev = hist_last;
777 hist_last = hp;
778 }
779 }
780
781
782 /*
783 * Find the function for a specified name.
784 */
785 S_FUNC FUNCPTR
find_func(char * name)786 find_func(char *name)
787 {
788 FUNC *fp;
789
790 for (fp = funcs; fp->name; fp++) {
791 if (strcmp(fp->name, name) == 0)
792 return fp->func;
793 }
794 return NULL;
795 }
796
797
798 S_FUNC void
arrow_key(void)799 arrow_key(void)
800 {
801 switch (fgetc(stdin)) {
802 case 'A':
803 backward_history();
804 break;
805 case 'B':
806 forward_history();
807 break;
808 case 'C':
809 forward_char();
810 break;
811 case 'D':
812 backward_char();
813 break;
814 }
815 }
816
817
818 S_FUNC void
back_over_char(int ch)819 back_over_char(int ch)
820 {
821 backspace();
822 if (!isprint(ch))
823 backspace();
824 }
825
826
827 S_FUNC void
remove_char(int ch)828 remove_char(int ch)
829 {
830 erasechar();
831 if (!isprint(ch))
832 erasechar();
833 }
834
835
836 S_FUNC void
echo_rest_of_line(void)837 echo_rest_of_line(void)
838 {
839 echo_string(HS.pos, HS.end - HS.pos);
840 }
841
842
843 S_FUNC void
goto_start_of_line(void)844 goto_start_of_line(void)
845 {
846 while (HS.pos > HS.buf)
847 back_over_char((int)(*--HS.pos));
848 }
849
850
851 S_FUNC void
goto_end_of_line(void)852 goto_end_of_line(void)
853 {
854 echo_rest_of_line();
855 HS.pos = HS.end;
856 }
857
858
859 S_FUNC void
decrement_end(int n)860 decrement_end(int n)
861 {
862 HS.end -= n;
863 if (HS.mark && (HS.mark > HS.end))
864 HS.mark = NULL;
865 }
866
867
868 S_FUNC void
ignore_char(void)869 ignore_char(void)
870 {
871 }
872
873
874 S_FUNC void
flush_input(void)875 flush_input(void)
876 {
877 echo_rest_of_line();
878 while (HS.end > HS.buf)
879 remove_char((int)(*--HS.end));
880 HS.pos = HS.buf;
881 HS.mark = NULL;
882 }
883
884
885 S_FUNC void
start_of_line(void)886 start_of_line(void)
887 {
888 goto_start_of_line();
889 }
890
891
892 S_FUNC void
end_of_line(void)893 end_of_line(void)
894 {
895 goto_end_of_line();
896 }
897
898
899 S_FUNC void
forward_char(void)900 forward_char(void)
901 {
902 if (HS.pos < HS.end)
903 echo_char(*HS.pos++);
904 }
905
906
907 S_FUNC void
backward_char(void)908 backward_char(void)
909 {
910 if (HS.pos > HS.buf)
911 back_over_char((int)(*--HS.pos));
912 }
913
914
915 S_FUNC void
uppercase_word(void)916 uppercase_word(void)
917 {
918 while ((HS.pos < HS.end) && !in_word((int)(*HS.pos)))
919 echo_char(*HS.pos++);
920 while ((HS.pos < HS.end) && in_word((int)(*HS.pos))) {
921 if ((*HS.pos >= 'a') && (*HS.pos <= 'z'))
922 *HS.pos += 'A' - 'a';
923 echo_char(*HS.pos++);
924 }
925 }
926
927
928 S_FUNC void
lowercase_word(void)929 lowercase_word(void)
930 {
931 while ((HS.pos < HS.end) && !in_word((int)(*HS.pos)))
932 echo_char(*HS.pos++);
933 while ((HS.pos < HS.end) && in_word((int)(*HS.pos))) {
934 if ((*HS.pos >= 'A') && (*HS.pos <= 'Z'))
935 *HS.pos += 'a' - 'A';
936 echo_char(*HS.pos++);
937 }
938 }
939
940
941 S_FUNC void
forward_word(void)942 forward_word(void)
943 {
944 while ((HS.pos < HS.end) && !in_word((int)(*HS.pos)))
945 echo_char(*HS.pos++);
946 while ((HS.pos < HS.end) && in_word((int)(*HS.pos)))
947 echo_char(*HS.pos++);
948 }
949
950
951 S_FUNC void
backward_word(void)952 backward_word(void)
953 {
954 if ((HS.pos > HS.buf) && in_word((int)(*HS.pos)))
955 back_over_char((int)(*--HS.pos));
956 while ((HS.pos > HS.buf) && !in_word((int)(*HS.pos)))
957 back_over_char((int)(*--HS.pos));
958 while ((HS.pos > HS.buf) && in_word((int)(*HS.pos)))
959 back_over_char((int)(*--HS.pos));
960 if ((HS.pos < HS.end) && !in_word((int)(*HS.pos)))
961 echo_char(*HS.pos++);
962 }
963
964
965 S_FUNC void
forward_kill_char(void)966 forward_kill_char(void)
967 {
968 int rest;
969 char ch;
970
971 rest = HS.end - HS.pos;
972 if (rest-- <= 0)
973 return;
974 ch = *HS.pos;
975 if (rest > 0) {
976 memcpy(HS.pos, HS.pos + 1, rest);
977 *(HS.end - 1) = ch;
978 }
979 echo_rest_of_line();
980 remove_char((int)ch);
981 decrement_end(1);
982 while (rest > 0)
983 back_over_char((int)(HS.pos[--rest]));
984 }
985
986
987 S_FUNC void
delete_char(void)988 delete_char(void)
989 {
990 /*
991 * quit delete_char (usually ^D) is at start of line and we are allowed
992 *
993 * We exit of start of line and config("ctrl_d", "empty") or
994 * if config("ctrl_d", "virgin") and we have never typed on the line.
995 */
996 if ((HS.end == HS.buf) &&
997 (conf->ctrl_d == CTRL_D_EMPTY_EOF ||
998 (conf->ctrl_d == CTRL_D_VIRGIN_EOF && HS.virgin_line == TRUE))) {
999 quit_calc();
1000 }
1001
1002 /*
1003 * normal case: just forward_kill_char
1004 */
1005 if (HS.end > HS.buf)
1006 forward_kill_char();
1007 }
1008
1009
1010 S_FUNC void
backward_kill_char(void)1011 backward_kill_char(void)
1012 {
1013 if (HS.pos > HS.buf) {
1014 HS.pos--;
1015 back_over_char((int)(*HS.pos));
1016 forward_kill_char();
1017 }
1018 }
1019
1020
1021 S_FUNC void
forward_kill_word(void)1022 forward_kill_word(void)
1023 {
1024 char *cp;
1025
1026 if (HS.pos >= HS.end)
1027 return;
1028 echo_rest_of_line();
1029 for (cp = HS.end; cp > HS.pos;)
1030 remove_char((int)(*--cp));
1031 cp = HS.pos;
1032 while ((cp < HS.end) && !in_word((int)(*cp)))
1033 cp++;
1034 while ((cp < HS.end) && in_word((int)(*cp)))
1035 cp++;
1036 savetext(HS.pos, cp - HS.pos);
1037 memcpy(HS.pos, cp, HS.end - cp);
1038 decrement_end(cp - HS.pos);
1039 echo_rest_of_line();
1040 for (cp = HS.end; cp > HS.pos;)
1041 back_over_char((int)(*--cp));
1042 }
1043
1044
1045 S_FUNC void
kill_line(void)1046 kill_line(void)
1047 {
1048 if (HS.end <= HS.pos)
1049 return;
1050 savetext(HS.pos, HS.end - HS.pos);
1051 echo_rest_of_line();
1052 while (HS.end > HS.pos)
1053 remove_char((int)(*--HS.end));
1054 decrement_end(0);
1055 }
1056
1057
1058 /*
1059 * This is the function which completes a command line editing session.
1060 * The final line length is returned in the HS.linelen variable.
1061 * The line is NOT put into the edit history, so that the caller can
1062 * decide whether or not this should be done.
1063 */
1064 S_FUNC void
new_line(void)1065 new_line(void)
1066 {
1067 int len;
1068
1069 newline();
1070 fflush(stdout);
1071
1072 HS.mark = NULL;
1073 HS.end[0] = '\n';
1074 HS.end[1] = '\0';
1075 len = HS.end - HS.buf + 1;
1076 if (len <= 1) {
1077 HS.curhist = HS.histcount;
1078 HS.linelen = 1;
1079 return;
1080 }
1081 HS.curhist = HS.histcount;
1082 HS.pos = HS.buf;
1083 HS.end = HS.buf;
1084 HS.linelen = len;
1085 }
1086
1087
1088 S_FUNC void
save_line(void)1089 save_line(void)
1090 {
1091 int len;
1092
1093 len = HS.end - HS.buf;
1094 if (len > 0) {
1095 hist_saveline(HS.buf, len);
1096 flush_input();
1097 }
1098 HS.curhist = HS.histcount;
1099 }
1100
1101
1102 S_FUNC void
goto_line(void)1103 goto_line(void)
1104 {
1105 int num;
1106 char *cp;
1107 HIST *hp;
1108
1109 num = 0;
1110 cp = HS.buf;
1111 while ((*cp >= '0') && (*cp <= '9') && (cp < HS.pos))
1112 num = num * 10 + (*cp++ - '0');
1113 if ((num <= 0) || (num > HS.histcount) || (cp != HS.pos)) {
1114 beep();
1115 return;
1116 }
1117 flush_input();
1118 HS.curhist = HS.histcount - num;
1119 hp = get_event(HS.curhist);
1120 memcpy(HS.buf, hp->data, hp->len);
1121 HS.end = &HS.buf[hp->len];
1122 goto_end_of_line();
1123 }
1124
1125
1126 S_FUNC void
forward_history(void)1127 forward_history(void)
1128 {
1129 HIST *hp;
1130
1131 flush_input();
1132 if (++HS.curhist >= HS.histcount)
1133 HS.curhist = 0;
1134 hp = get_event(HS.curhist);
1135 if (hp) {
1136 memcpy(HS.buf, hp->data, hp->len);
1137 HS.end = &HS.buf[hp->len];
1138 }
1139 goto_end_of_line();
1140 }
1141
1142
1143 S_FUNC void
backward_history(void)1144 backward_history(void)
1145 {
1146 HIST *hp;
1147
1148 flush_input();
1149 if (--HS.curhist < 0)
1150 HS.curhist = HS.histcount - 1;
1151 hp = get_event(HS.curhist);
1152 if (hp) {
1153 memcpy(HS.buf, hp->data, hp->len);
1154 HS.end = &HS.buf[hp->len];
1155 }
1156 goto_end_of_line();
1157 }
1158
1159
1160 S_FUNC void
insert_char(int key)1161 insert_char(int key)
1162 {
1163 int len;
1164 int rest;
1165
1166 len = HS.end - HS.buf;
1167 if (len >= HS.bufsize) {
1168 beep();
1169 return;
1170 }
1171 rest = HS.end - HS.pos;
1172 if (rest > 0)
1173 memrcpy(HS.pos + 1, HS.pos, rest);
1174 HS.end++;
1175 *HS.pos++ = key;
1176 echo_char(key);
1177 echo_rest_of_line();
1178 while (rest > 0)
1179 back_over_char((int)(HS.pos[--rest]));
1180 }
1181
1182
1183 S_FUNC void
insert_string(char * str,int len)1184 insert_string(char *str, int len)
1185 {
1186 int rest;
1187 int totallen;
1188
1189 if (len <= 0)
1190 return;
1191 totallen = (HS.end - HS.buf) + len;
1192 if (totallen > HS.bufsize) {
1193 beep();
1194 return;
1195 }
1196 rest = HS.end - HS.pos;
1197 if (rest > 0)
1198 memrcpy(HS.pos + len, HS.pos, rest);
1199 HS.end += len;
1200 memcpy(HS.pos, str, len);
1201 HS.pos += len;
1202 echo_string(str, len);
1203 echo_rest_of_line();
1204 while (rest > 0)
1205 back_over_char((int)(HS.pos[--rest]));
1206 }
1207
1208
1209 S_FUNC void
list_history(void)1210 list_history(void)
1211 {
1212 HIST *hp;
1213 int hnum;
1214
1215 for (hnum = 0; hnum < HS.histcount; hnum++) {
1216 hp = get_event(hnum);
1217 printf("\n%3d: ", HS.histcount - hnum);
1218 echo_string(hp->data, hp->len);
1219 }
1220 refresh_line();
1221 }
1222
1223
1224 S_FUNC void
refresh_line(void)1225 refresh_line(void)
1226 {
1227 char *cp;
1228
1229 newline();
1230 fputs(HS.prompt, stdout);
1231 if (HS.end > HS.buf) {
1232 echo_string(HS.buf, HS.end - HS.buf);
1233 cp = HS.end;
1234 while (cp > HS.pos)
1235 back_over_char((int)(*--cp));
1236 }
1237 }
1238
1239
1240 S_FUNC void
swap_chars(void)1241 swap_chars(void)
1242 {
1243 char ch1;
1244 char ch2;
1245
1246 if ((HS.pos <= HS.buf) || (HS.pos >= HS.end))
1247 return;
1248 ch1 = *HS.pos--;
1249 ch2 = *HS.pos;
1250 *HS.pos++ = ch1;
1251 *HS.pos = ch2;
1252 back_over_char((int)ch2);
1253 echo_char(ch1);
1254 echo_char(ch2);
1255 back_over_char((int)ch2);
1256 }
1257
1258
1259 S_FUNC void
set_mark(void)1260 set_mark(void)
1261 {
1262 HS.mark = HS.pos;
1263 }
1264
1265
1266 S_FUNC void
save_region(void)1267 save_region(void)
1268 {
1269 int len;
1270
1271 if (HS.mark == NULL)
1272 return;
1273 len = HS.mark - HS.pos;
1274 if (len > 0)
1275 savetext(HS.pos, len);
1276 if (len < 0)
1277 savetext(HS.mark, -len);
1278 }
1279
1280
1281 S_FUNC void
kill_region(void)1282 kill_region(void)
1283 {
1284 char *cp;
1285 char *left;
1286 char *right;
1287
1288 if ((HS.mark == NULL) || (HS.mark == HS.pos))
1289 return;
1290
1291 echo_rest_of_line();
1292 if (HS.mark < HS.pos) {
1293 left = HS.mark;
1294 right = HS.pos;
1295 HS.pos = HS.mark;
1296 } else {
1297 left = HS.pos;
1298 right = HS.mark;
1299 HS.mark = HS.pos;
1300 }
1301 savetext(left, right - left);
1302 for (cp = HS.end; cp > left;)
1303 remove_char((int)(*--cp));
1304 if (right < HS.end)
1305 memcpy(left, right, HS.end - right);
1306 decrement_end(right - left);
1307 echo_rest_of_line();
1308 for (cp = HS.end; cp > HS.pos;)
1309 back_over_char((int)(*--cp));
1310 }
1311
1312
1313 S_FUNC void
yank(void)1314 yank(void)
1315 {
1316 insert_string(save_buffer, save_len);
1317 }
1318
1319
1320 S_FUNC void
reverse_search(void)1321 reverse_search(void)
1322 {
1323 int len;
1324 int count;
1325 int testhist;
1326 HIST *hp;
1327 char *save_pos;
1328
1329 count = HS.histcount;
1330 len = HS.pos - HS.buf;
1331 if (len <= 0)
1332 count = 0;
1333 testhist = HS.curhist;
1334 do {
1335 if (--count < 0) {
1336 beep();
1337 return;
1338 }
1339 if (--testhist < 0)
1340 testhist = HS.histcount - 1;
1341 hp = get_event(testhist);
1342 } while ((hp == NULL) || (hp->len < len) ||
1343 memcmp(hp->data, HS.buf, len));
1344
1345 HS.curhist = testhist;
1346 save_pos = HS.pos;
1347 flush_input();
1348 memcpy(HS.buf, hp->data, hp->len);
1349 HS.end = &HS.buf[hp->len];
1350 goto_end_of_line();
1351 while (HS.pos > save_pos)
1352 back_over_char((int)(*--HS.pos));
1353 }
1354
1355
1356 S_FUNC void
quote_char(void)1357 quote_char(void)
1358 {
1359 int ch;
1360
1361 ch = fgetc(stdin);
1362 if (ch != EOF)
1363 insert_char(ch);
1364 }
1365
1366
1367 /*
1368 * Save data in the save buffer.
1369 */
1370 S_FUNC void
savetext(char * str,int len)1371 savetext(char *str, int len)
1372 {
1373 save_len = 0;
1374 if (len <= 0)
1375 return;
1376 if (len > SAVE_SIZE)
1377 len = SAVE_SIZE;
1378 memcpy(save_buffer, str, len);
1379 save_len = len;
1380 }
1381
1382
1383 /*
1384 * Test whether a character is part of a word.
1385 */
1386 S_FUNC int
in_word(int ch)1387 in_word(int ch)
1388 {
1389 return (isalnum(ch) || (ch == '_'));
1390 }
1391
1392
1393 S_FUNC void
erasechar(void)1394 erasechar(void)
1395 {
1396 fputs("\b \b", stdout);
1397 }
1398
1399
1400 S_FUNC void
newline(void)1401 newline(void)
1402 {
1403 fputc('\n', stdout);
1404 }
1405
1406
1407 S_FUNC void
backspace(void)1408 backspace(void)
1409 {
1410 fputc('\b', stdout);
1411 }
1412
1413
1414 S_FUNC void
beep(void)1415 beep(void)
1416 {
1417 fputc('\007', stdout);
1418 }
1419
1420
1421 S_FUNC void
echo_char(int ch)1422 echo_char(int ch)
1423 {
1424 if (isprint(ch)) {
1425 putchar(ch);
1426 } else {
1427 putchar('^');
1428 putchar((ch + '@') & 0x7f);
1429 }
1430 }
1431
1432
1433 S_FUNC void
echo_string(char * str,int len)1434 echo_string(char *str, int len)
1435 {
1436 while (len-- > 0)
1437 echo_char(*str++);
1438 }
1439
1440
1441 S_FUNC void
memrcpy(char * dest,char * src,int len)1442 memrcpy(char *dest, char *src, int len)
1443 {
1444 dest += len - 1;
1445 src += len - 1;
1446 while (len-- > 0)
1447 *dest-- = *src--;
1448 }
1449
1450 #endif /* !USE_READLINE */
1451
1452 S_FUNC void
quit_calc(void)1453 quit_calc(void)
1454 {
1455 hist_term();
1456 putchar('\n');
1457 libcalc_call_me_last();
1458 exit(0);
1459 }
1460
1461 #if defined(USE_READLINE)
1462
1463
1464 #define HISTORY_LEN (1024) /* number of entries to save */
1465
1466
1467 #include <readline/readline.h>
1468 #include <readline/history.h>
1469
1470
1471 /*
1472 * The readline/history libs do most of the dirty work for us, so we can
1473 * replace hist_init() and hist_term() with dummies when using readline.
1474 * For hist_getline() we have to add a newline that readline removed but
1475 * calc expects. For hist_saveline(), we have to undo this. hist_getline()
1476 * also has to cope with the different memory management schemes of calc and
1477 * readline.
1478 */
1479
1480
1481 size_t
hist_getline(char * prompt,char * buf,size_t len)1482 hist_getline(char *prompt, char *buf, size_t len)
1483 {
1484 char *line;
1485
1486 buf[0] = '\0';
1487 line = readline(prompt);
1488 if (!line) {
1489 switch (conf->ctrl_d) {
1490 case CTRL_D_NEVER_EOF:
1491 return 0;
1492 case CTRL_D_VIRGIN_EOF:
1493 case CTRL_D_EMPTY_EOF:
1494 default:
1495 quit_calc();
1496 /*NOTREACHED*/
1497 }
1498 }
1499 strlcpy(buf, line, len);
1500 buf[len - 2] = '\0';
1501 len = strlen(buf);
1502 buf[len] = '\n';
1503 buf[len + 1] = '\0';
1504 free(line);
1505 return len + 1;
1506 }
1507
1508
1509 void
hist_term(void)1510 hist_term(void)
1511 {
1512 }
1513
1514
1515 S_FUNC void
my_stifle_history(void)1516 my_stifle_history (void)
1517 {
1518 /* only save last number of entries */
1519 stifle_history(HISTORY_LEN);
1520
1521 if (calc_history)
1522 write_history(calc_history);
1523 }
1524
1525
1526 int
hist_init(char * UNUSED (filename))1527 hist_init(char *UNUSED(filename))
1528 {
1529 /* used when parsing conditionals in ~/.inputrc */
1530 rl_readline_name = "calc";
1531
1532 /* initialize interactive variables */
1533 using_history();
1534
1535 /* name of history file */
1536 if (calc_history == NULL) {
1537 calc_history = tilde_expand("~/.calc_history");
1538 }
1539
1540 /* read previous history */
1541 read_history(calc_history);
1542
1543 atexit(my_stifle_history);
1544
1545 return HIST_SUCCESS;
1546 }
1547
1548 void
hist_saveline(char * line,int len)1549 hist_saveline(char *line, int len)
1550 {
1551 STATIC char *prev = NULL;
1552
1553 if (len <= 1)
1554 return;
1555
1556 /* ignore if identical with previous line */
1557 if (prev != NULL && strcmp(prev, line) == 0)
1558 return;
1559
1560 free(prev);
1561
1562 /* fail silently */
1563 prev = strdup(line);
1564
1565 line[len - 1] = '\0';
1566 add_history(line);
1567 line[len - 1] = '\n';
1568 }
1569
1570
1571 #endif /* USE_READLINE */
1572
1573
1574 #if defined(HIST_TEST)
1575
1576 /*
1577 * Main routine to test history.
1578 */
1579 int
main(int argc,char ** argv)1580 main(int argc, char **argv)
1581 {
1582 char *filename;
1583 int len;
1584 char buf[BUFSIZ+1];
1585
1586 filename = NULL;
1587 if (argc > 1)
1588 filename = argv[1];
1589
1590 switch (hist_init(filename)) {
1591 case HIST_SUCCESS:
1592 break;
1593 case HIST_NOFILE:
1594 fprintf(stderr, "Binding file was not found\n");
1595 break;
1596 case HIST_NOTTY:
1597 fprintf(stderr, "Cannot set terminal parameters\n");
1598 break;
1599 case HIST_INITED:
1600 fprintf(stderr, "Hist is already inited\n");
1601 break;
1602 default:
1603 fprintf(stderr, "Unknown error from hist_init\n");
1604 break;
1605 }
1606
1607 do {
1608 len = hist_getline("HIST> ", buf, sizeof(buf));
1609 hist_saveline(buf, len);
1610 } while (len && (buf[0] != 'q'));
1611
1612 hist_term();
1613 exit(0);
1614 }
1615
1616 #endif /* HIST_TEST */
1617