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