1/*-
2 * Copyright (c) 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)histedit.c	8.2 (Berkeley) 5/4/95";
36#endif
37#endif /* not lint */
38#include <sys/cdefs.h>
39__FBSDID("$FreeBSD$");
40
41#include <sys/param.h>
42#include <sys/stat.h>
43#include <dirent.h>
44#include <limits.h>
45#include <paths.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <unistd.h>
49/*
50 * Editline and history functions (and glue).
51 */
52#include "shell.h"
53#include "parser.h"
54#include "var.h"
55#include "options.h"
56#include "main.h"
57#include "output.h"
58#include "mystring.h"
59#include "builtins.h"
60#ifndef NO_HISTORY
61#include "myhistedit.h"
62#include "error.h"
63#include "eval.h"
64#include "memalloc.h"
65
66#define MAXHISTLOOPS	4	/* max recursions through fc */
67#define DEFEDITOR	"ed"	/* default editor *should* be $EDITOR */
68
69History *hist;	/* history cookie */
70EditLine *el;	/* editline cookie */
71int displayhist;
72static FILE *el_in, *el_out;
73
74static char *fc_replace(const char *, char *, char *);
75static int not_fcnumber(const char *);
76static int str_to_event(const char *, int);
77static int comparator(const void *, const void *, void *);
78static char **sh_matches(const char *, int, int);
79static unsigned char sh_complete(EditLine *, int);
80
81/*
82 * Set history and editing status.  Called whenever the status may
83 * have changed (figures out what to do).
84 */
85void
86histedit(void)
87{
88
89#define editing (Eflag || Vflag)
90
91	if (iflag) {
92		if (!hist) {
93			/*
94			 * turn history on
95			 */
96			INTOFF;
97			hist = history_init();
98			INTON;
99
100			if (hist != NULL)
101				sethistsize(histsizeval());
102			else
103				out2fmt_flush("sh: can't initialize history\n");
104		}
105		if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
106			/*
107			 * turn editing on
108			 */
109			char *term;
110
111			INTOFF;
112			if (el_in == NULL)
113				el_in = fdopen(0, "r");
114			if (el_out == NULL)
115				el_out = fdopen(2, "w");
116			if (el_in == NULL || el_out == NULL)
117				goto bad;
118			term = lookupvar("TERM");
119			if (term)
120				setenv("TERM", term, 1);
121			else
122				unsetenv("TERM");
123			el = el_init(arg0, el_in, el_out, el_out);
124			if (el != NULL) {
125				if (hist)
126					el_set(el, EL_HIST, history, hist);
127				el_set(el, EL_PROMPT, getprompt);
128				el_set(el, EL_ADDFN, "sh-complete",
129				    "Filename completion",
130				    sh_complete);
131			} else {
132bad:
133				out2fmt_flush("sh: can't initialize editing\n");
134			}
135			INTON;
136		} else if (!editing && el) {
137			INTOFF;
138			el_end(el);
139			el = NULL;
140			INTON;
141		}
142		if (el) {
143			if (Vflag)
144				el_set(el, EL_EDITOR, "vi");
145			else if (Eflag) {
146				el_set(el, EL_EDITOR, "emacs");
147				el_set(el, EL_BIND, "^R", "em-inc-search-prev", NULL);
148			}
149			el_set(el, EL_BIND, "^I", "sh-complete", NULL);
150			el_source(el, NULL);
151		}
152	} else {
153		INTOFF;
154		if (el) {	/* no editing if not interactive */
155			el_end(el);
156			el = NULL;
157		}
158		if (hist) {
159			history_end(hist);
160			hist = NULL;
161		}
162		INTON;
163	}
164}
165
166
167void
168sethistsize(const char *hs)
169{
170	int histsize;
171	HistEvent he;
172
173	if (hist != NULL) {
174		if (hs == NULL || !is_number(hs))
175			histsize = 100;
176		else
177			histsize = atoi(hs);
178		history(hist, &he, H_SETSIZE, histsize);
179		history(hist, &he, H_SETUNIQUE, 1);
180	}
181}
182
183void
184setterm(const char *term)
185{
186	if (rootshell && el != NULL && term != NULL)
187		el_set(el, EL_TERMINAL, term);
188}
189
190int
191histcmd(int argc, char **argv __unused)
192{
193	int ch;
194	const char *editor = NULL;
195	HistEvent he;
196	int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
197	int i, retval;
198	const char *firststr, *laststr;
199	int first, last, direction;
200	char *pat = NULL, *repl = NULL;
201	static int active = 0;
202	struct jmploc jmploc;
203	struct jmploc *savehandler;
204	char editfilestr[PATH_MAX];
205	char *volatile editfile;
206	FILE *efp = NULL;
207	int oldhistnum;
208
209	if (hist == NULL)
210		error("history not active");
211
212	if (argc == 1)
213		error("missing history argument");
214
215	while (not_fcnumber(*argptr) && (ch = nextopt("e:lnrs")) != '\0')
216		switch ((char)ch) {
217		case 'e':
218			editor = shoptarg;
219			break;
220		case 'l':
221			lflg = 1;
222			break;
223		case 'n':
224			nflg = 1;
225			break;
226		case 'r':
227			rflg = 1;
228			break;
229		case 's':
230			sflg = 1;
231			break;
232		}
233
234	savehandler = handler;
235	/*
236	 * If executing...
237	 */
238	if (lflg == 0 || editor || sflg) {
239		lflg = 0;	/* ignore */
240		editfile = NULL;
241		/*
242		 * Catch interrupts to reset active counter and
243		 * cleanup temp files.
244		 */
245		if (setjmp(jmploc.loc)) {
246			active = 0;
247			if (editfile)
248				unlink(editfile);
249			handler = savehandler;
250			longjmp(handler->loc, 1);
251		}
252		handler = &jmploc;
253		if (++active > MAXHISTLOOPS) {
254			active = 0;
255			displayhist = 0;
256			error("called recursively too many times");
257		}
258		/*
259		 * Set editor.
260		 */
261		if (sflg == 0) {
262			if (editor == NULL &&
263			    (editor = bltinlookup("FCEDIT", 1)) == NULL &&
264			    (editor = bltinlookup("EDITOR", 1)) == NULL)
265				editor = DEFEDITOR;
266			if (editor[0] == '-' && editor[1] == '\0') {
267				sflg = 1;	/* no edit */
268				editor = NULL;
269			}
270		}
271	}
272
273	/*
274	 * If executing, parse [old=new] now
275	 */
276	if (lflg == 0 && *argptr != NULL &&
277	     ((repl = strchr(*argptr, '=')) != NULL)) {
278		pat = *argptr;
279		*repl++ = '\0';
280		argptr++;
281	}
282	/*
283	 * determine [first] and [last]
284	 */
285	if (*argptr == NULL) {
286		firststr = lflg ? "-16" : "-1";
287		laststr = "-1";
288	} else if (argptr[1] == NULL) {
289		firststr = argptr[0];
290		laststr = lflg ? "-1" : argptr[0];
291	} else if (argptr[2] == NULL) {
292		firststr = argptr[0];
293		laststr = argptr[1];
294	} else
295		error("too many arguments");
296	/*
297	 * Turn into event numbers.
298	 */
299	first = str_to_event(firststr, 0);
300	last = str_to_event(laststr, 1);
301
302	if (rflg) {
303		i = last;
304		last = first;
305		first = i;
306	}
307	/*
308	 * XXX - this should not depend on the event numbers
309	 * always increasing.  Add sequence numbers or offset
310	 * to the history element in next (diskbased) release.
311	 */
312	direction = first < last ? H_PREV : H_NEXT;
313
314	/*
315	 * If editing, grab a temp file.
316	 */
317	if (editor) {
318		int fd;
319		INTOFF;		/* easier */
320		sprintf(editfilestr, "%s/_shXXXXXX", _PATH_TMP);
321		if ((fd = mkstemp(editfilestr)) < 0)
322			error("can't create temporary file %s", editfile);
323		editfile = editfilestr;
324		if ((efp = fdopen(fd, "w")) == NULL) {
325			close(fd);
326			error("Out of space");
327		}
328	}
329
330	/*
331	 * Loop through selected history events.  If listing or executing,
332	 * do it now.  Otherwise, put into temp file and call the editor
333	 * after.
334	 *
335	 * The history interface needs rethinking, as the following
336	 * convolutions will demonstrate.
337	 */
338	history(hist, &he, H_FIRST);
339	retval = history(hist, &he, H_NEXT_EVENT, first);
340	for (;retval != -1; retval = history(hist, &he, direction)) {
341		if (lflg) {
342			if (!nflg)
343				out1fmt("%5d ", he.num);
344			out1str(he.str);
345		} else {
346			const char *s = pat ?
347			   fc_replace(he.str, pat, repl) : he.str;
348
349			if (sflg) {
350				if (displayhist) {
351					out2str(s);
352					flushout(out2);
353				}
354				evalstring(s, 0);
355				if (displayhist && hist) {
356					/*
357					 *  XXX what about recursive and
358					 *  relative histnums.
359					 */
360					oldhistnum = he.num;
361					history(hist, &he, H_ENTER, s);
362					/*
363					 * XXX H_ENTER moves the internal
364					 * cursor, set it back to the current
365					 * entry.
366					 */
367					history(hist, &he,
368					    H_NEXT_EVENT, oldhistnum);
369				}
370			} else
371				fputs(s, efp);
372		}
373		/*
374		 * At end?  (if we were to lose last, we'd sure be
375		 * messed up).
376		 */
377		if (he.num == last)
378			break;
379	}
380	if (editor) {
381		char *editcmd;
382
383		fclose(efp);
384		INTON;
385		editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
386		sprintf(editcmd, "%s %s", editor, editfile);
387		evalstring(editcmd, 0);	/* XXX - should use no JC command */
388		readcmdfile(editfile);	/* XXX - should read back - quick tst */
389		unlink(editfile);
390	}
391
392	if (lflg == 0 && active > 0)
393		--active;
394	if (displayhist)
395		displayhist = 0;
396	handler = savehandler;
397	return 0;
398}
399
400static char *
401fc_replace(const char *s, char *p, char *r)
402{
403	char *dest;
404	int plen = strlen(p);
405
406	STARTSTACKSTR(dest);
407	while (*s) {
408		if (*s == *p && strncmp(s, p, plen) == 0) {
409			STPUTS(r, dest);
410			s += plen;
411			*p = '\0';	/* so no more matches */
412		} else
413			STPUTC(*s++, dest);
414	}
415	STPUTC('\0', dest);
416	dest = grabstackstr(dest);
417
418	return (dest);
419}
420
421static int
422not_fcnumber(const char *s)
423{
424	if (s == NULL)
425		return (0);
426	if (*s == '-')
427		s++;
428	return (!is_number(s));
429}
430
431static int
432str_to_event(const char *str, int last)
433{
434	HistEvent he;
435	const char *s = str;
436	int relative = 0;
437	int i, retval;
438
439	retval = history(hist, &he, H_FIRST);
440	switch (*s) {
441	case '-':
442		relative = 1;
443		/*FALLTHROUGH*/
444	case '+':
445		s++;
446	}
447	if (is_number(s)) {
448		i = atoi(s);
449		if (relative) {
450			while (retval != -1 && i--) {
451				retval = history(hist, &he, H_NEXT);
452			}
453			if (retval == -1)
454				retval = history(hist, &he, H_LAST);
455		} else {
456			retval = history(hist, &he, H_NEXT_EVENT, i);
457			if (retval == -1) {
458				/*
459				 * the notion of first and last is
460				 * backwards to that of the history package
461				 */
462				retval = history(hist, &he, last ? H_FIRST : H_LAST);
463			}
464		}
465		if (retval == -1)
466			error("history number %s not found (internal error)",
467			       str);
468	} else {
469		/*
470		 * pattern
471		 */
472		retval = history(hist, &he, H_PREV_STR, str);
473		if (retval == -1)
474			error("history pattern not found: %s", str);
475	}
476	return (he.num);
477}
478
479int
480bindcmd(int argc, char **argv)
481{
482	int ret;
483	FILE *old;
484	FILE *out;
485
486	if (el == NULL)
487		error("line editing is disabled");
488
489	INTOFF;
490
491	out = out1fp();
492	if (out == NULL)
493		error("Out of space");
494
495	el_get(el, EL_GETFP, 1, &old);
496	el_set(el, EL_SETFP, 1, out);
497
498	ret = el_parse(el, argc, __DECONST(const char **, argv));
499
500	el_set(el, EL_SETFP, 1, old);
501
502	fclose(out);
503
504	INTON;
505
506	return ret;
507}
508
509#else
510#include "error.h"
511
512int
513histcmd(int argc __unused, char **argv __unused)
514{
515
516	error("not compiled with history support");
517	/*NOTREACHED*/
518	return (0);
519}
520
521int
522bindcmd(int argc __unused, char **argv __unused)
523{
524
525	error("not compiled with line editing support");
526	return (0);
527}
528#endif
529
530/*
531 * Comparator function for qsort(). The use of curpos here is to skip
532 * characters that we already know to compare equal (common prefix).
533 */
534static int
535comparator(const void *a, const void *b, void *thunk)
536{
537	size_t curpos = (intptr_t)thunk;
538	return (strcmp(*(char *const *)a + curpos,
539		*(char *const *)b + curpos));
540}
541
542/*
543 * This function is passed to libedit's fn_complete2(). The library will
544 * use it instead of its standard function that finds matching files in
545 * current directory. If we're at the start of the line, we want to look
546 * for available commands from all paths in $PATH.
547 */
548static char
549**sh_matches(const char *text, int start, int end)
550{
551	char *free_path = NULL, *path;
552	const char *dirname;
553	char **matches = NULL;
554	size_t i = 0, size = 16, j, k;
555	size_t curpos = end - start;
556
557	if (start > 0 || memchr("/.~", text[0], 3) != NULL)
558		return (NULL);
559	if ((free_path = path = strdup(pathval())) == NULL)
560		goto out;
561	if ((matches = malloc(size * sizeof(matches[0]))) == NULL)
562		goto out;
563	while ((dirname = strsep(&path, ":")) != NULL) {
564		struct dirent *entry;
565		DIR *dir;
566		int dfd;
567
568		dir = opendir(dirname[0] == '\0' ? "." : dirname);
569		if (dir == NULL)
570			continue;
571		if ((dfd = dirfd(dir)) == -1) {
572			closedir(dir);
573			continue;
574		}
575		while ((entry = readdir(dir)) != NULL) {
576			struct stat statb;
577			char **rmatches;
578
579			if (strncmp(entry->d_name, text, curpos) != 0)
580				continue;
581			if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) {
582				if (fstatat(dfd, entry->d_name, &statb, 0) == -1)
583					continue;
584				if (!S_ISREG(statb.st_mode))
585					continue;
586			} else if (entry->d_type != DT_REG)
587				continue;
588			matches[++i] = strdup(entry->d_name);
589			if (i < size - 1)
590				continue;
591			size *= 2;
592			rmatches = reallocarray(matches, size, sizeof(matches[0]));
593			if (rmatches == NULL) {
594				closedir(dir);
595				goto out;
596			}
597			matches = rmatches;
598		}
599		closedir(dir);
600	}
601out:
602	free(free_path);
603	/*
604	 * matches[0] is special: it's not a real matching file name but a common
605	 * prefix for all matching names. It can't be null, unlike any other
606	 * element of the array. When strings matches[0] and matches[1] compare
607	 * equal and matches[2] is null that means to libedit that there is only
608	 * a single match. It will then replace user input with possibly escaped
609	 * string in matches[0] which is the reason to copy the full name of the
610	 * only match.
611	 */
612	if (i == 0) {
613		free(matches);
614		return (NULL);
615	} else if (i == 1) {
616		matches[0] = strdup(matches[1]);
617		matches[2] = NULL;
618		if (matches[0] != NULL)
619			return (matches);
620	} else
621		matches[0] = strdup(text);
622	if (matches[0] == NULL) {
623		for (j = 1; j <= i; j++)
624			free(matches[j]);
625		free(matches);
626		return (NULL);
627	}
628	qsort_s(matches + 1, i, sizeof(matches[0]), comparator,
629		(void *)(intptr_t)curpos);
630	for (j = 1, k = 2; k <= i; k++)
631		if (strcmp(matches[j] + curpos, matches[k] + curpos) == 0)
632			free(matches[k]);
633		else
634			matches[++j] = matches[k];
635	matches[j + 1] = NULL;
636	return (matches);
637}
638
639/*
640 * This is passed to el_set(el, EL_ADDFN, ...) so that it's possible to
641 * bind a key (tab by default) to execute the function.
642 */
643unsigned char
644sh_complete(EditLine *sel, int ch __unused)
645{
646	return (unsigned char)fn_complete2(sel, NULL, sh_matches,
647		L" \t\n\"\\'`@$><=;|&{(", NULL, NULL, (size_t)100,
648		NULL, &((int) {0}), NULL, NULL, FN_QUOTE_MATCH);
649}
650