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