xref: /netbsd-src/lib/libedit/readline.c (revision 481fca6e59249d8ffcf24fef7cfbe7b131bfb080)
1 /*	$NetBSD: readline.c,v 1.10 2000/03/10 13:06:43 jdolecek Exp $	*/
2 
3 /*-
4  * Copyright (c) 1997 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jaromir Dolecek.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #if !defined(lint) && !defined(SCCSID)
41 __RCSID("$NetBSD: readline.c,v 1.10 2000/03/10 13:06:43 jdolecek Exp $");
42 #endif /* not lint && not SCCSID */
43 
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <stdio.h>
47 #include <dirent.h>
48 #include <string.h>
49 #include <pwd.h>
50 #include <ctype.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <limits.h>
54 #include "histedit.h"
55 #include "readline.h"
56 #include "sys.h"
57 #include "el.h"
58 
59 /* for rl_complete() */
60 #define TAB		'\r'
61 
62 /* see comment at the #ifdef for sense of this */
63 #define GDB_411_HACK
64 
65 /* readline compatibility stuff - look at readline sources/documentation */
66 /* to see what these variables mean */
67 const char     *rl_library_version = "EditLine wrapper";
68 char           *rl_readline_name = "";
69 FILE           *rl_instream = NULL;
70 FILE           *rl_outstream = NULL;
71 int             rl_point = 0;
72 int             rl_end = 0;
73 char           *rl_line_buffer = NULL;
74 
75 int             history_base = 1;	/* probably never subject to change */
76 int             history_length = 0;
77 int             max_input_history = 0;
78 char            history_expansion_char = '!';
79 char            history_subst_char = '^';
80 char           *history_no_expand_chars = " \t\n=(";
81 Function       *history_inhibit_expansion_function = NULL;
82 
83 int             rl_inhibit_completion = 0;
84 int             rl_attempted_completion_over = 0;
85 char           *rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(";
86 char           *rl_completer_word_break_characters = NULL;
87 char           *rl_completer_quote_characters = NULL;
88 CPFunction     *rl_completion_entry_function = NULL;
89 CPPFunction    *rl_attempted_completion_function = NULL;
90 
91 /* used for readline emulation */
92 static History *h = NULL;
93 static EditLine *e = NULL;
94 
95 /* internal functions */
96 static unsigned char _el_rl_complete __P((EditLine *, int));
97 static char *_get_prompt __P((EditLine *));
98 static HIST_ENTRY *_move_history __P((int));
99 static int _history_search_gen __P((const char *, int, int));
100 static int _history_expand_command __P((const char *, size_t, char **));
101 static char *_rl_compat_sub __P((const char *, const char *,
102      const char *, int));
103 static int rl_complete_internal __P((int));
104 
105 /*
106  * needed for prompt switching in readline()
107  */
108 static char    *el_rl_prompt = NULL;
109 
110 /* ARGSUSED */
111 static char *
112 _get_prompt(el)
113 	EditLine *el;
114 {
115 	return el_rl_prompt;
116 }
117 
118 /*
119  * generic function for moving around history
120  */
121 static HIST_ENTRY *
122 _move_history(op)
123 	int op;
124 {
125 	HistEvent ev;
126 	static HIST_ENTRY rl_he;
127 
128 	if (history(h, &ev, op) != 0)
129 		return (HIST_ENTRY *) NULL;
130 
131 	rl_he.line = ev.str;
132 	rl_he.data = "";
133 
134 	return &rl_he;
135 }
136 
137 
138 /*
139  * READLINE compatibility stuff
140  */
141 
142 /*
143  * initialize rl compat stuff
144  */
145 int
146 rl_initialize()
147 {
148 	HistEvent ev;
149 	const LineInfo *li;
150 
151 	if (e != NULL)
152 		el_end(e);
153 	if (h != NULL)
154 		history_end(h);
155 
156 	if (!rl_instream)
157 		rl_instream = stdin;
158 	if (!rl_outstream)
159 		rl_outstream = stdout;
160 	e = el_init(rl_readline_name, rl_instream, rl_outstream, stderr);
161 
162 	h = history_init();
163 	if (!e || !h)
164 		return -1;
165 
166 	history(h, &ev, H_SETSIZE, INT_MAX);	/* unlimited */
167 	history_length = 0;
168 	max_input_history = INT_MAX;
169 	el_set(e, EL_HIST, history, h);
170 
171 	/* for proper prompt printing in readline() */
172 	el_rl_prompt = strdup("");
173 	el_set(e, EL_PROMPT, _get_prompt);
174 	el_set(e, EL_SIGNAL, 1);
175 
176 	/* set default mode to "emacs"-style and read setting afterwards */
177 	/* so this can be overriden */
178 	el_set(e, EL_EDITOR, "emacs");
179 
180 	/* for word completition - this has to go AFTER rebinding keys */
181 	/* to emacs-style */
182 	el_set(e, EL_ADDFN, "rl_complete",
183 	       "ReadLine compatible completition function",
184 	       _el_rl_complete);
185 	el_set(e, EL_BIND, "^I", "rl_complete", NULL);
186 
187 	/* read settings from configuration file */
188 	el_source(e, NULL);
189 
190 	/* some readline apps do use this */
191 	li = el_line(e);
192 	/* LINTED const cast */
193 	rl_line_buffer = (char *) li->buffer;
194 	rl_point = rl_end = 0;
195 
196 	return 0;
197 }
198 
199 /*
200  * read one line from input stream and return it, chomping
201  * trailing newline (if there is any)
202  */
203 char *
204 readline(const char *prompt)
205 {
206 	HistEvent ev;
207 	int count;
208 	const char *ret;
209 
210 	if (e == NULL || h == NULL)
211 		rl_initialize();
212 
213 	/* update prompt accordingly to what has been passed */
214 	if (!prompt) prompt = "";
215 	if (strcmp(el_rl_prompt, prompt) != 0) {
216 		free(el_rl_prompt);
217 		el_rl_prompt = strdup(prompt);
218 	}
219 
220 	/* get one line from input stream */
221 	ret = el_gets(e, &count);
222 
223 	if (ret && count > 0) {
224 		char *foo;
225 		int lastidx;
226 
227 		foo = strdup(ret);
228 		lastidx = count - 1;
229 		if (foo[lastidx] == '\n')
230 			foo[lastidx] = '\0';
231 
232 		ret = foo;
233 	} else
234 		ret = NULL;
235 
236 	history(h, &ev, H_GETSIZE);
237 	history_length = ev.num;
238 
239 	/* LINTED const cast */
240 	return (char *) ret;
241 }
242 
243 /*
244  * history functions
245  */
246 
247 /*
248  * is normally called before application starts to use
249  * history expansion functions
250  */
251 void
252 using_history()
253 {
254 	if (h == NULL || e == NULL)
255 		rl_initialize();
256 }
257 
258 /*
259  * substitute ``what'' with ``with'', returning resulting string; if
260  * globally == 1, substitutes all occurences of what, otherwise only the
261  * first one
262  */
263 static char    *
264 _rl_compat_sub(str, what, with, globally)
265 	const char     *str, *what, *with;
266 	int             globally;
267 {
268 	char           *result;
269 	const char     *temp, *new;
270 	int             len, with_len, what_len, add;
271 	size_t		size, i;
272 
273 	result = malloc((size = 16));
274 	temp = str;
275 	with_len = strlen(with);
276 	what_len = strlen(what);
277 	len = 0;
278 	do {
279 		new = strstr(temp, what);
280 		if (new) {
281 			i = new - temp;
282 			add = i + with_len;
283 			if (i + add + 1 >= size) {
284 				size += add + 1;
285 				result = realloc(result, size);
286 			}
287 			(void)strncpy(&result[len], temp, i);
288 			len += i;
289 			(void)strcpy(&result[len], with);	/* safe */
290 			len += with_len;
291 			temp = new + what_len;
292 		} else {
293 			add = strlen(temp);
294 			if (len + add + 1 >= size) {
295 				size += add + 1;
296 				result = realloc(result, size);
297 			}
298 			(void)strcpy(&result[len], temp);	/* safe */
299 			len += add;
300 			temp = NULL;
301 		}
302 	} while (temp && globally);
303 	result[len] = '\0';
304 
305 	return result;
306 }
307 
308 /*
309  * the real function doing history expansion - takes as argument command
310  * to do and data upon which the command should be executed
311  * does expansion the way I've understood readline documentation
312  * word designator ``%'' isn't supported (yet ?)
313  *
314  * returns 0 if data was not modified, 1 if it was and 2 if the string
315  * should be only printed and not executed; in case of error,
316  * returns -1 and *result points to NULL
317  * it's callers responsibility to free() string returned in *result
318  */
319 static int
320 _history_expand_command(command, cmdlen, result)
321 	const char     *command;
322 	size_t          cmdlen;
323 	char          **result;
324 {
325 	char          **arr, *tempcmd, *line, *search = NULL, *cmd;
326 	const char     *event_data = NULL;
327 	static char    *from = NULL, *to = NULL;
328 	int             start = -1, end = -1, max, i, idx;
329 	int             h_on = 0, t_on = 0, r_on = 0, e_on = 0, p_on = 0,
330 	                g_on = 0;
331 	int             event_num = 0, retval;
332 	size_t		cmdsize;
333 
334 	*result = NULL;
335 
336 	cmd = alloca(cmdlen + 1);
337 	(void)strncpy(cmd, command, cmdlen);
338 	cmd[cmdlen] = 0;
339 
340 	idx = 1;
341 	/* find out which event to take */
342 	if (cmd[idx] == history_expansion_char) {
343 		event_num = history_length;
344 		idx++;
345 	} else {
346 		int    off, num;
347 		size_t len;
348 		off = idx;
349 		while (cmd[off] && !strchr(":^$*-%", cmd[off]))
350 			off++;
351 		num = atoi(&cmd[idx]);
352 		if (num != 0) {
353 			event_num = num;
354 			if (num < 0)
355 				event_num += history_length + 1;
356 		} else {
357 			int prefix = 1, curr_num;
358 			HistEvent ev;
359 
360 			len = off - idx;
361 			if (cmd[idx] == '?') {
362 				idx++, len--;
363 				if (cmd[off - 1] == '?')
364 					len--;
365 				else if (cmd[off] != '\n' && cmd[off] != '\0')
366 					return -1;
367 				prefix = 0;
368 			}
369 			search = alloca(len + 1);
370 			(void)strncpy(search, &cmd[idx], len);
371 			search[len] = '\0';
372 
373 			if (history(h, &ev, H_CURR) != 0)
374 				return -1;
375 			curr_num = ev.num;
376 
377 			if (prefix)
378 				retval = history_search_prefix(search, -1);
379 			else
380 				retval = history_search(search, -1);
381 
382 			if (retval == -1) {
383 				fprintf(rl_outstream, "%s: Event not found\n",
384 					search);
385 				return -1;
386 			}
387 			if (history(h, &ev, H_CURR) != 0)
388 				return -1;
389 			event_data = ev.str;
390 
391 			/* roll back to original position */
392 			history(h, &ev, H_NEXT_EVENT, curr_num);
393 		}
394 		idx = off;
395 	}
396 
397 	if (!event_data && event_num >= 0) {
398 		HIST_ENTRY *rl_he;
399 		rl_he = history_get(event_num);
400 		if (!rl_he)
401 			return 0;
402 		event_data = rl_he->line;
403 	} else
404 		return -1;
405 
406 	if (cmd[idx] != ':')
407 		return -1;
408 	cmd += idx + 1;
409 
410 	/* recognize cmd */
411 	if (*cmd == '^')
412 		start = end = 1, cmd++;
413 	else if (*cmd == '$')
414 		start = end = -1, cmd++;
415 	else if (*cmd == '*')
416 		start = 1, end = -1, cmd++;
417 	else if (isdigit((unsigned char) *cmd)) {
418 		const char *temp;
419 		int shifted = 0;
420 
421 		start = atoi(cmd);
422 		temp = cmd;
423 		for (; isdigit((unsigned char) *cmd); cmd++);
424 		if (temp != cmd)
425 			shifted = 1;
426 		if (shifted && *cmd == '-') {
427 			if (!isdigit((unsigned char) *(cmd + 1)))
428 				end = -2;
429 			else {
430 				end = atoi(cmd + 1);
431 				for (; isdigit((unsigned char) *cmd); cmd++);
432 			}
433 		} else if (shifted && *cmd == '*')
434 			end = -1, cmd++;
435 		else if (shifted)
436 			end = start;
437 	}
438 	if (*cmd == ':')
439 		cmd++;
440 
441 	line = strdup(event_data);
442 	for (; *cmd; cmd++) {
443 		if (*cmd == ':')
444 			continue;
445 		else if (*cmd == 'h')
446 			h_on = 1 | g_on, g_on = 0;
447 		else if (*cmd == 't')
448 			t_on = 1 | g_on, g_on = 0;
449 		else if (*cmd == 'r')
450 			r_on = 1 | g_on, g_on = 0;
451 		else if (*cmd == 'e')
452 			e_on = 1 | g_on, g_on = 0;
453 		else if (*cmd == 'p')
454 			p_on = 1 | g_on, g_on = 0;
455 		else if (*cmd == 'g')
456 			g_on = 2;
457 		else if (*cmd == 's' || *cmd == '&') {
458 			char  *what, *with, delim;
459 			int    len, from_len;
460 			size_t size;
461 
462 			if (*cmd == '&' && (from == NULL || to == NULL))
463 				continue;
464 			else if (*cmd == 's') {
465 				delim = *(++cmd), cmd++;
466 				size = 16;
467 				what = realloc(from, size);
468 				len = 0;
469 				for (; *cmd && *cmd != delim; cmd++) {
470 					if (*cmd == '\\'
471 					    && *(cmd + 1) == delim)
472 						cmd++;
473 					if (len >= size)
474 						what = realloc(what,
475 						    (size <<= 1));
476 					what[len++] = *cmd;
477 				}
478 				what[len] = '\0';
479 				from = what;
480 				if (*what == '\0') {
481 					free(what);
482 					if (search)
483 						from = strdup(search);
484 					else {
485 						from = NULL;
486 						return -1;
487 					}
488 				}
489 				cmd++;	/* shift after delim */
490 				if (!*cmd)
491 					continue;
492 
493 				size = 16;
494 				with = realloc(to, size);
495 				len = 0;
496 				from_len = strlen(from);
497 				for (; *cmd && *cmd != delim; cmd++) {
498 					if (len + from_len + 1 >= size) {
499 						size += from_len + 1;
500 						with = realloc(with, size);
501 					}
502 					if (*cmd == '&') {
503 						/* safe */
504 						(void)strcpy(&with[len], from);
505 						len += from_len;
506 						continue;
507 					}
508 					if (*cmd == '\\'
509 					    && (*(cmd + 1) == delim
510 						|| *(cmd + 1) == '&'))
511 						cmd++;
512 					with[len++] = *cmd;
513 				}
514 				with[len] = '\0';
515 				to = with;
516 
517 				tempcmd = _rl_compat_sub(line, from, to,
518 				    (g_on) ? 1 : 0);
519 				free(line);
520 				line = tempcmd;
521 				g_on = 0;
522 			}
523 		}
524 	}
525 
526 	arr = history_tokenize(line);
527 	free(line);		/* no more needed */
528 	if (arr && *arr == NULL)
529 		free(arr), arr = NULL;
530 	if (!arr)
531 		return -1;
532 
533 	/* find out max valid idx to array of array */
534 	max = 0;
535 	for (i = 0; arr[i]; i++)
536 		max++;
537 	max--;
538 
539 	/* set boundaries to something relevant */
540 	if (start < 0)
541 		start = 1;
542 	if (end < 0)
543 		end = max - ((end < -1) ? 1 : 0);
544 
545 	/* check boundaries ... */
546 	if (start > max || end > max || start > end)
547 		return -1;
548 
549 	for (i = 0; i <= max; i++) {
550 		char           *temp;
551 		if (h_on && (i == 1 || h_on > 1) &&
552 		    (temp = strrchr(arr[i], '/')))
553 			*(temp + 1) = '\0';
554 		if (t_on && (i == 1 || t_on > 1) &&
555 		    (temp = strrchr(arr[i], '/')))
556 			(void)strcpy(arr[i], temp + 1);
557 		if (r_on && (i == 1 || r_on > 1) &&
558 		    (temp = strrchr(arr[i], '.')))
559 			*temp = '\0';
560 		if (e_on && (i == 1 || e_on > 1) &&
561 		    (temp = strrchr(arr[i], '.')))
562 			(void)strcpy(arr[i], temp);
563 	}
564 
565 	cmdsize = 1, cmdlen = 0;
566 	tempcmd = malloc(cmdsize);
567 	for (i = start; start <= i && i <= end; i++) {
568 		int             arr_len;
569 
570 		arr_len = strlen(arr[i]);
571 		if (cmdlen + arr_len + 1 >= cmdsize) {
572 			cmdsize += arr_len + 1;
573 			tempcmd = realloc(tempcmd, cmdsize);
574 		}
575 		(void)strcpy(&tempcmd[cmdlen], arr[i]);	/* safe */
576 		cmdlen += arr_len;
577 		tempcmd[cmdlen++] = ' ';	/* add a space */
578 	}
579 	while (cmdlen > 0 && isspace((unsigned char) tempcmd[cmdlen - 1]))
580 		cmdlen--;
581 	tempcmd[cmdlen] = '\0';
582 
583 	*result = tempcmd;
584 
585 	for (i = 0; i <= max; i++)
586 		free(arr[i]);
587 	free(arr), arr = (char **) NULL;
588 	return (p_on) ? 2 : 1;
589 }
590 
591 /*
592  * csh-style history expansion
593  */
594 int
595 history_expand(str, output)
596 	char           *str;
597 	char          **output;
598 {
599 	int             i, retval = 0, idx;
600 	size_t		size;
601 	char           *temp, *result;
602 
603 	if (h == NULL || e == NULL)
604 		rl_initialize();
605 
606 	*output = strdup(str);	/* do it early */
607 
608 	if (str[0] == history_subst_char) {
609 		/* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */
610 		temp = alloca(4 + strlen(str) + 1);
611 		temp[0] = temp[1] = history_expansion_char;
612 		temp[2] = ':';
613 		temp[3] = 's';
614 		(void)strcpy(temp + 4, str);
615 		str = temp;
616 	}
617 #define ADD_STRING(what, len) 						\
618 	{								\
619 		if (idx + len + 1 > size)				\
620 			result = realloc(result, (size += len + 1));	\
621 		(void)strncpy(&result[idx], what, len);			\
622 		idx += len;						\
623 		result[idx] = '\0';					\
624 	}
625 
626 	result = NULL;
627 	size = idx = 0;
628 	for (i = 0; str[i];) {
629 		int start, j, loop_again;
630 		size_t len;
631 
632 		loop_again = 1;
633 		start = j = i;
634 loop:
635 		for (; str[j]; j++) {
636 			if (str[j] == '\\' &&
637 			    str[j + 1] == history_expansion_char) {
638 				(void)strcpy(&str[j], &str[j + 1]);
639 				continue;
640 			}
641 			if (!loop_again) {
642 				if (str[j] == '?') {
643 					while (str[j] && str[++j] != '?');
644 					if (str[j] == '?')
645 						j++;
646 				} else if (isspace((unsigned char) str[j]))
647 					break;
648 			}
649 			if (str[j] == history_expansion_char
650 			    && !strchr(history_no_expand_chars, str[j + 1])
651 			    && (!history_inhibit_expansion_function ||
652 			(*history_inhibit_expansion_function) (str, j) == 0))
653 				break;
654 		}
655 
656 		if (str[j] && str[j + 1] != '#' && loop_again) {
657 			i = j;
658 			j++;
659 			if (str[j] == history_expansion_char)
660 				j++;
661 			loop_again = 0;
662 			goto loop;
663 		}
664 		len = i - start;
665 		temp = &str[start];
666 		ADD_STRING(temp, len);
667 
668 		if (str[i] == '\0' || str[i] != history_expansion_char
669 		    || str[i + 1] == '#') {
670 			len = j - i;
671 			temp = &str[i];
672 			ADD_STRING(temp, len);
673 			if (start == 0)
674 				retval = 0;
675 			else
676 				retval = 1;
677 			break;
678 		}
679 		retval = _history_expand_command(&str[i], (size_t)(j - i),
680 						 &temp);
681 		if (retval != -1) {
682 			len = strlen(temp);
683 			ADD_STRING(temp, len);
684 		}
685 		i = j;
686 	}			/* for(i ...) */
687 
688 	if (retval == 2) {
689 		add_history(temp);
690 #ifdef GDB_411_HACK
691 		/* gdb 4.11 has been shipped with readline, where */
692 		/* history_expand() returned -1 when the line	  */
693 		/* should not be executed; in readline 2.1+	  */
694 		/* it should return 2 in such a case		  */
695 		retval = -1;
696 #endif
697 	}
698 	free(*output);
699 	*output = result;
700 
701 	return retval;
702 }
703 
704 /*
705  * Parse the string into individual tokens, similarily to how shell would do it.
706  */
707 char **
708 history_tokenize(str)
709 	const char     *str;
710 {
711 	int  size = 1, result_idx = 0, i, start;
712 	size_t len;
713 	char **result = NULL, *temp, delim = '\0';
714 
715 	for (i = 0; str[i]; i++) {
716 		while (isspace((unsigned char) str[i]))
717 			i++;
718 		start = i;
719 		for (; str[i]; i++) {
720 			if (str[i] == '\\') {
721 				if (str[i] != '\0')
722 					i++;
723 			} else if (str[i] == delim)
724 				delim = '\0';
725 			else if (!delim &&
726 			    (isspace((unsigned char) str[i]) ||
727 			    strchr("()<>;&|$", str[i])))
728 				break;
729 			else if (!delim && strchr("'`\"", str[i]))
730 				delim = str[i];
731 		}
732 
733 		if (result_idx + 2 >= size) {
734 			size <<= 1;
735 			result = realloc(result, size * sizeof(char *));
736 		}
737 		len = i - start;
738 		temp = malloc(len + 1);
739 		(void)strncpy(temp, &str[start], len);
740 		temp[len] = '\0';
741 		result[result_idx++] = temp;
742 		result[result_idx] = NULL;
743 	}
744 
745 	return result;
746 }
747 
748 /*
749  * limit size of history record to ``max'' events
750  */
751 void
752 stifle_history(max)
753 	int max;
754 {
755 	HistEvent ev;
756 
757 	if (h == NULL || e == NULL)
758 		rl_initialize();
759 
760 	if (history(h, &ev, H_SETSIZE, max) == 0)
761 		max_input_history = max;
762 }
763 
764 /*
765  * "unlimit" size of history - set the limit to maximum allowed int value
766  */
767 int
768 unstifle_history()
769 {
770 	HistEvent ev;
771 	int omax;
772 
773 	history(h, &ev, H_SETSIZE, INT_MAX);
774 	omax = max_input_history;
775 	max_input_history = INT_MAX;
776 	return omax;		/* some value _must_ be returned */
777 }
778 
779 int
780 history_is_stifled()
781 {
782 	/* cannot return true answer */
783 	return (max_input_history != INT_MAX);
784 }
785 
786 /*
787  * read history from a file given
788  */
789 int
790 read_history(filename)
791 	const char *filename;
792 {
793 	HistEvent ev;
794 
795 	if (h == NULL || e == NULL)
796 		rl_initialize();
797 	return history(h, &ev, H_LOAD, filename);
798 }
799 
800 /*
801  * write history to a file given
802  */
803 int
804 write_history(filename)
805 	const char *filename;
806 {
807 	HistEvent ev;
808 
809 	if (h == NULL || e == NULL)
810 		rl_initialize();
811 	return history(h, &ev, H_SAVE, filename);
812 }
813 
814 /*
815  * returns history ``num''th event
816  *
817  * returned pointer points to static variable
818  */
819 HIST_ENTRY *
820 history_get(num)
821 	int             num;
822 {
823 	static HIST_ENTRY she;
824 	HistEvent ev;
825 	int i = 1, curr_num;
826 
827 	if (h == NULL || e == NULL)
828 		rl_initialize();
829 
830 	/* rewind to beginning */
831 	if (history(h, &ev, H_CURR) != 0)
832 		return NULL;
833 	curr_num = ev.num;
834 	if (history(h, &ev, H_LAST) != 0)
835 		return NULL;	/* error */
836 	while (i < num && history(h, &ev, H_PREV) == 0)
837 		i++;
838 	if (i != num)
839 		return NULL;	/* not so many entries */
840 
841 	she.line = ev.str;
842 	she.data = NULL;
843 
844 	/* rewind history to the same event it was before */
845 	(void) history(h, &ev, H_FIRST);
846 	(void) history(h, &ev, H_NEXT_EVENT, curr_num);
847 
848 	return &she;
849 }
850 
851 /*
852  * add the line to history table
853  */
854 int
855 add_history(line)
856 	const char *line;
857 {
858 	HistEvent ev;
859 
860 	if (h == NULL || e == NULL)
861 		rl_initialize();
862 
863 	(void) history(h, &ev, H_ENTER, line);
864 	if (history(h, &ev, H_GETSIZE) == 0)
865 		history_length = ev.num;
866 
867 	return (!(history_length > 0));	/* return 0 if all is okay */
868 }
869 
870 /*
871  * clear the history list - delete all entries
872  */
873 void
874 clear_history()
875 {
876 	HistEvent ev;
877 	history(h, &ev, H_CLEAR);
878 }
879 
880 /*
881  * returns offset of the current history event
882  */
883 int
884 where_history()
885 {
886 	HistEvent ev;
887 	int curr_num, off;
888 
889 	if (history(h, &ev, H_CURR) != 0)
890 		return 0;
891 	curr_num = ev.num;
892 
893 	history(h, &ev, H_FIRST);
894 	off = 1;
895 	while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0)
896 		off++;
897 
898 	return off;
899 }
900 
901 /*
902  * returns current history event or NULL if there is no such event
903  */
904 HIST_ENTRY *
905 current_history()
906 {
907 	return _move_history(H_CURR);
908 }
909 
910 /*
911  * returns total number of bytes history events' data are using
912  */
913 int
914 history_total_bytes()
915 {
916 	HistEvent ev;
917 	int curr_num, size;
918 
919 	if (history(h, &ev, H_CURR) != 0)
920 		return -1;
921 	curr_num = ev.num;
922 
923 	history(h, &ev, H_FIRST);
924 	size = 0;
925 	do
926 		size += strlen(ev.str);
927 	while (history(h, &ev, H_NEXT) == 0);
928 
929 	/* get to the same position as before */
930 	history(h, &ev, H_PREV_EVENT, curr_num);
931 
932 	return size;
933 }
934 
935 /*
936  * sets the position in the history list to ``pos''
937  */
938 int
939 history_set_pos(pos)
940 	int pos;
941 {
942 	HistEvent ev;
943 	int off, curr_num;
944 
945 	if (pos > history_length || pos < 0)
946 		return -1;
947 
948 	history(h, &ev, H_CURR);
949 	curr_num = ev.num;
950 	history(h, &ev, H_FIRST);
951 	off = 0;
952 	while (off < pos && history(h, &ev, H_NEXT) == 0)
953 		off++;
954 
955 	if (off != pos) {	/* do a rollback in case of error */
956 		history(h, &ev, H_FIRST);
957 		history(h, &ev, H_NEXT_EVENT, curr_num);
958 		return -1;
959 	}
960 	return 0;
961 }
962 
963 /*
964  * returns previous event in history and shifts pointer accordingly
965  */
966 HIST_ENTRY *
967 previous_history()
968 {
969 	return _move_history(H_PREV);
970 }
971 
972 /*
973  * returns next event in history and shifts pointer accordingly
974  */
975 HIST_ENTRY *
976 next_history()
977 {
978 	return _move_history(H_NEXT);
979 }
980 
981 /*
982  * generic history search function
983  */
984 static int
985 _history_search_gen(str, direction, pos)
986 	const char *str;
987 	int direction, pos;
988 {
989 	HistEvent       ev;
990 	const char     *strp;
991 	int             curr_num;
992 
993 	if (history(h, &ev, H_CURR) != 0)
994 		return -1;
995 	curr_num = ev.num;
996 
997 	for (;;) {
998 		strp = strstr(ev.str, str);
999 		if (strp && (pos < 0 || &ev.str[pos] == strp))
1000 			return (int) (strp - ev.str);
1001 		if (history(h, &ev, direction < 0 ? H_PREV : H_NEXT) != 0)
1002 			break;
1003 	}
1004 
1005 	history(h, &ev, direction < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
1006 
1007 	return -1;
1008 }
1009 
1010 /*
1011  * searches for first history event containing the str
1012  */
1013 int
1014 history_search(str, direction)
1015 	const char     *str;
1016 	int             direction;
1017 {
1018 	return _history_search_gen(str, direction, -1);
1019 }
1020 
1021 /*
1022  * searches for first history event beginning with str
1023  */
1024 int
1025 history_search_prefix(str, direction)
1026 	const char     *str;
1027 	int             direction;
1028 {
1029 	return _history_search_gen(str, direction, 0);
1030 }
1031 
1032 /*
1033  * search for event in history containing str, starting at offset
1034  * abs(pos); continue backward, if pos<0, forward otherwise
1035  */
1036 /* ARGSUSED */
1037 int
1038 history_search_pos(str, direction, pos)
1039 	const char     *str;
1040 	int             direction, pos;
1041 {
1042 	HistEvent       ev;
1043 	int             curr_num, off;
1044 
1045 	off = (pos > 0) ? pos : -pos;
1046 	pos = (pos > 0) ? 1 : -1;
1047 
1048 	if (history(h, &ev, H_CURR) != 0)
1049 		return -1;
1050 	curr_num = ev.num;
1051 
1052 	if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0)
1053 		return -1;
1054 
1055 
1056 	for (;;) {
1057 		if (strstr(ev.str, str))
1058 			return off;
1059 		if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0)
1060 			break;
1061 	}
1062 
1063 	/* set "current" pointer back to previous state */
1064 	history(h, &ev, (pos < 0) ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
1065 
1066 	return -1;
1067 }
1068 
1069 
1070 /********************************/
1071 /* completition functions	 */
1072 
1073 /*
1074  * does tilde expansion of strings of type ``~user/foo''
1075  * if ``user'' isn't valid user name or ``txt'' doesn't start
1076  * w/ '~', returns pointer to strdup()ed copy of ``txt''
1077  *
1078  * it's callers's responsibility to free() returned string
1079  */
1080 char *
1081 tilde_expand(txt)
1082 	char     *txt;
1083 {
1084 	struct passwd  *pass;
1085 	char           *temp;
1086 	size_t          len = 0;
1087 
1088 	if (txt[0] != '~')
1089 		return strdup(txt);
1090 
1091 	temp = strchr(txt + 1, '/');
1092 	if (temp == NULL)
1093 		temp = strdup(txt + 1);
1094 	else {
1095 		len = temp - txt + 1;	/* text until string after slash */
1096 		temp = malloc(len);
1097 		(void)strncpy(temp, txt + 1, len - 2);
1098 		temp[len - 2] = '\0';
1099 	}
1100 	pass = getpwnam(temp);
1101 	free(temp);		/* value no more needed */
1102 	if (pass == NULL)
1103 		return strdup(txt);
1104 
1105 	/* update pointer txt to point at string immedially following */
1106 	/* first slash */
1107 	txt += len;
1108 
1109 	temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1);
1110 	(void)sprintf(temp, "%s/%s", pass->pw_dir, txt);
1111 
1112 	return temp;
1113 }
1114 
1115 /*
1116  * return first found file name starting by the ``text'' or NULL if no
1117  * such file can be found
1118  * value of ``state'' is ignored
1119  *
1120  * it's caller's responsibility to free returned string
1121  */
1122 char           *
1123 filename_completion_function(text, state)
1124 	const char     *text;
1125 	int             state;
1126 {
1127 	static DIR     *dir = NULL;
1128 	static char    *filename = NULL, *dirname = NULL;
1129 	static size_t   filename_len = 0;
1130 	struct dirent  *entry;
1131 	char           *temp;
1132 	size_t          len;
1133 
1134 	if (state == 0 || dir == NULL) {
1135 		if (dir != NULL) {
1136 			closedir(dir);
1137 			dir = NULL;
1138 		}
1139 		temp = strrchr(text, '/');
1140 		if (temp) {
1141 			temp++;
1142 			filename = realloc(filename, strlen(temp) + 1);
1143 			(void)strcpy(filename, temp);
1144 			len = temp - text;	/* including last slash */
1145 			dirname = realloc(dirname, len + 1);
1146 			(void)strncpy(dirname, text, len);
1147 			dirname[len] = '\0';
1148 		} else {
1149 			filename = strdup(text);
1150 			dirname = NULL;
1151 		}
1152 
1153 		/* support for ``~user'' syntax */
1154 		if (dirname && *dirname == '~') {
1155 			temp = tilde_expand(dirname);
1156 			dirname = realloc(dirname, strlen(temp) + 1);
1157 			(void)strcpy(dirname, temp);	/* safe */
1158 			free(temp);	/* no more needed */
1159 		}
1160 		/* will be used in cycle */
1161 		filename_len = strlen(filename);
1162 		if (filename_len == 0)
1163 			return NULL;	/* no expansion possible */
1164 
1165 		dir = opendir(dirname ? dirname : ".");
1166 		if (!dir)
1167 			return NULL;	/* cannot open the directory */
1168 	}
1169 	/* find the match */
1170 	while ((entry = readdir(dir)) != NULL) {
1171 		/* otherwise, get first entry where first */
1172 		/* filename_len characters are equal	  */
1173 		if (entry->d_name[0] == filename[0]
1174 #if defined(__SVR4) || defined(__linux__)
1175 		    && strlen(entry->d_name) >= filename_len
1176 #else
1177 		    && entry->d_namlen >= filename_len
1178 #endif
1179 		    && strncmp(entry->d_name, filename,
1180 			       filename_len) == 0)
1181 			break;
1182 	}
1183 
1184 	if (entry) {		/* match found */
1185 
1186 		struct stat     stbuf;
1187 #if defined(__SVR4) || defined(__linux__)
1188 		len = strlen(entry->d_name) +
1189 #else
1190 		len = entry->d_namlen +
1191 #endif
1192 			((dirname) ? strlen(dirname) : 0) + 1 + 1;
1193 		temp = malloc(len);
1194 		(void)sprintf(temp, "%s%s",
1195 			dirname ? dirname : "", entry->d_name);	/* safe */
1196 
1197 		/* test, if it's directory */
1198 		if (stat(temp, &stbuf) == 0 && S_ISDIR(stbuf.st_mode))
1199 			strcat(temp, "/");	/* safe */
1200 	} else
1201 		temp = NULL;
1202 
1203 	return temp;
1204 }
1205 
1206 /*
1207  * a completion generator for usernames; returns _first_ username
1208  * which starts with supplied text
1209  * text contains a partial username preceded by random character
1210  * (usually '~'); state is ignored
1211  * it's callers responsibility to free returned value
1212  */
1213 char           *
1214 username_completion_function(text, state)
1215 	const char     *text;
1216 	int             state;
1217 {
1218 	struct passwd  *pwd;
1219 
1220 	if (text[0] == '\0')
1221 		return NULL;
1222 
1223 	if (*text == '~')
1224 		text++;
1225 
1226 	if (state == 0)
1227 		setpwent();
1228 
1229 	while ((pwd = getpwent()) && text[0] == pwd->pw_name[0]
1230 	       && strcmp(text, pwd->pw_name) == 0);
1231 
1232 	if (pwd == NULL) {
1233 		endpwent();
1234 		return NULL;
1235 	}
1236 	return strdup(pwd->pw_name);
1237 }
1238 
1239 /*
1240  * el-compatible wrapper around rl_complete; needed for key binding
1241  */
1242 /* ARGSUSED */
1243 static unsigned char
1244 _el_rl_complete(el, ch)
1245 	EditLine       *el;
1246 	int             ch;
1247 {
1248 	return (unsigned char) rl_complete(0, ch);
1249 }
1250 
1251 /*
1252  * returns list of completitions for text given
1253  */
1254 char          **
1255 completion_matches(text, genfunc)
1256 	const char     *text;
1257 	CPFunction     *genfunc;
1258 {
1259 	char          **match_list = NULL, *retstr, *prevstr;
1260 	size_t          math_list_len, max_equal, which, i;
1261 	int		matches;
1262 
1263 	if (h == NULL || e == NULL)
1264 		rl_initialize();
1265 
1266 	matches = 0;
1267 	math_list_len = 1;
1268 	while ((retstr = (*genfunc) (text, matches)) != NULL) {
1269 		if (matches + 1 >= math_list_len) {
1270 			math_list_len <<= 1;
1271 			match_list = realloc(match_list,
1272 			    math_list_len * sizeof(char *));
1273 		}
1274 		match_list[++matches] = retstr;
1275 	}
1276 
1277 	if (!match_list)
1278 		return (char **) NULL;	/* nothing found */
1279 
1280 	/* find least denominator and insert it to match_list[0] */
1281 	which = 2;
1282 	prevstr = match_list[1];
1283 	max_equal = strlen(prevstr);
1284 	for (; which < matches; which++) {
1285 		for (i = 0; i < max_equal &&
1286 		    prevstr[i] == match_list[which][i]; i++)
1287 			continue;
1288 		max_equal = i;
1289 	}
1290 
1291 	retstr = malloc(max_equal + 1);
1292 	(void)strncpy(retstr, match_list[1], max_equal);
1293 	retstr[max_equal] = '\0';
1294 	match_list[0] = retstr;
1295 
1296 	/* add NULL as last pointer to the array */
1297 	if (matches + 1 >= math_list_len)
1298 		match_list = realloc(match_list,
1299 		    (math_list_len + 1) * sizeof(char *));
1300 	match_list[matches + 1] = (char *) NULL;
1301 
1302 	return match_list;
1303 }
1304 
1305 /*
1306  * called by rl_complete()
1307  */
1308 /* ARGSUSED */
1309 static int
1310 rl_complete_internal(what_to_do)
1311 	int             what_to_do;
1312 {
1313 	CPFunction     *complet_func;
1314 	const LineInfo *li;
1315 	char           *temp, *temp2, **arr;
1316 	size_t          len;
1317 
1318 	if (h == NULL || e == NULL)
1319 		rl_initialize();
1320 
1321 	complet_func = rl_completion_entry_function;
1322 	if (!complet_func)
1323 		complet_func = filename_completion_function;
1324 
1325 	li = el_line(e);
1326 	/* LINTED const cast */
1327 	temp = (char *) li->cursor;
1328 	while (temp > li->buffer &&
1329 	    !strchr(rl_basic_word_break_characters, *(temp - 1)))
1330 		temp--;
1331 
1332 	len = li->cursor - temp;
1333 	temp2 = alloca(len + 1);
1334 	(void)strncpy(temp2, temp, len);
1335 	temp = temp2;
1336 	temp[len] = '\0';
1337 
1338 	/* these can be used by function called in completion_matches() */
1339 	/* or (*rl_attempted_completion_function)() */
1340 	rl_point = li->cursor - li->buffer;
1341 	rl_end = li->lastchar - li->buffer;
1342 
1343 	if (!rl_attempted_completion_function)
1344 		arr = completion_matches(temp, complet_func);
1345 	else {
1346 		int             end = li->cursor - li->buffer;
1347 		arr = (*rl_attempted_completion_function) (temp, (int)
1348 							   (end - len), end);
1349 	}
1350 
1351 	if (arr) {
1352 		int             i;
1353 
1354 		el_deletestr(e, (int)len);
1355 		el_insertstr(e, arr[0]);
1356 		if (strcmp(arr[0], arr[1]) == 0) {
1357 			/* lcd is valid object, so add a space to mark it */
1358 			/* in case of filename completition, add a space  */
1359 			/* only if object found is not directory	  */
1360 			size_t alen = strlen(arr[0]);
1361 			if (complet_func != filename_completion_function
1362 			    || (alen > 0 && (arr[0])[alen - 1] != '/'))
1363 				el_insertstr(e, " ");
1364 		} else
1365 			/* lcd is not a valid object - further specification */
1366 			/* is needed */
1367 			el_beep(e);
1368 
1369 		/* free elements of array and the array itself */
1370 		for (i = 0; arr[i]; i++)
1371 			free(arr[i]);
1372 		free(arr), arr = NULL;
1373 
1374 		return CC_REFRESH;
1375 	}
1376 	return CC_NORM;
1377 }
1378 
1379 /*
1380  * complete word at current point
1381  */
1382 int
1383 rl_complete(ignore, invoking_key)
1384 	int             ignore, invoking_key;
1385 {
1386 	if (h == NULL || e == NULL)
1387 		rl_initialize();
1388 
1389 	if (rl_inhibit_completion) {
1390 		rl_insert(ignore, invoking_key);
1391 		return CC_REFRESH;
1392 	} else
1393 		return rl_complete_internal(invoking_key);
1394 }
1395 
1396 /*
1397  * misc other functions
1398  */
1399 
1400 /*
1401  * bind key c to readline-type function func
1402  */
1403 int
1404 rl_bind_key(c, func)
1405 	int             c;
1406 	int func        __P((int, int));
1407 {
1408 	int             retval = -1;
1409 
1410 	if (h == NULL || e == NULL)
1411 		rl_initialize();
1412 
1413 	if (func == rl_insert) {
1414 		/* XXX notice there is no range checking of ``c'' */
1415 		e->el_map.key[c] = ED_INSERT;
1416 		retval = 0;
1417 	}
1418 	return retval;
1419 }
1420 
1421 /*
1422  * read one key from input - handles chars pushed back
1423  * to input stream also
1424  */
1425 int
1426 rl_read_key()
1427 {
1428 	char            fooarr[2 * sizeof(int)];
1429 
1430 	if (e == NULL || h == NULL)
1431 		rl_initialize();
1432 
1433 	return el_getc(e, fooarr);
1434 }
1435 
1436 /*
1437  * reset the terminal
1438  */
1439 /* ARGSUSED */
1440 void
1441 rl_reset_terminal(p)
1442 	const char     *p;
1443 {
1444 	if (h == NULL || e == NULL)
1445 		rl_initialize();
1446 	el_reset(e);
1447 }
1448 
1449 /*
1450  * insert character ``c'' back into input stream, ``count'' times
1451  */
1452 int
1453 rl_insert(count, c)
1454 	int             count, c;
1455 {
1456 	char            arr[2];
1457 
1458 	if (h == NULL || e == NULL)
1459 		rl_initialize();
1460 
1461 	/* XXX - int -> char conversion can lose on multichars */
1462 	arr[0] = c;
1463 	arr[1] = '\0';
1464 
1465 	for (; count > 0; count--)
1466 		el_push(e, arr);
1467 
1468 	return 0;
1469 }
1470