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