1/*	$OpenBSD: history.c,v 1.39 2010/05/19 17:36:08 jasper Exp $	*/
2/*	$OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $	*/
3
4/*-
5 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
6 *	Thorsten Glaser <tg@mirbsd.org>
7 *
8 * Provided that these terms and disclaimer and all copyright notices
9 * are retained or reproduced in an accompanying document, permission
10 * is granted to deal in this work without restriction, including un-
11 * limited rights to use, publicly perform, distribute, sell, modify,
12 * merge, give away, or sublicence.
13 *
14 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
15 * the utmost extent permitted by applicable law, neither express nor
16 * implied; without malicious intent or gross negligence. In no event
17 * may a licensor, author or contributor be held liable for indirect,
18 * direct, other damage, loss, or other issues arising in any way out
19 * of dealing in the work, even if advised of the possibility of such
20 * damage or existence of a defect, except proven that it results out
21 * of said person's immediate fault when using the work as intended.
22 */
23
24#include "sh.h"
25#if HAVE_SYS_FILE_H
26#include <sys/file.h>
27#endif
28
29__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.111 2011/09/07 15:24:16 tg Exp $");
30
31/*-
32 * MirOS: This is the default mapping type, and need not be specified.
33 * IRIX doesn't have this constant.
34 */
35#ifndef MAP_FILE
36#define MAP_FILE	0
37#endif
38
39Trap sigtraps[NSIG + 1];
40static struct sigaction Sigact_ign;
41
42#if HAVE_PERSISTENT_HISTORY
43static int hist_count_lines(unsigned char *, int);
44static int hist_shrink(unsigned char *, int);
45static unsigned char *hist_skip_back(unsigned char *,int *,int);
46static void histload(Source *, unsigned char *, int);
47static void histinsert(Source *, int, const char *);
48static void writehistfile(int, char *);
49static int sprinkle(int);
50#endif
51
52static int hist_execute(char *);
53static int hist_replace(char **, const char *, const char *, bool);
54static char **hist_get(const char *, bool, bool);
55static char **hist_get_oldest(void);
56static void histbackup(void);
57
58static char **current;		/* current position in history[] */
59static int hstarted;		/* set after hist_init() called */
60static Source *hist_source;
61
62#if HAVE_PERSISTENT_HISTORY
63/* current history file: name, fd, size */
64static char *hname;
65static int histfd;
66static size_t hsize;
67#endif
68
69static const char Tnot_in_history[] = "not in history";
70#define Thistory (Tnot_in_history + 7)
71
72int
73c_fc(const char **wp)
74{
75	struct shf *shf;
76	struct temp *tf;
77	const char *p;
78	char *editor = NULL;
79	bool gflag = false, lflag = false, nflag = false, rflag = false,
80	    sflag = false;
81	int optc;
82	const char *first = NULL, *last = NULL;
83	char **hfirst, **hlast, **hp;
84
85	if (!Flag(FTALKING_I)) {
86		bi_errorf("history %ss not available", Tfunction);
87		return (1);
88	}
89
90	while ((optc = ksh_getopt(wp, &builtin_opt,
91	    "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != -1)
92		switch (optc) {
93
94		case 'e':
95			p = builtin_opt.optarg;
96			if (ksh_isdash(p))
97				sflag = true;
98			else {
99				size_t len = strlen(p);
100
101				/* almost certainly not overflowing */
102				editor = alloc(len + 4, ATEMP);
103				memcpy(editor, p, len);
104				memcpy(editor + len, " $_", 4);
105			}
106			break;
107
108		/* non-AT&T ksh */
109		case 'g':
110			gflag = true;
111			break;
112
113		case 'l':
114			lflag = true;
115			break;
116
117		case 'n':
118			nflag = true;
119			break;
120
121		case 'r':
122			rflag = true;
123			break;
124
125		/* POSIX version of -e - */
126		case 's':
127			sflag = true;
128			break;
129
130		/* kludge city - accept -num as -- -num (kind of) */
131		case '0': case '1': case '2': case '3': case '4':
132		case '5': case '6': case '7': case '8': case '9':
133			p = shf_smprintf("-%c%s",
134					optc, builtin_opt.optarg);
135			if (!first)
136				first = p;
137			else if (!last)
138				last = p;
139			else {
140				bi_errorf("too many arguments");
141				return (1);
142			}
143			break;
144
145		case '?':
146			return (1);
147		}
148	wp += builtin_opt.optind;
149
150	/* Substitute and execute command */
151	if (sflag) {
152		char *pat = NULL, *rep = NULL;
153
154		if (editor || lflag || nflag || rflag) {
155			bi_errorf("can't use -e, -l, -n, -r with -s (-e -)");
156			return (1);
157		}
158
159		/* Check for pattern replacement argument */
160		if (*wp && **wp && (p = cstrchr(*wp + 1, '='))) {
161			strdupx(pat, *wp, ATEMP);
162			rep = pat + (p - *wp);
163			*rep++ = '\0';
164			wp++;
165		}
166		/* Check for search prefix */
167		if (!first && (first = *wp))
168			wp++;
169		if (last || *wp) {
170			bi_errorf("too many arguments");
171			return (1);
172		}
173
174		hp = first ? hist_get(first, false, false) :
175		    hist_get_newest(false);
176		if (!hp)
177			return (1);
178		return (hist_replace(hp, pat, rep, gflag));
179	}
180
181	if (editor && (lflag || nflag)) {
182		bi_errorf("can't use -l, -n with -e");
183		return (1);
184	}
185
186	if (!first && (first = *wp))
187		wp++;
188	if (!last && (last = *wp))
189		wp++;
190	if (*wp) {
191		bi_errorf("too many arguments");
192		return (1);
193	}
194	if (!first) {
195		hfirst = lflag ? hist_get("-16", true, true) :
196		    hist_get_newest(false);
197		if (!hfirst)
198			return (1);
199		/* can't fail if hfirst didn't fail */
200		hlast = hist_get_newest(false);
201	} else {
202		/*
203		 * POSIX says not an error if first/last out of bounds
204		 * when range is specified; AT&T ksh and pdksh allow out
205		 * of bounds for -l as well.
206		 */
207		hfirst = hist_get(first, tobool(lflag || last), lflag);
208		if (!hfirst)
209			return (1);
210		hlast = last ? hist_get(last, true, lflag) :
211		    (lflag ? hist_get_newest(false) : hfirst);
212		if (!hlast)
213			return (1);
214	}
215	if (hfirst > hlast) {
216		char **temp;
217
218		temp = hfirst; hfirst = hlast; hlast = temp;
219		/* POSIX */
220		rflag = !rflag;
221	}
222
223	/* List history */
224	if (lflag) {
225		char *s, *t;
226
227		for (hp = rflag ? hlast : hfirst;
228		    hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) {
229			if (!nflag)
230				shf_fprintf(shl_stdout, "%d",
231				    hist_source->line - (int)(histptr - hp));
232			shf_putc('\t', shl_stdout);
233			/* print multi-line commands correctly */
234			s = *hp;
235			while ((t = strchr(s, '\n'))) {
236				*t = '\0';
237				shf_fprintf(shl_stdout, "%s\n\t", s);
238				*t++ = '\n';
239				s = t;
240			}
241			shf_fprintf(shl_stdout, "%s\n", s);
242		}
243		shf_flush(shl_stdout);
244		return (0);
245	}
246
247	/* Run editor on selected lines, then run resulting commands */
248
249	tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps);
250	if (!(shf = tf->shf)) {
251		bi_errorf("can't %s temporary file %s: %s",
252		    "create", tf->name, strerror(errno));
253		return (1);
254	}
255	for (hp = rflag ? hlast : hfirst;
256	    hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1)
257		shf_fprintf(shf, "%s\n", *hp);
258	if (shf_close(shf) == EOF) {
259		bi_errorf("can't %s temporary file %s: %s",
260		    "write", tf->name, strerror(errno));
261		return (1);
262	}
263
264	/* Ignore setstr errors here (arbitrary) */
265	setstr(local("_", false), tf->name, KSH_RETURN_ERROR);
266
267	/* XXX: source should not get trashed by this.. */
268	{
269		Source *sold = source;
270		int ret;
271
272		ret = command(editor ? editor : "${FCEDIT:-/bin/ed} $_", 0);
273		source = sold;
274		if (ret)
275			return (ret);
276	}
277
278	{
279		struct stat statb;
280		XString xs;
281		char *xp;
282		int n;
283
284		if (!(shf = shf_open(tf->name, O_RDONLY, 0, 0))) {
285			bi_errorf("can't %s temporary file %s: %s",
286			    "open", tf->name, strerror(errno));
287			return (1);
288		}
289
290		if (stat(tf->name, &statb) < 0)
291			n = 128;
292		else if (statb.st_size > (1024 * 1048576)) {
293			bi_errorf("%s %s too large: %lu", Thistory,
294			    "file", (unsigned long)statb.st_size);
295			goto errout;
296		} else
297			n = statb.st_size + 1;
298		Xinit(xs, xp, n, hist_source->areap);
299		while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) {
300			xp += n;
301			if (Xnleft(xs, xp) <= 0)
302				XcheckN(xs, xp, Xlength(xs, xp));
303		}
304		if (n < 0) {
305			bi_errorf("can't %s temporary file %s: %s",
306			    "read", tf->name, strerror(shf_errno(shf)));
307 errout:
308			shf_close(shf);
309			return (1);
310		}
311		shf_close(shf);
312		*xp = '\0';
313		strip_nuls(Xstring(xs, xp), Xlength(xs, xp));
314		return (hist_execute(Xstring(xs, xp)));
315	}
316}
317
318/* Save cmd in history, execute cmd (cmd gets trashed) */
319static int
320hist_execute(char *cmd)
321{
322	Source *sold;
323	int ret;
324	char *p, *q;
325
326	histbackup();
327
328	for (p = cmd; p; p = q) {
329		if ((q = strchr(p, '\n'))) {
330			/* kill the newline */
331			*q++ = '\0';
332			if (!*q)
333				/* ignore trailing newline */
334				q = NULL;
335		}
336		histsave(&hist_source->line, p, true, true);
337
338		/* POSIX doesn't say this is done... */
339		shellf("%s\n", p);
340		if (q)
341			/* restore \n (trailing \n not restored) */
342			q[-1] = '\n';
343	}
344
345	/*-
346	 * Commands are executed here instead of pushing them onto the
347	 * input 'cause POSIX says the redirection and variable assignments
348	 * in
349	 *	X=y fc -e - 42 2> /dev/null
350	 * are to effect the repeated commands environment.
351	 */
352	/* XXX: source should not get trashed by this.. */
353	sold = source;
354	ret = command(cmd, 0);
355	source = sold;
356	return (ret);
357}
358
359static int
360hist_replace(char **hp, const char *pat, const char *rep, bool globr)
361{
362	char *line;
363
364	if (!pat)
365		strdupx(line, *hp, ATEMP);
366	else {
367		char *s, *s1;
368		size_t pat_len = strlen(pat);
369		size_t rep_len = strlen(rep);
370		size_t len;
371		XString xs;
372		char *xp;
373		bool any_subst = false;
374
375		Xinit(xs, xp, 128, ATEMP);
376		for (s = *hp; (s1 = strstr(s, pat)) && (!any_subst || globr);
377		    s = s1 + pat_len) {
378			any_subst = true;
379			len = s1 - s;
380			XcheckN(xs, xp, len + rep_len);
381			/*; first part */
382			memcpy(xp, s, len);
383			xp += len;
384			/* replacement */
385			memcpy(xp, rep, rep_len);
386			xp += rep_len;
387		}
388		if (!any_subst) {
389			bi_errorf("bad substitution");
390			return (1);
391		}
392		len = strlen(s) + 1;
393		XcheckN(xs, xp, len);
394		memcpy(xp, s, len);
395		xp += len;
396		line = Xclose(xs, xp);
397	}
398	return (hist_execute(line));
399}
400
401/*
402 * get pointer to history given pattern
403 * pattern is a number or string
404 */
405static char **
406hist_get(const char *str, bool approx, bool allow_cur)
407{
408	char **hp = NULL;
409	int n;
410
411	if (getn(str, &n)) {
412		hp = histptr + (n < 0 ? n : (n - hist_source->line));
413		if ((ptrdiff_t)hp < (ptrdiff_t)history) {
414			if (approx)
415				hp = hist_get_oldest();
416			else {
417				bi_errorf("%s: %s", str, Tnot_in_history);
418				hp = NULL;
419			}
420		} else if ((ptrdiff_t)hp > (ptrdiff_t)histptr) {
421			if (approx)
422				hp = hist_get_newest(allow_cur);
423			else {
424				bi_errorf("%s: %s", str, Tnot_in_history);
425				hp = NULL;
426			}
427		} else if (!allow_cur && hp == histptr) {
428			bi_errorf("%s: %s", str, "invalid range");
429			hp = NULL;
430		}
431	} else {
432		int anchored = *str == '?' ? (++str, 0) : 1;
433
434		/* the -1 is to avoid the current fc command */
435		if ((n = findhist(histptr - history - 1, 0, str, anchored)) < 0)
436			bi_errorf("%s: %s", str, Tnot_in_history);
437		else
438			hp = &history[n];
439	}
440	return (hp);
441}
442
443/* Return a pointer to the newest command in the history */
444char **
445hist_get_newest(bool allow_cur)
446{
447	if (histptr < history || (!allow_cur && histptr == history)) {
448		bi_errorf("no history (yet)");
449		return (NULL);
450	}
451	return (allow_cur ? histptr : histptr - 1);
452}
453
454/* Return a pointer to the oldest command in the history */
455static char **
456hist_get_oldest(void)
457{
458	if (histptr <= history) {
459		bi_errorf("no history (yet)");
460		return (NULL);
461	}
462	return (history);
463}
464
465/*
466 * Back up over last histsave
467 */
468static void
469histbackup(void)
470{
471	static int last_line = -1;
472
473	if (histptr >= history && last_line != hist_source->line) {
474		hist_source->line--;
475		afree(*histptr, APERM);
476		histptr--;
477		last_line = hist_source->line;
478	}
479}
480
481/*
482 * Return the current position.
483 */
484char **
485histpos(void)
486{
487	return (current);
488}
489
490int
491histnum(int n)
492{
493	int last = histptr - history;
494
495	if (n < 0 || n >= last) {
496		current = histptr;
497		return (last);
498	} else {
499		current = &history[n];
500		return (n);
501	}
502}
503
504/*
505 * This will become unnecessary if hist_get is modified to allow
506 * searching from positions other than the end, and in either
507 * direction.
508 */
509int
510findhist(int start, int fwd, const char *str, int anchored)
511{
512	char **hp;
513	int maxhist = histptr - history;
514	int incr = fwd ? 1 : -1;
515	size_t len = strlen(str);
516
517	if (start < 0 || start >= maxhist)
518		start = maxhist;
519
520	hp = &history[start];
521	for (; hp >= history && hp <= histptr; hp += incr)
522		if ((anchored && strncmp(*hp, str, len) == 0) ||
523		    (!anchored && strstr(*hp, str)))
524			return (hp - history);
525
526	return (-1);
527}
528
529/*
530 *	set history
531 *	this means reallocating the dataspace
532 */
533void
534sethistsize(int n)
535{
536	if (n > 0 && n != histsize) {
537		int cursize = histptr - history;
538
539		/* save most recent history */
540		if (n < cursize) {
541			memmove(history, histptr - n, n * sizeof(char *));
542			cursize = n;
543		}
544
545		history = aresize2(history, n, sizeof(char *), APERM);
546
547		histsize = n;
548		histptr = history + cursize;
549	}
550}
551
552#if HAVE_PERSISTENT_HISTORY
553/*
554 *	set history file
555 *	This can mean reloading/resetting/starting history file
556 *	maintenance
557 */
558void
559sethistfile(const char *name)
560{
561	/* if not started then nothing to do */
562	if (hstarted == 0)
563		return;
564
565	/* if the name is the same as the name we have */
566	if (hname && strcmp(hname, name) == 0)
567		return;
568
569	/*
570	 * its a new name - possibly
571	 */
572	if (histfd) {
573		/* yes the file is open */
574		(void)close(histfd);
575		histfd = 0;
576		hsize = 0;
577		afree(hname, APERM);
578		hname = NULL;
579		/* let's reset the history */
580		histptr = history - 1;
581		hist_source->line = 0;
582	}
583
584	hist_init(hist_source);
585}
586#endif
587
588/*
589 *	initialise the history vector
590 */
591void
592init_histvec(void)
593{
594	if (history == (char **)NULL) {
595		histsize = HISTORYSIZE;
596		history = alloc2(histsize, sizeof(char *), APERM);
597		histptr = history - 1;
598	}
599}
600
601
602/*
603 *	Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to
604 *	a) permit HISTSIZE to control number of lines of history stored
605 *	b) maintain a physical history file
606 *
607 *	It turns out that there is a lot of ghastly hackery here
608 */
609
610#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
611/* do not save command in history but possibly sync */
612bool
613histsync(void)
614{
615	bool changed = false;
616
617	if (histfd) {
618		int lno = hist_source->line;
619
620		hist_source->line++;
621		writehistfile(0, NULL);
622		hist_source->line--;
623
624		if (lno != hist_source->line)
625			changed = true;
626	}
627
628	return (changed);
629}
630#endif
631
632/*
633 * save command in history
634 */
635void
636histsave(int *lnp, const char *cmd, bool dowrite MKSH_A_UNUSED, bool ignoredups)
637{
638	char **hp;
639	char *c, *cp;
640
641	strdupx(c, cmd, APERM);
642	if ((cp = strchr(c, '\n')) != NULL)
643		*cp = '\0';
644
645	if (ignoredups && !strcmp(c, *histptr)
646#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
647	    && !histsync()
648#endif
649	    ) {
650		afree(c, APERM);
651		return;
652	}
653	++*lnp;
654
655#if HAVE_PERSISTENT_HISTORY
656	if (histfd && dowrite)
657		writehistfile(*lnp, c);
658#endif
659
660	hp = histptr;
661
662	if (++hp >= history + histsize) {
663		/* remove oldest command */
664		afree(*history, APERM);
665		for (hp = history; hp < history + histsize - 1; hp++)
666			hp[0] = hp[1];
667	}
668	*hp = c;
669	histptr = hp;
670}
671
672/*
673 *	Write history data to a file nominated by HISTFILE
674 *	if HISTFILE is unset then history still happens, but
675 *	the data is not written to a file
676 *	All copies of ksh looking at the file will maintain the
677 *	same history. This is ksh behaviour.
678 *
679 *	This stuff uses mmap()
680 *	if your system ain't got it - then you'll have to undef HISTORYFILE
681 */
682
683/*-
684 *	Open a history file
685 *	Format is:
686 *	Bytes 1, 2:
687 *		HMAGIC - just to check that we are dealing with
688 *		the correct object
689 *	Then follows a number of stored commands
690 *	Each command is
691 *	<command byte><command number(4 bytes)><bytes><null>
692 */
693#define HMAGIC1		0xab
694#define HMAGIC2		0xcd
695#define COMMAND		0xff
696
697void
698hist_init(Source *s)
699{
700#if HAVE_PERSISTENT_HISTORY
701	unsigned char *base;
702	int lines, fd, rv = 0;
703	off_t hfsize;
704#endif
705
706	if (Flag(FTALKING) == 0)
707		return;
708
709	hstarted = 1;
710
711	hist_source = s;
712
713#if HAVE_PERSISTENT_HISTORY
714	if ((hname = str_val(global("HISTFILE"))) == NULL)
715		return;
716	strdupx(hname, hname, APERM);
717
718 retry:
719	/* we have a file and are interactive */
720	if ((fd = open(hname, O_RDWR|O_CREAT|O_APPEND, 0600)) < 0)
721		return;
722
723	histfd = savefd(fd);
724	if (histfd != fd)
725		close(fd);
726
727	(void)flock(histfd, LOCK_EX);
728
729	hfsize = lseek(histfd, (off_t)0, SEEK_END);
730	hsize = 1024 * 1048576;
731	if (hfsize < (off_t)hsize)
732		hsize = (size_t)hfsize;
733
734	if (hsize == 0) {
735		/* add magic */
736		if (sprinkle(histfd)) {
737			hist_finish();
738			return;
739		}
740	} else if (hsize > 0) {
741		/*
742		 * we have some data
743		 */
744		base = (void *)mmap(NULL, hsize, PROT_READ,
745		    MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
746		/*
747		 * check on its validity
748		 */
749		if (base == (unsigned char *)MAP_FAILED ||
750		    *base != HMAGIC1 || base[1] != HMAGIC2) {
751			if (base != (unsigned char *)MAP_FAILED)
752				munmap((caddr_t)base, hsize);
753			hist_finish();
754			if (unlink(hname) /* fails */)
755				goto hiniterr;
756			goto retry;
757		}
758		if (hsize > 2) {
759			lines = hist_count_lines(base+2, hsize-2);
760			if (lines > histsize) {
761				/* we need to make the file smaller */
762				if (hist_shrink(base, hsize))
763					rv = unlink(hname);
764				munmap((caddr_t)base, hsize);
765				hist_finish();
766				if (rv) {
767 hiniterr:
768					bi_errorf("can't %s %s: %s",
769					    "unlink HISTFILE", hname,
770					    strerror(errno));
771					hsize = 0;
772					return;
773				}
774				goto retry;
775			}
776		}
777		histload(hist_source, base+2, hsize-2);
778		munmap((caddr_t)base, hsize);
779	}
780	(void)flock(histfd, LOCK_UN);
781	hfsize = lseek(histfd, (off_t)0, SEEK_END);
782	hsize = 1024 * 1048576;
783	if (hfsize < (off_t)hsize)
784		hsize = hfsize;
785#endif
786}
787
788#if HAVE_PERSISTENT_HISTORY
789typedef enum state {
790	shdr,		/* expecting a header */
791	sline,		/* looking for a null byte to end the line */
792	sn1,		/* bytes 1 to 4 of a line no */
793	sn2, sn3, sn4
794} State;
795
796static int
797hist_count_lines(unsigned char *base, int bytes)
798{
799	State state = shdr;
800	int lines = 0;
801
802	while (bytes--) {
803		switch (state) {
804		case shdr:
805			if (*base == COMMAND)
806				state = sn1;
807			break;
808		case sn1:
809			state = sn2; break;
810		case sn2:
811			state = sn3; break;
812		case sn3:
813			state = sn4; break;
814		case sn4:
815			state = sline; break;
816		case sline:
817			if (*base == '\0') {
818				lines++;
819				state = shdr;
820			}
821		}
822		base++;
823	}
824	return (lines);
825}
826
827/*
828 *	Shrink the history file to histsize lines
829 */
830static int
831hist_shrink(unsigned char *oldbase, int oldbytes)
832{
833	int fd, rv = 0;
834	char *nfile = NULL;
835	struct	stat statb;
836	unsigned char *nbase = oldbase;
837	int nbytes = oldbytes;
838
839	nbase = hist_skip_back(nbase, &nbytes, histsize);
840	if (nbase == NULL)
841		return (1);
842	if (nbase == oldbase)
843		return (0);
844
845	/*
846	 *	create temp file
847	 */
848	nfile = shf_smprintf("%s.%d", hname, (int)procpid);
849	if ((fd = open(nfile, O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0)
850		goto errout;
851	if (fstat(histfd, &statb) >= 0 &&
852	    chown(nfile, statb.st_uid, statb.st_gid))
853		goto errout;
854
855	if (sprinkle(fd) || write(fd, nbase, nbytes) != nbytes)
856		goto errout;
857	close(fd);
858	fd = -1;
859
860	/*
861	 *	rename
862	 */
863	if (rename(nfile, hname) < 0) {
864 errout:
865		if (fd >= 0) {
866			close(fd);
867			if (nfile)
868				unlink(nfile);
869		}
870		rv = 1;
871	}
872	afree(nfile, ATEMP);
873	return (rv);
874}
875
876/*
877 *	find a pointer to the data 'no' back from the end of the file
878 *	return the pointer and the number of bytes left
879 */
880static unsigned char *
881hist_skip_back(unsigned char *base, int *bytes, int no)
882{
883	int lines = 0;
884	unsigned char *ep;
885
886	for (ep = base + *bytes; --ep > base; ) {
887		/*
888		 * this doesn't really work: the 4 byte line number that
889		 * is encoded after the COMMAND byte can itself contain
890		 * the COMMAND byte....
891		 */
892		for (; ep > base && *ep != COMMAND; ep--)
893			;
894		if (ep == base)
895			break;
896		if (++lines == no) {
897			*bytes = *bytes - ((char *)ep - (char *)base);
898			return (ep);
899		}
900	}
901	return (NULL);
902}
903
904/*
905 *	load the history structure from the stored data
906 */
907static void
908histload(Source *s, unsigned char *base, int bytes)
909{
910	State state;
911	int lno = 0;
912	unsigned char *line = NULL;
913
914	for (state = shdr; bytes-- > 0; base++) {
915		switch (state) {
916		case shdr:
917			if (*base == COMMAND)
918				state = sn1;
919			break;
920		case sn1:
921			lno = (((*base)&0xff)<<24);
922			state = sn2;
923			break;
924		case sn2:
925			lno |= (((*base)&0xff)<<16);
926			state = sn3;
927			break;
928		case sn3:
929			lno |= (((*base)&0xff)<<8);
930			state = sn4;
931			break;
932		case sn4:
933			lno |= (*base)&0xff;
934			line = base+1;
935			state = sline;
936			break;
937		case sline:
938			if (*base == '\0') {
939				/* worry about line numbers */
940				if (histptr >= history && lno-1 != s->line) {
941					/* a replacement ? */
942					histinsert(s, lno, (char *)line);
943				} else {
944					s->line = lno--;
945					histsave(&lno, (char *)line, false,
946					    false);
947				}
948				state = shdr;
949			}
950		}
951	}
952}
953
954/*
955 *	Insert a line into the history at a specified number
956 */
957static void
958histinsert(Source *s, int lno, const char *line)
959{
960	char **hp;
961
962	if (lno >= s->line - (histptr - history) && lno <= s->line) {
963		hp = &histptr[lno - s->line];
964		if (*hp)
965			afree(*hp, APERM);
966		strdupx(*hp, line, APERM);
967	}
968}
969
970/*
971 *	write a command to the end of the history file
972 *	This *MAY* seem easy but it's also necessary to check
973 *	that the history file has not changed in size.
974 *	If it has - then some other shell has written to it
975 *	and we should read those commands to update our history
976 */
977static void
978writehistfile(int lno, char *cmd)
979{
980	off_t sizenow;
981	ssize_t bytes;
982	unsigned char *base, *news, hdr[5];
983
984	(void)flock(histfd, LOCK_EX);
985	sizenow = lseek(histfd, (off_t)0, SEEK_END);
986	if ((sizenow <= (1024 * 1048576)) && ((size_t)sizenow != hsize)) {
987		/*
988		 *	Things have changed
989		 */
990		if ((size_t)sizenow > hsize) {
991			/* someone has added some lines */
992			bytes = (size_t)sizenow - hsize;
993			base = (void *)mmap(NULL, (size_t)sizenow, PROT_READ,
994			    MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
995			if (base == (unsigned char *)MAP_FAILED)
996				goto bad;
997			news = base + hsize;
998			if (*news != COMMAND) {
999				munmap((caddr_t)base, (size_t)sizenow);
1000				goto bad;
1001			}
1002			hist_source->line--;
1003			histload(hist_source, news, bytes);
1004			hist_source->line++;
1005			lno = hist_source->line;
1006			munmap((caddr_t)base, (size_t)sizenow);
1007			hsize = (size_t)sizenow;
1008		} else {
1009			/* it has shrunk */
1010			/* but to what? */
1011			/* we'll give up for now */
1012			goto bad;
1013		}
1014	}
1015	if (cmd) {
1016		/*
1017		 *	we can write our bit now
1018		 */
1019		hdr[0] = COMMAND;
1020		hdr[1] = (lno>>24)&0xff;
1021		hdr[2] = (lno>>16)&0xff;
1022		hdr[3] = (lno>>8)&0xff;
1023		hdr[4] = lno&0xff;
1024		bytes = strlen(cmd) + 1;
1025		if ((write(histfd, hdr, 5) != 5) ||
1026		    (write(histfd, cmd, bytes) != bytes))
1027			goto bad;
1028		sizenow = lseek(histfd, (off_t)0, SEEK_END);
1029		hsize = 1024 * 1048576;
1030		if (sizenow < (off_t)hsize)
1031			hsize = (size_t)sizenow;
1032	}
1033	(void)flock(histfd, LOCK_UN);
1034	return;
1035 bad:
1036	hist_finish();
1037}
1038
1039void
1040hist_finish(void)
1041{
1042	(void)flock(histfd, LOCK_UN);
1043	(void)close(histfd);
1044	histfd = 0;
1045}
1046
1047/*
1048 *	add magic to the history file
1049 */
1050static int
1051sprinkle(int fd)
1052{
1053	static const unsigned char mag[] = { HMAGIC1, HMAGIC2 };
1054
1055	return (write(fd, mag, 2) != 2);
1056}
1057#endif
1058
1059#if !HAVE_SYS_SIGNAME
1060static const struct mksh_sigpair {
1061	const char *const name;
1062	int nr;
1063} mksh_sigpairs[] = {
1064#include "signames.inc"
1065	{ NULL, 0 }
1066};
1067#endif
1068
1069void
1070inittraps(void)
1071{
1072	int i;
1073	const char *cs;
1074
1075	trap_exstat = -1;
1076
1077	/* Populate sigtraps based on sys_signame and sys_siglist. */
1078	for (i = 0; i <= NSIG; i++) {
1079		sigtraps[i].signal = i;
1080		if (i == ksh_SIGERR) {
1081			sigtraps[i].name = "ERR";
1082			sigtraps[i].mess = "Error handler";
1083		} else {
1084#if HAVE_SYS_SIGNAME
1085			cs = sys_signame[i];
1086#else
1087			const struct mksh_sigpair *pair = mksh_sigpairs;
1088			while ((pair->nr != i) && (pair->name != NULL))
1089				++pair;
1090			cs = pair->name;
1091#endif
1092			if ((cs == NULL) ||
1093			    (cs[0] == '\0'))
1094				sigtraps[i].name = shf_smprintf("%d", i);
1095			else {
1096				char *s;
1097
1098				if (!strncasecmp(cs, "SIG", 3))
1099					cs += 3;
1100				strdupx(s, cs, APERM);
1101				sigtraps[i].name = s;
1102				while ((*s = ksh_toupper(*s)))
1103					++s;
1104			}
1105#if HAVE_SYS_SIGLIST
1106			sigtraps[i].mess = sys_siglist[i];
1107#elif HAVE_STRSIGNAL
1108			sigtraps[i].mess = strsignal(i);
1109#else
1110			sigtraps[i].mess = NULL;
1111#endif
1112			if ((sigtraps[i].mess == NULL) ||
1113			    (sigtraps[i].mess[0] == '\0'))
1114				sigtraps[i].mess = shf_smprintf("%s %d",
1115				    "Signal", i);
1116		}
1117	}
1118	/* our name for signal 0 */
1119	sigtraps[ksh_SIGEXIT].name = "EXIT";
1120
1121	(void)sigemptyset(&Sigact_ign.sa_mask);
1122	Sigact_ign.sa_flags = 0; /* interruptible */
1123	Sigact_ign.sa_handler = SIG_IGN;
1124
1125	sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
1126	sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
1127	/* SIGTERM is not fatal for interactive */
1128	sigtraps[SIGTERM].flags |= TF_DFL_INTR;
1129	sigtraps[SIGHUP].flags |= TF_FATAL;
1130	sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
1131
1132	/* these are always caught so we can clean up any temporary files. */
1133	setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
1134	setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
1135	setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
1136	setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
1137}
1138
1139static void alarm_catcher(int sig);
1140
1141void
1142alarm_init(void)
1143{
1144	sigtraps[SIGALRM].flags |= TF_SHELL_USES;
1145	setsig(&sigtraps[SIGALRM], alarm_catcher,
1146		SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
1147}
1148
1149/* ARGSUSED */
1150static void
1151alarm_catcher(int sig MKSH_A_UNUSED)
1152{
1153	/* this runs inside interrupt context, with errno saved */
1154
1155	if (ksh_tmout_state == TMOUT_READING) {
1156		int left = alarm(0);
1157
1158		if (left == 0) {
1159			ksh_tmout_state = TMOUT_LEAVING;
1160			intrsig = 1;
1161		} else
1162			alarm(left);
1163	}
1164}
1165
1166Trap *
1167gettrap(const char *name, int igncase)
1168{
1169	int n = NSIG + 1;
1170	Trap *p;
1171	const char *n2;
1172	int (*cmpfunc)(const char *, const char *) = strcmp;
1173
1174	if (ksh_isdigit(*name)) {
1175		if (getn(name, &n) && 0 <= n && n < NSIG)
1176			return (&sigtraps[n]);
1177		else
1178			return (NULL);
1179	}
1180
1181	n2 = strncasecmp(name, "SIG", 3) ? NULL : name + 3;
1182	if (igncase)
1183		cmpfunc = strcasecmp;
1184	for (p = sigtraps; --n >= 0; p++)
1185		if (!cmpfunc(p->name, name) || (n2 && !cmpfunc(p->name, n2)))
1186			return (p);
1187	return (NULL);
1188}
1189
1190/*
1191 * trap signal handler
1192 */
1193void
1194trapsig(int i)
1195{
1196	Trap *p = &sigtraps[i];
1197	int errno_sv = errno;
1198
1199	trap = p->set = 1;
1200	if (p->flags & TF_DFL_INTR)
1201		intrsig = 1;
1202	if ((p->flags & TF_FATAL) && !p->trap) {
1203		fatal_trap = 1;
1204		intrsig = 1;
1205	}
1206	if (p->shtrap)
1207		(*p->shtrap)(i);
1208	errno = errno_sv;
1209}
1210
1211/*
1212 * called when we want to allow the user to ^C out of something - won't
1213 * work if user has trapped SIGINT.
1214 */
1215void
1216intrcheck(void)
1217{
1218	if (intrsig)
1219		runtraps(TF_DFL_INTR|TF_FATAL);
1220}
1221
1222/*
1223 * called after EINTR to check if a signal with normally causes process
1224 * termination has been received.
1225 */
1226int
1227fatal_trap_check(void)
1228{
1229	int i;
1230	Trap *p;
1231
1232	/* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
1233	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
1234		if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
1235			/* return value is used as an exit code */
1236			return (128 + p->signal);
1237	return (0);
1238}
1239
1240/*
1241 * Returns the signal number of any pending traps: ie, a signal which has
1242 * occurred for which a trap has been set or for which the TF_DFL_INTR flag
1243 * is set.
1244 */
1245int
1246trap_pending(void)
1247{
1248	int i;
1249	Trap *p;
1250
1251	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
1252		if (p->set && ((p->trap && p->trap[0]) ||
1253		    ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap)))
1254			return (p->signal);
1255	return (0);
1256}
1257
1258/*
1259 * run any pending traps. If intr is set, only run traps that
1260 * can interrupt commands.
1261 */
1262void
1263runtraps(int flag)
1264{
1265	int i;
1266	Trap *p;
1267
1268	if (ksh_tmout_state == TMOUT_LEAVING) {
1269		ksh_tmout_state = TMOUT_EXECUTING;
1270		warningf(false, "timed out waiting for input");
1271		unwind(LEXIT);
1272	} else
1273		/*
1274		 * XXX: this means the alarm will have no effect if a trap
1275		 * is caught after the alarm() was started...not good.
1276		 */
1277		ksh_tmout_state = TMOUT_EXECUTING;
1278	if (!flag)
1279		trap = 0;
1280	if (flag & TF_DFL_INTR)
1281		intrsig = 0;
1282	if (flag & TF_FATAL)
1283		fatal_trap = 0;
1284	++trap_nested;
1285	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
1286		if (p->set && (!flag ||
1287		    ((p->flags & flag) && p->trap == NULL)))
1288			runtrap(p, false);
1289	if (!--trap_nested)
1290		runtrap(NULL, true);
1291}
1292
1293void
1294runtrap(Trap *p, bool is_last)
1295{
1296	int old_changed = 0, i;
1297	char *trapstr;
1298
1299	if (p == NULL)
1300		/* just clean up, see runtraps() above */
1301		goto donetrap;
1302	i = p->signal;
1303	trapstr = p->trap;
1304	p->set = 0;
1305	if (trapstr == NULL) {
1306		/* SIG_DFL */
1307		if (p->flags & TF_FATAL) {
1308			/* eg, SIGHUP */
1309			exstat = 128 + i;
1310			unwind(LLEAVE);
1311		}
1312		if (p->flags & TF_DFL_INTR) {
1313			/* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
1314			exstat = 128 + i;
1315			unwind(LINTR);
1316		}
1317		goto donetrap;
1318	}
1319	if (trapstr[0] == '\0')
1320		/* SIG_IGN */
1321		goto donetrap;
1322	if (i == ksh_SIGEXIT || i == ksh_SIGERR) {
1323		/* avoid recursion on these */
1324		old_changed = p->flags & TF_CHANGED;
1325		p->flags &= ~TF_CHANGED;
1326		p->trap = NULL;
1327	}
1328	if (trap_exstat == -1)
1329		trap_exstat = exstat;
1330	/*
1331	 * Note: trapstr is fully parsed before anything is executed, thus
1332	 * no problem with afree(p->trap) in settrap() while still in use.
1333	 */
1334	command(trapstr, current_lineno);
1335	if (i == ksh_SIGEXIT || i == ksh_SIGERR) {
1336		if (p->flags & TF_CHANGED)
1337			/* don't clear TF_CHANGED */
1338			afree(trapstr, APERM);
1339		else
1340			p->trap = trapstr;
1341		p->flags |= old_changed;
1342	}
1343
1344 donetrap:
1345	/* we're the last trap of a sequence executed */
1346	if (is_last && trap_exstat != -1) {
1347		exstat = trap_exstat;
1348		trap_exstat = -1;
1349	}
1350}
1351
1352/* clear pending traps and reset user's trap handlers; used after fork(2) */
1353void
1354cleartraps(void)
1355{
1356	int i;
1357	Trap *p;
1358
1359	trap = 0;
1360	intrsig = 0;
1361	fatal_trap = 0;
1362	for (i = NSIG+1, p = sigtraps; --i >= 0; p++) {
1363		p->set = 0;
1364		if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
1365			settrap(p, NULL);
1366	}
1367}
1368
1369/* restore signals just before an exec(2) */
1370void
1371restoresigs(void)
1372{
1373	int i;
1374	Trap *p;
1375
1376	for (i = NSIG+1, p = sigtraps; --i >= 0; p++)
1377		if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
1378			setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
1379			    SS_RESTORE_CURR|SS_FORCE);
1380}
1381
1382void
1383settrap(Trap *p, const char *s)
1384{
1385	sig_t f;
1386
1387	if (p->trap)
1388		afree(p->trap, APERM);
1389	/* handles s == NULL */
1390	strdupx(p->trap, s, APERM);
1391	p->flags |= TF_CHANGED;
1392	f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
1393
1394	p->flags |= TF_USER_SET;
1395	if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
1396		f = trapsig;
1397	else if (p->flags & TF_SHELL_USES) {
1398		if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
1399			/* do what user wants at exec time */
1400			p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
1401			if (f == SIG_IGN)
1402				p->flags |= TF_EXEC_IGN;
1403			else
1404				p->flags |= TF_EXEC_DFL;
1405		}
1406
1407		/*
1408		 * assumes handler already set to what shell wants it
1409		 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
1410		 */
1411		return;
1412	}
1413
1414	/* todo: should we let user know signal is ignored? how? */
1415	setsig(p, f, SS_RESTORE_CURR|SS_USER);
1416}
1417
1418/*
1419 * Called by c_print() when writing to a co-process to ensure SIGPIPE won't
1420 * kill shell (unless user catches it and exits)
1421 */
1422int
1423block_pipe(void)
1424{
1425	int restore_dfl = 0;
1426	Trap *p = &sigtraps[SIGPIPE];
1427
1428	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
1429		setsig(p, SIG_IGN, SS_RESTORE_CURR);
1430		if (p->flags & TF_ORIG_DFL)
1431			restore_dfl = 1;
1432	} else if (p->cursig == SIG_DFL) {
1433		setsig(p, SIG_IGN, SS_RESTORE_CURR);
1434		/* restore to SIG_DFL */
1435		restore_dfl = 1;
1436	}
1437	return (restore_dfl);
1438}
1439
1440/* Called by c_print() to undo whatever block_pipe() did */
1441void
1442restore_pipe(int restore_dfl)
1443{
1444	if (restore_dfl)
1445		setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
1446}
1447
1448/*
1449 * Set action for a signal. Action may not be set if original
1450 * action was SIG_IGN, depending on the value of flags and FTALKING.
1451 */
1452int
1453setsig(Trap *p, sig_t f, int flags)
1454{
1455	struct sigaction sigact;
1456
1457	if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR)
1458		return (1);
1459
1460	/*
1461	 * First time setting this signal? If so, get and note the current
1462	 * setting.
1463	 */
1464	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
1465		sigaction(p->signal, &Sigact_ign, &sigact);
1466		p->flags |= sigact.sa_handler == SIG_IGN ?
1467		    TF_ORIG_IGN : TF_ORIG_DFL;
1468		p->cursig = SIG_IGN;
1469	}
1470
1471	/*-
1472	 * Generally, an ignored signal stays ignored, except if
1473	 *	- the user of an interactive shell wants to change it
1474	 *	- the shell wants for force a change
1475	 */
1476	if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) &&
1477	    (!(flags & SS_USER) || !Flag(FTALKING)))
1478		return (0);
1479
1480	setexecsig(p, flags & SS_RESTORE_MASK);
1481
1482	/*
1483	 * This is here 'cause there should be a way of clearing
1484	 * shtraps, but don't know if this is a sane way of doing
1485	 * it. At the moment, all users of shtrap are lifetime
1486	 * users (SIGALRM, SIGCHLD, SIGWINCH).
1487	 */
1488	if (!(flags & SS_USER))
1489		p->shtrap = (sig_t)NULL;
1490	if (flags & SS_SHTRAP) {
1491		p->shtrap = f;
1492		f = trapsig;
1493	}
1494
1495	if (p->cursig != f) {
1496		p->cursig = f;
1497		(void)sigemptyset(&sigact.sa_mask);
1498		/* interruptible */
1499		sigact.sa_flags = 0;
1500		sigact.sa_handler = f;
1501		sigaction(p->signal, &sigact, NULL);
1502	}
1503
1504	return (1);
1505}
1506
1507/* control what signal is set to before an exec() */
1508void
1509setexecsig(Trap *p, int restore)
1510{
1511	/* XXX debugging */
1512	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
1513		internal_errorf("setexecsig: unset signal %d(%s)",
1514		    p->signal, p->name);
1515
1516	/* restore original value for exec'd kids */
1517	p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
1518	switch (restore & SS_RESTORE_MASK) {
1519	case SS_RESTORE_CURR:
1520		/* leave things as they currently are */
1521		break;
1522	case SS_RESTORE_ORIG:
1523		p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
1524		break;
1525	case SS_RESTORE_DFL:
1526		p->flags |= TF_EXEC_DFL;
1527		break;
1528	case SS_RESTORE_IGN:
1529		p->flags |= TF_EXEC_IGN;
1530		break;
1531	}
1532}
1533