Home | History | Annotate | Download | only in src
      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 (at) 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 
     39 Trap sigtraps[NSIG + 1];
     40 static struct sigaction Sigact_ign;
     41 
     42 #if HAVE_PERSISTENT_HISTORY
     43 static int hist_count_lines(unsigned char *, int);
     44 static int hist_shrink(unsigned char *, int);
     45 static unsigned char *hist_skip_back(unsigned char *,int *,int);
     46 static void histload(Source *, unsigned char *, int);
     47 static void histinsert(Source *, int, const char *);
     48 static void writehistfile(int, char *);
     49 static int sprinkle(int);
     50 #endif
     51 
     52 static int hist_execute(char *);
     53 static int hist_replace(char **, const char *, const char *, bool);
     54 static char **hist_get(const char *, bool, bool);
     55 static char **hist_get_oldest(void);
     56 static void histbackup(void);
     57 
     58 static char **current;		/* current position in history[] */
     59 static int hstarted;		/* set after hist_init() called */
     60 static Source *hist_source;
     61 
     62 #if HAVE_PERSISTENT_HISTORY
     63 /* current history file: name, fd, size */
     64 static char *hname;
     65 static int histfd;
     66 static size_t hsize;
     67 #endif
     68 
     69 static const char Tnot_in_history[] = "not in history";
     70 #define Thistory (Tnot_in_history + 7)
     71 
     72 int
     73 c_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) */
    319 static int
    320 hist_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 
    359 static int
    360 hist_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  */
    405 static char **
    406 hist_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 */
    444 char **
    445 hist_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 */
    455 static char **
    456 hist_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  */
    468 static void
    469 histbackup(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  */
    484 char **
    485 histpos(void)
    486 {
    487 	return (current);
    488 }
    489 
    490 int
    491 histnum(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  */
    509 int
    510 findhist(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  */
    533 void
    534 sethistsize(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  */
    558 void
    559 sethistfile(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  */
    591 void
    592 init_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 */
    612 bool
    613 histsync(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  */
    635 void
    636 histsave(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 
    697 void
    698 hist_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
    789 typedef 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 
    796 static int
    797 hist_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  */
    830 static int
    831 hist_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  */
    880 static unsigned char *
    881 hist_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  */
    907 static void
    908 histload(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  */
    957 static void
    958 histinsert(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  */
    977 static void
    978 writehistfile(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 
   1039 void
   1040 hist_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  */
   1050 static int
   1051 sprinkle(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
   1060 static 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 
   1069 void
   1070 inittraps(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 
   1139 static void alarm_catcher(int sig);
   1140 
   1141 void
   1142 alarm_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 */
   1150 static void
   1151 alarm_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 
   1166 Trap *
   1167 gettrap(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  */
   1193 void
   1194 trapsig(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  */
   1215 void
   1216 intrcheck(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  */
   1226 int
   1227 fatal_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  */
   1245 int
   1246 trap_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  */
   1262 void
   1263 runtraps(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 
   1293 void
   1294 runtrap(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) */
   1353 void
   1354 cleartraps(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) */
   1370 void
   1371 restoresigs(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 
   1382 void
   1383 settrap(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  */
   1422 int
   1423 block_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 */
   1441 void
   1442 restore_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  */
   1452 int
   1453 setsig(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() */
   1508 void
   1509 setexecsig(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