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