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