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