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