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, 2014
      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.134 2014/06/09 13:25:53 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 		bool anchored = *str == '?' ? (++str, false) : true;
    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, bool 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 | O_BINARY,
    724 	    0600)) < 0)
    725 		return;
    726 
    727 	histfd = savefd(fd);
    728 	if (histfd != fd)
    729 		close(fd);
    730 
    731 	mksh_lockfd(histfd);
    732 
    733 	histfsize = lseek(histfd, (off_t)0, SEEK_END);
    734 	if (histfsize > MKSH_MAXHISTFSIZE || hs == hist_init_restore) {
    735 		/* we ignore too large files but still append to them */
    736 		/* we also don't need to re-read after truncation */
    737 		goto hist_init_tail;
    738 	} else if (histfsize > 2) {
    739 		/* we have some data, check its validity */
    740 		base = (void *)mmap(NULL, (size_t)histfsize, PROT_READ,
    741 		    MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
    742 		if (base == (unsigned char *)MAP_FAILED)
    743 			goto hist_init_fail;
    744 		if (base[0] != HMAGIC1 || base[1] != HMAGIC2) {
    745 			munmap(caddr_cast(base), (size_t)histfsize);
    746 			goto hist_init_fail;
    747 		}
    748 		/* load _all_ data */
    749 		lines = histload(hist_source, base + 2, (size_t)histfsize - 2);
    750 		munmap(caddr_cast(base), (size_t)histfsize);
    751 		/* check if the file needs to be truncated */
    752 		if (lines > histsize && histptr >= history) {
    753 			/* you're fucked up with the current code, trust me */
    754 			char *nhname, **hp;
    755 			struct stat sb;
    756 
    757 			/* create temporary file */
    758 			nhname = shf_smprintf("%s.%d", hname, (int)procpid);
    759 			if ((fd = open(nhname, O_RDWR | O_CREAT | O_TRUNC |
    760 			    O_EXCL | O_BINARY, 0600)) < 0) {
    761 				/* just don't truncate then, meh. */
    762 				goto hist_trunc_dont;
    763 			}
    764 			if (fstat(histfd, &sb) >= 0 &&
    765 			    chown(nhname, sb.st_uid, sb.st_gid)) {
    766 				/* abort the truncation then, meh. */
    767 				goto hist_trunc_abort;
    768 			}
    769 			/* we definitively want some magic in that file */
    770 			if (write(fd, sprinkle, 2) != 2)
    771 				goto hist_trunc_abort;
    772 			/* and of course the entries */
    773 			hp = history;
    774 			while (hp < histptr) {
    775 				if (!writehistline(fd,
    776 				    s->line - (histptr - hp), *hp))
    777 					goto hist_trunc_abort;
    778 				++hp;
    779 			}
    780 			/* now unlock, close both, rename, rinse, repeat */
    781 			close(fd);
    782 			fd = -1;
    783 			hist_finish();
    784 			if (rename(nhname, hname) < 0) {
    785  hist_trunc_abort:
    786 				if (fd != -1)
    787 					close(fd);
    788 				unlink(nhname);
    789 				if (fd != -1)
    790 					goto hist_trunc_dont;
    791 				/* darn! restore histfd and pray */
    792 			}
    793 			hs = hist_init_restore;
    794  hist_trunc_dont:
    795 			afree(nhname, ATEMP);
    796 			if (hs == hist_init_restore)
    797 				goto retry;
    798 		}
    799 	} else if (histfsize != 0) {
    800 		/* negative or too small... */
    801  hist_init_fail:
    802 		/* ... or mmap failed or illegal */
    803 		hist_finish();
    804 		/* nuke the bogus file then retry, at most once */
    805 		if (!unlink(hname) && hs != hist_init_retry) {
    806 			hs = hist_init_retry;
    807 			goto retry;
    808 		}
    809 		if (hs != hist_init_retry)
    810 			bi_errorf("can't %s %s: %s",
    811 			    "unlink HISTFILE", hname, cstrerror(errno));
    812 		histfsize = 0;
    813 		return;
    814 	} else {
    815 		/* size 0, add magic to the history file */
    816 		if (write(histfd, sprinkle, 2) != 2) {
    817 			hist_finish();
    818 			return;
    819 		}
    820 	}
    821 	histfsize = lseek(histfd, (off_t)0, SEEK_END);
    822  hist_init_tail:
    823 	mksh_unlkfd(histfd);
    824 #endif
    825 }
    826 
    827 #if HAVE_PERSISTENT_HISTORY
    828 /*
    829  * load the history structure from the stored data
    830  */
    831 static int
    832 histload(Source *s, unsigned char *base, size_t bytes)
    833 {
    834 	int lno = 0, lines = 0;
    835 	unsigned char *cp;
    836 
    837  histload_loop:
    838 	/* !bytes check as some systems (older FreeBSDs) have buggy memchr */
    839 	if (!bytes || (cp = memchr(base, COMMAND, bytes)) == NULL)
    840 		return (lines);
    841 	/* advance base pointer past COMMAND byte */
    842 	bytes -= ++cp - base;
    843 	base = cp;
    844 	/* if there is no full string left, don't bother with the rest */
    845 	if (bytes < 5 || (cp = memchr(base + 4, '\0', bytes - 4)) == NULL)
    846 		return (lines);
    847 	/* load the stored line number */
    848 	lno = ((base[0] & 0xFF) << 24) | ((base[1] & 0xFF) << 16) |
    849 	    ((base[2] & 0xFF) << 8) | (base[3] & 0xFF);
    850 	/* store away the found line (@base[4]) */
    851 	++lines;
    852 	if (histptr >= history && lno - 1 != s->line) {
    853 		/* a replacement? */
    854 		char **hp;
    855 
    856 		if (lno >= s->line - (histptr - history) && lno <= s->line) {
    857 			hp = &histptr[lno - s->line];
    858 			if (*hp)
    859 				afree(*hp, APERM);
    860 			strdupx(*hp, (char *)(base + 4), APERM);
    861 		}
    862 	} else {
    863 		s->line = lno--;
    864 		histsave(&lno, (char *)(base + 4), false, false);
    865 	}
    866 	/* advance base pointer past NUL */
    867 	bytes -= ++cp - base;
    868 	base = cp;
    869 	/* repeat until no more */
    870 	goto histload_loop;
    871 }
    872 
    873 /*
    874  * write a command to the end of the history file
    875  *
    876  * This *MAY* seem easy but it's also necessary to check
    877  * that the history file has not changed in size.
    878  * If it has - then some other shell has written to it and
    879  * we should (re)read those commands to update our history
    880  */
    881 static void
    882 writehistfile(int lno, const char *cmd)
    883 {
    884 	off_t sizenow;
    885 	size_t bytes;
    886 	unsigned char *base, *news;
    887 
    888 	mksh_lockfd(histfd);
    889 	sizenow = lseek(histfd, (off_t)0, SEEK_END);
    890 	if (sizenow < histfsize) {
    891 		/* the file has shrunk; give up */
    892 		goto bad;
    893 	}
    894 	if (
    895 		/* ignore changes when the file is too large */
    896 		sizenow <= MKSH_MAXHISTFSIZE
    897 	    &&
    898 		/* the size has changed, we need to do read updates */
    899 		sizenow > histfsize
    900 	    ) {
    901 		/* both sizenow and histfsize are <= MKSH_MAXHISTFSIZE */
    902 		bytes = (size_t)(sizenow - histfsize);
    903 		base = (void *)mmap(NULL, (size_t)sizenow, PROT_READ,
    904 		    MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
    905 		if (base == (unsigned char *)MAP_FAILED)
    906 			goto bad;
    907 		news = base + (size_t)histfsize;
    908 		if (*news == COMMAND) {
    909 			hist_source->line--;
    910 			histload(hist_source, news, bytes);
    911 			hist_source->line++;
    912 			lno = hist_source->line;
    913 		} else
    914 			bytes = 0;
    915 		munmap(caddr_cast(base), (size_t)sizenow);
    916 		if (!bytes)
    917 			goto bad;
    918 	}
    919 	if (cmd && !writehistline(histfd, lno, cmd)) {
    920  bad:
    921 		hist_finish();
    922 		return;
    923 	}
    924 	histfsize = lseek(histfd, (off_t)0, SEEK_END);
    925 	mksh_unlkfd(histfd);
    926 }
    927 
    928 static int
    929 writehistline(int fd, int lno, const char *cmd)
    930 {
    931 	ssize_t n;
    932 	unsigned char hdr[5];
    933 
    934 	hdr[0] = COMMAND;
    935 	hdr[1] = (lno >> 24) & 0xFF;
    936 	hdr[2] = (lno >> 16) & 0xFF;
    937 	hdr[3] = (lno >> 8) & 0xFF;
    938 	hdr[4] = lno & 0xFF;
    939 	n = strlen(cmd) + 1;
    940 	return (write(fd, hdr, 5) == 5 && write(fd, cmd, n) == n);
    941 }
    942 
    943 void
    944 hist_finish(void)
    945 {
    946 	if (histfd >= 0) {
    947 		mksh_unlkfd(histfd);
    948 		(void)close(histfd);
    949 	}
    950 	histfd = -1;
    951 }
    952 #endif
    953 
    954 
    955 #if !HAVE_SYS_SIGNAME
    956 static const struct mksh_sigpair {
    957 	const char * const name;
    958 	int nr;
    959 } mksh_sigpairs[] = {
    960 #include "signames.inc"
    961 	{ NULL, 0 }
    962 };
    963 #endif
    964 
    965 #if HAVE_SYS_SIGLIST
    966 #if !HAVE_SYS_SIGLIST_DECL
    967 extern const char * const sys_siglist[];
    968 #endif
    969 #endif
    970 
    971 void
    972 inittraps(void)
    973 {
    974 	int i;
    975 	const char *cs;
    976 
    977 	trap_exstat = -1;
    978 
    979 	/* Populate sigtraps based on sys_signame and sys_siglist. */
    980 	/*XXX this is idiotic, use a multi-key/value hashtable! */
    981 	for (i = 0; i <= NSIG; i++) {
    982 		sigtraps[i].signal = i;
    983 		if (i == ksh_SIGERR) {
    984 			sigtraps[i].name = "ERR";
    985 			sigtraps[i].mess = "Error handler";
    986 		} else {
    987 #if HAVE_SYS_SIGNAME
    988 			cs = sys_signame[i];
    989 #else
    990 			const struct mksh_sigpair *pair = mksh_sigpairs;
    991 			while ((pair->nr != i) && (pair->name != NULL))
    992 				++pair;
    993 			cs = pair->name;
    994 #endif
    995 			if ((cs == NULL) ||
    996 			    (cs[0] == '\0'))
    997 				sigtraps[i].name = shf_smprintf("%d", i);
    998 			else {
    999 				char *s;
   1000 
   1001 				/* this is not optimal, what about SIGSIG1? */
   1002 				if ((cs[0] & 0xDF) == 'S' &&
   1003 				    (cs[1] & 0xDF) == 'I' &&
   1004 				    (cs[2] & 0xDF) == 'G' &&
   1005 				    cs[3] != '\0') {
   1006 					/* skip leading "SIG" */
   1007 					cs += 3;
   1008 				}
   1009 				strdupx(s, cs, APERM);
   1010 				sigtraps[i].name = s;
   1011 				while ((*s = ksh_toupper(*s)))
   1012 					++s;
   1013 			}
   1014 #if HAVE_SYS_SIGLIST
   1015 			sigtraps[i].mess = sys_siglist[i];
   1016 #elif HAVE_STRSIGNAL
   1017 			sigtraps[i].mess = strsignal(i);
   1018 #else
   1019 			sigtraps[i].mess = NULL;
   1020 #endif
   1021 			if ((sigtraps[i].mess == NULL) ||
   1022 			    (sigtraps[i].mess[0] == '\0'))
   1023 				sigtraps[i].mess = shf_smprintf("%s %d",
   1024 				    "Signal", i);
   1025 		}
   1026 	}
   1027 	/* our name for signal 0 */
   1028 	sigtraps[ksh_SIGEXIT].name = "EXIT";
   1029 
   1030 	(void)sigemptyset(&Sigact_ign.sa_mask);
   1031 	Sigact_ign.sa_flags = 0; /* interruptible */
   1032 	Sigact_ign.sa_handler = SIG_IGN;
   1033 
   1034 	sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
   1035 	sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
   1036 	/* SIGTERM is not fatal for interactive */
   1037 	sigtraps[SIGTERM].flags |= TF_DFL_INTR;
   1038 	sigtraps[SIGHUP].flags |= TF_FATAL;
   1039 	sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
   1040 
   1041 	/* these are always caught so we can clean up any temporary files. */
   1042 	setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
   1043 	setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
   1044 	setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
   1045 	setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
   1046 }
   1047 
   1048 static void alarm_catcher(int sig);
   1049 
   1050 void
   1051 alarm_init(void)
   1052 {
   1053 	sigtraps[SIGALRM].flags |= TF_SHELL_USES;
   1054 	setsig(&sigtraps[SIGALRM], alarm_catcher,
   1055 		SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
   1056 }
   1057 
   1058 /* ARGSUSED */
   1059 static void
   1060 alarm_catcher(int sig MKSH_A_UNUSED)
   1061 {
   1062 	/* this runs inside interrupt context, with errno saved */
   1063 
   1064 	if (ksh_tmout_state == TMOUT_READING) {
   1065 		int left = alarm(0);
   1066 
   1067 		if (left == 0) {
   1068 			ksh_tmout_state = TMOUT_LEAVING;
   1069 			intrsig = 1;
   1070 		} else
   1071 			alarm(left);
   1072 	}
   1073 }
   1074 
   1075 Trap *
   1076 gettrap(const char *cs, bool igncase)
   1077 {
   1078 	int i;
   1079 	Trap *p;
   1080 	char *as;
   1081 
   1082 	if (ksh_isdigit(*cs)) {
   1083 		return ((getn(cs, &i) && 0 <= i && i < NSIG) ?
   1084 		    (&sigtraps[i]) : NULL);
   1085 	}
   1086 
   1087 	/* this breaks SIGSIG1, but we do that above anyway */
   1088 	if ((cs[0] & 0xDF) == 'S' &&
   1089 	    (cs[1] & 0xDF) == 'I' &&
   1090 	    (cs[2] & 0xDF) == 'G' &&
   1091 	    cs[3] != '\0') {
   1092 		/* skip leading "SIG" */
   1093 		cs += 3;
   1094 	}
   1095 	if (igncase) {
   1096 		char *s;
   1097 
   1098 		strdupx(as, cs, ATEMP);
   1099 		cs = s = as;
   1100 		while ((*s = ksh_toupper(*s)))
   1101 			++s;
   1102 	} else
   1103 		as = NULL;
   1104 
   1105 	p = sigtraps;
   1106 	for (i = 0; i <= NSIG; i++) {
   1107 		if (!strcmp(p->name, cs))
   1108 			goto found;
   1109 		++p;
   1110 	}
   1111 	p = NULL;
   1112  found:
   1113 	afree(as, ATEMP);
   1114 	return (p);
   1115 }
   1116 
   1117 /*
   1118  * trap signal handler
   1119  */
   1120 void
   1121 trapsig(int i)
   1122 {
   1123 	Trap *p = &sigtraps[i];
   1124 	int eno = errno;
   1125 
   1126 	trap = p->set = 1;
   1127 	if (p->flags & TF_DFL_INTR)
   1128 		intrsig = 1;
   1129 	if ((p->flags & TF_FATAL) && !p->trap) {
   1130 		fatal_trap = 1;
   1131 		intrsig = 1;
   1132 	}
   1133 	if (p->shtrap)
   1134 		(*p->shtrap)(i);
   1135 	errno = eno;
   1136 }
   1137 
   1138 /*
   1139  * called when we want to allow the user to ^C out of something - won't
   1140  * work if user has trapped SIGINT.
   1141  */
   1142 void
   1143 intrcheck(void)
   1144 {
   1145 	if (intrsig)
   1146 		runtraps(TF_DFL_INTR|TF_FATAL);
   1147 }
   1148 
   1149 /*
   1150  * called after EINTR to check if a signal with normally causes process
   1151  * termination has been received.
   1152  */
   1153 int
   1154 fatal_trap_check(void)
   1155 {
   1156 	int i;
   1157 	Trap *p;
   1158 
   1159 	/* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
   1160 	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
   1161 		if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
   1162 			/* return value is used as an exit code */
   1163 			return (128 + p->signal);
   1164 	return (0);
   1165 }
   1166 
   1167 /*
   1168  * Returns the signal number of any pending traps: ie, a signal which has
   1169  * occurred for which a trap has been set or for which the TF_DFL_INTR flag
   1170  * is set.
   1171  */
   1172 int
   1173 trap_pending(void)
   1174 {
   1175 	int i;
   1176 	Trap *p;
   1177 
   1178 	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
   1179 		if (p->set && ((p->trap && p->trap[0]) ||
   1180 		    ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap)))
   1181 			return (p->signal);
   1182 	return (0);
   1183 }
   1184 
   1185 /*
   1186  * run any pending traps. If intr is set, only run traps that
   1187  * can interrupt commands.
   1188  */
   1189 void
   1190 runtraps(int flag)
   1191 {
   1192 	int i;
   1193 	Trap *p;
   1194 
   1195 	if (ksh_tmout_state == TMOUT_LEAVING) {
   1196 		ksh_tmout_state = TMOUT_EXECUTING;
   1197 		warningf(false, "timed out waiting for input");
   1198 		unwind(LEXIT);
   1199 	} else
   1200 		/*
   1201 		 * XXX: this means the alarm will have no effect if a trap
   1202 		 * is caught after the alarm() was started...not good.
   1203 		 */
   1204 		ksh_tmout_state = TMOUT_EXECUTING;
   1205 	if (!flag)
   1206 		trap = 0;
   1207 	if (flag & TF_DFL_INTR)
   1208 		intrsig = 0;
   1209 	if (flag & TF_FATAL)
   1210 		fatal_trap = 0;
   1211 	++trap_nested;
   1212 	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
   1213 		if (p->set && (!flag ||
   1214 		    ((p->flags & flag) && p->trap == NULL)))
   1215 			runtrap(p, false);
   1216 	if (!--trap_nested)
   1217 		runtrap(NULL, true);
   1218 }
   1219 
   1220 void
   1221 runtrap(Trap *p, bool is_last)
   1222 {
   1223 	int old_changed = 0, i;
   1224 	char *trapstr;
   1225 
   1226 	if (p == NULL)
   1227 		/* just clean up, see runtraps() above */
   1228 		goto donetrap;
   1229 	i = p->signal;
   1230 	trapstr = p->trap;
   1231 	p->set = 0;
   1232 	if (trapstr == NULL) {
   1233 		/* SIG_DFL */
   1234 		if (p->flags & TF_FATAL) {
   1235 			/* eg, SIGHUP */
   1236 			exstat = (int)ksh_min(128U + (unsigned)i, 255U);
   1237 			unwind(LLEAVE);
   1238 		}
   1239 		if (p->flags & TF_DFL_INTR) {
   1240 			/* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
   1241 			exstat = (int)ksh_min(128U + (unsigned)i, 255U);
   1242 			unwind(LINTR);
   1243 		}
   1244 		goto donetrap;
   1245 	}
   1246 	if (trapstr[0] == '\0')
   1247 		/* SIG_IGN */
   1248 		goto donetrap;
   1249 	if (i == ksh_SIGEXIT || i == ksh_SIGERR) {
   1250 		/* avoid recursion on these */
   1251 		old_changed = p->flags & TF_CHANGED;
   1252 		p->flags &= ~TF_CHANGED;
   1253 		p->trap = NULL;
   1254 	}
   1255 	if (trap_exstat == -1)
   1256 		trap_exstat = exstat & 0xFF;
   1257 	/*
   1258 	 * Note: trapstr is fully parsed before anything is executed, thus
   1259 	 * no problem with afree(p->trap) in settrap() while still in use.
   1260 	 */
   1261 	command(trapstr, current_lineno);
   1262 	if (i == ksh_SIGEXIT || i == ksh_SIGERR) {
   1263 		if (p->flags & TF_CHANGED)
   1264 			/* don't clear TF_CHANGED */
   1265 			afree(trapstr, APERM);
   1266 		else
   1267 			p->trap = trapstr;
   1268 		p->flags |= old_changed;
   1269 	}
   1270 
   1271  donetrap:
   1272 	/* we're the last trap of a sequence executed */
   1273 	if (is_last && trap_exstat != -1) {
   1274 		exstat = trap_exstat;
   1275 		trap_exstat = -1;
   1276 	}
   1277 }
   1278 
   1279 /* clear pending traps and reset user's trap handlers; used after fork(2) */
   1280 void
   1281 cleartraps(void)
   1282 {
   1283 	int i;
   1284 	Trap *p;
   1285 
   1286 	trap = 0;
   1287 	intrsig = 0;
   1288 	fatal_trap = 0;
   1289 	for (i = NSIG+1, p = sigtraps; --i >= 0; p++) {
   1290 		p->set = 0;
   1291 		if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
   1292 			settrap(p, NULL);
   1293 	}
   1294 }
   1295 
   1296 /* restore signals just before an exec(2) */
   1297 void
   1298 restoresigs(void)
   1299 {
   1300 	int i;
   1301 	Trap *p;
   1302 
   1303 	for (i = NSIG+1, p = sigtraps; --i >= 0; p++)
   1304 		if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
   1305 			setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
   1306 			    SS_RESTORE_CURR|SS_FORCE);
   1307 }
   1308 
   1309 void
   1310 settrap(Trap *p, const char *s)
   1311 {
   1312 	sig_t f;
   1313 
   1314 	if (p->trap)
   1315 		afree(p->trap, APERM);
   1316 	/* handles s == NULL */
   1317 	strdupx(p->trap, s, APERM);
   1318 	p->flags |= TF_CHANGED;
   1319 	f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
   1320 
   1321 	p->flags |= TF_USER_SET;
   1322 	if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
   1323 		f = trapsig;
   1324 	else if (p->flags & TF_SHELL_USES) {
   1325 		if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
   1326 			/* do what user wants at exec time */
   1327 			p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
   1328 			if (f == SIG_IGN)
   1329 				p->flags |= TF_EXEC_IGN;
   1330 			else
   1331 				p->flags |= TF_EXEC_DFL;
   1332 		}
   1333 
   1334 		/*
   1335 		 * assumes handler already set to what shell wants it
   1336 		 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
   1337 		 */
   1338 		return;
   1339 	}
   1340 
   1341 	/* todo: should we let user know signal is ignored? how? */
   1342 	setsig(p, f, SS_RESTORE_CURR|SS_USER);
   1343 }
   1344 
   1345 /*
   1346  * Called by c_print() when writing to a co-process to ensure SIGPIPE won't
   1347  * kill shell (unless user catches it and exits)
   1348  */
   1349 int
   1350 block_pipe(void)
   1351 {
   1352 	int restore_dfl = 0;
   1353 	Trap *p = &sigtraps[SIGPIPE];
   1354 
   1355 	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
   1356 		setsig(p, SIG_IGN, SS_RESTORE_CURR);
   1357 		if (p->flags & TF_ORIG_DFL)
   1358 			restore_dfl = 1;
   1359 	} else if (p->cursig == SIG_DFL) {
   1360 		setsig(p, SIG_IGN, SS_RESTORE_CURR);
   1361 		/* restore to SIG_DFL */
   1362 		restore_dfl = 1;
   1363 	}
   1364 	return (restore_dfl);
   1365 }
   1366 
   1367 /* Called by c_print() to undo whatever block_pipe() did */
   1368 void
   1369 restore_pipe(int restore_dfl)
   1370 {
   1371 	if (restore_dfl)
   1372 		setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
   1373 }
   1374 
   1375 /*
   1376  * Set action for a signal. Action may not be set if original
   1377  * action was SIG_IGN, depending on the value of flags and FTALKING.
   1378  */
   1379 int
   1380 setsig(Trap *p, sig_t f, int flags)
   1381 {
   1382 	struct sigaction sigact;
   1383 
   1384 	if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR)
   1385 		return (1);
   1386 
   1387 	memset(&sigact, 0, sizeof(sigact));
   1388 
   1389 	/*
   1390 	 * First time setting this signal? If so, get and note the current
   1391 	 * setting.
   1392 	 */
   1393 	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
   1394 		sigaction(p->signal, &Sigact_ign, &sigact);
   1395 		p->flags |= sigact.sa_handler == SIG_IGN ?
   1396 		    TF_ORIG_IGN : TF_ORIG_DFL;
   1397 		p->cursig = SIG_IGN;
   1398 	}
   1399 
   1400 	/*-
   1401 	 * Generally, an ignored signal stays ignored, except if
   1402 	 *	- the user of an interactive shell wants to change it
   1403 	 *	- the shell wants for force a change
   1404 	 */
   1405 	if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) &&
   1406 	    (!(flags & SS_USER) || !Flag(FTALKING)))
   1407 		return (0);
   1408 
   1409 	setexecsig(p, flags & SS_RESTORE_MASK);
   1410 
   1411 	/*
   1412 	 * This is here 'cause there should be a way of clearing
   1413 	 * shtraps, but don't know if this is a sane way of doing
   1414 	 * it. At the moment, all users of shtrap are lifetime
   1415 	 * users (SIGALRM, SIGCHLD, SIGWINCH).
   1416 	 */
   1417 	if (!(flags & SS_USER))
   1418 		p->shtrap = (sig_t)NULL;
   1419 	if (flags & SS_SHTRAP) {
   1420 		p->shtrap = f;
   1421 		f = trapsig;
   1422 	}
   1423 
   1424 	if (p->cursig != f) {
   1425 		p->cursig = f;
   1426 		(void)sigemptyset(&sigact.sa_mask);
   1427 		/* interruptible */
   1428 		sigact.sa_flags = 0;
   1429 		sigact.sa_handler = f;
   1430 		sigaction(p->signal, &sigact, NULL);
   1431 	}
   1432 
   1433 	return (1);
   1434 }
   1435 
   1436 /* control what signal is set to before an exec() */
   1437 void
   1438 setexecsig(Trap *p, int restore)
   1439 {
   1440 	/* XXX debugging */
   1441 	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
   1442 		internal_errorf("setexecsig: unset signal %d(%s)",
   1443 		    p->signal, p->name);
   1444 
   1445 	/* restore original value for exec'd kids */
   1446 	p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
   1447 	switch (restore & SS_RESTORE_MASK) {
   1448 	case SS_RESTORE_CURR:
   1449 		/* leave things as they currently are */
   1450 		break;
   1451 	case SS_RESTORE_ORIG:
   1452 		p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
   1453 		break;
   1454 	case SS_RESTORE_DFL:
   1455 		p->flags |= TF_EXEC_DFL;
   1456 		break;
   1457 	case SS_RESTORE_IGN:
   1458 		p->flags |= TF_EXEC_IGN;
   1459 		break;
   1460 	}
   1461 }
   1462 
   1463 #if HAVE_PERSISTENT_HISTORY || defined(DF)
   1464 /*
   1465  * File descriptor locking and unlocking functions.
   1466  * Could use some error handling, but hey, this is only
   1467  * advisory locking anyway, will often not work over NFS,
   1468  * and you are SOL if this fails...
   1469  */
   1470 
   1471 void
   1472 mksh_lockfd(int fd)
   1473 {
   1474 #if defined(__OpenBSD__)
   1475 	/* flock is not interrupted by signals */
   1476 	(void)flock(fd, LOCK_EX);
   1477 #elif HAVE_FLOCK
   1478 	int rv;
   1479 
   1480 	/* e.g. on Linux */
   1481 	do {
   1482 		rv = flock(fd, LOCK_EX);
   1483 	} while (rv == 1 && errno == EINTR);
   1484 #elif HAVE_LOCK_FCNTL
   1485 	int rv;
   1486 	struct flock lks;
   1487 
   1488 	memset(&lks, 0, sizeof(lks));
   1489 	lks.l_type = F_WRLCK;
   1490 	do {
   1491 		rv = fcntl(fd, F_SETLKW, &lks);
   1492 	} while (rv == 1 && errno == EINTR);
   1493 #endif
   1494 }
   1495 
   1496 /* designed to not define mksh_unlkfd if none triggered */
   1497 #if HAVE_FLOCK
   1498 void
   1499 mksh_unlkfd(int fd)
   1500 {
   1501 	(void)flock(fd, LOCK_UN);
   1502 }
   1503 #elif HAVE_LOCK_FCNTL
   1504 void
   1505 mksh_unlkfd(int fd)
   1506 {
   1507 	struct flock lks;
   1508 
   1509 	memset(&lks, 0, sizeof(lks));
   1510 	lks.l_type = F_UNLCK;
   1511 	(void)fcntl(fd, F_SETLKW, &lks);
   1512 }
   1513 #endif
   1514 #endif
   1515