• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*	$OpenBSD: main.c,v 1.47 2011/09/07 11:33:25 otto Exp $	*/
2 /*	$OpenBSD: tty.c,v 1.9 2006/03/14 22:08:01 deraadt Exp $	*/
3 /*	$OpenBSD: io.c,v 1.22 2006/03/17 16:30:13 millert Exp $	*/
4 /*	$OpenBSD: table.c,v 1.13 2009/01/17 22:06:44 millert Exp $	*/
5 
6 /*-
7  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
8  *	Thorsten Glaser <tg@mirbsd.org>
9  *
10  * Provided that these terms and disclaimer and all copyright notices
11  * are retained or reproduced in an accompanying document, permission
12  * is granted to deal in this work without restriction, including un-
13  * limited rights to use, publicly perform, distribute, sell, modify,
14  * merge, give away, or sublicence.
15  *
16  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
17  * the utmost extent permitted by applicable law, neither express nor
18  * implied; without malicious intent or gross negligence. In no event
19  * may a licensor, author or contributor be held liable for indirect,
20  * direct, other damage, loss, or other issues arising in any way out
21  * of dealing in the work, even if advised of the possibility of such
22  * damage or existence of a defect, except proven that it results out
23  * of said person's immediate fault when using the work as intended.
24  */
25 
26 #define EXTERN
27 #include "sh.h"
28 
29 #if HAVE_LANGINFO_CODESET
30 #include <langinfo.h>
31 #endif
32 #if HAVE_SETLOCALE_CTYPE
33 #include <locale.h>
34 #endif
35 
36 __RCSID("$MirOS: src/bin/mksh/main.c,v 1.200 2011/10/07 19:51:28 tg Exp $");
37 
38 extern char **environ;
39 
40 #ifndef MKSHRC_PATH
41 #define MKSHRC_PATH	"~/.mkshrc"
42 #endif
43 
44 #ifndef MKSH_DEFAULT_TMPDIR
45 #define MKSH_DEFAULT_TMPDIR	"/tmp"
46 #endif
47 
48 void chvt_reinit(void);
49 static void reclaim(void);
50 static void remove_temps(struct temp *);
51 static mksh_uari_t rndsetup(void);
52 #ifdef SIGWINCH
53 static void x_sigwinch(int);
54 #endif
55 
56 static const char initifs[] = "IFS= \t\n";
57 
58 static const char initsubs[] =
59     "${PS2=> } ${PS3=#? } ${PS4=+ } ${SECONDS=0} ${TMOUT=0}";
60 
61 static const char *initcoms[] = {
62 	Ttypeset, "-r", initvsn, NULL,
63 	Ttypeset, "-x", "HOME", "PATH", "RANDOM", "SHELL", NULL,
64 	Ttypeset, "-i10", "SECONDS", "TMOUT", NULL,
65 	Talias,
66 	"integer=typeset -i",
67 	Tlocal_typeset,
68 	/* not "alias -t --": hash -r needs to work */
69 	"hash=alias -t",
70 	"type=whence -v",
71 #if !defined(ANDROID) && !defined(MKSH_UNEMPLOYED)
72 	/* not in Android for political reasons */
73 	/* not in ARGE mksh due to no job control */
74 	"stop=kill -STOP",
75 	"suspend=kill -STOP $$",
76 #endif
77 	"autoload=typeset -fu",
78 	"functions=typeset -f",
79 	"history=fc -l",
80 	"nameref=typeset -n",
81 	"nohup=nohup ",
82 	Tr_fc_e_dash,
83 	"source=PATH=$PATH:. command .",
84 	"login=exec login",
85 	NULL,
86 	 /* this is what AT&T ksh seems to track, with the addition of emacs */
87 	Talias, "-tU",
88 	"cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
89 	"make", "mv", "pr", "rm", "sed", "sh", "vi", "who", NULL,
90 	NULL
91 };
92 
93 static const char *restr_com[] = {
94 	Ttypeset, "-r", "PATH", "ENV", "SHELL", NULL
95 };
96 
97 static int initio_done;
98 
99 /* top-level parsing and execution environment */
100 static struct env env;
101 struct env *e = &env;
102 
103 static mksh_uari_t
rndsetup(void)104 rndsetup(void)
105 {
106 	register uint32_t h;
107 	struct {
108 		ALLOC_ITEM alloc_INT;
109 		void *dataptr, *stkptr, *mallocptr;
110 		sigjmp_buf jbuf;
111 		struct timeval tv;
112 		struct timezone tz;
113 	} *bufptr;
114 	char *cp;
115 
116 	cp = alloc(sizeof(*bufptr) - ALLOC_SIZE, APERM);
117 #ifdef DEBUG
118 	/* clear the allocated space, for valgrind */
119 	memset(cp, 0, sizeof(*bufptr) - ALLOC_SIZE);
120 #endif
121 	/* undo what alloc() did to the malloc result address */
122 	bufptr = (void *)(cp - ALLOC_SIZE);
123 	/* PIE or something similar provides us with deltas here */
124 	bufptr->dataptr = &rndsetupstate;
125 	/* ASLR in at least Windows, Linux, some BSDs */
126 	bufptr->stkptr = &bufptr;
127 	/* randomised malloc in BSD (and possibly others) */
128 	bufptr->mallocptr = bufptr;
129 	/* glibc pointer guard */
130 	sigsetjmp(bufptr->jbuf, 1);
131 	/* introduce variation */
132 	gettimeofday(&bufptr->tv, &bufptr->tz);
133 
134 	NZATInit(h);
135 	/* variation through pid, ppid, and the works */
136 	NZATUpdateMem(h, &rndsetupstate, sizeof(rndsetupstate));
137 	/* some variation, some possibly entropy, depending on OE */
138 	NZATUpdateMem(h, bufptr, sizeof(*bufptr));
139 	NZAATFinish(h);
140 
141 	afree(cp, APERM);
142 	return ((mksh_uari_t)h);
143 }
144 
145 void
chvt_reinit(void)146 chvt_reinit(void)
147 {
148 	kshpid = procpid = getpid();
149 	ksheuid = geteuid();
150 	kshpgrp = getpgrp();
151 	kshppid = getppid();
152 }
153 
154 static const char *empty_argv[] = {
155 	"mksh", NULL
156 };
157 
158 int
main(int argc,const char * argv[])159 main(int argc, const char *argv[])
160 {
161 	int argi, i;
162 	Source *s = NULL;
163 	struct block *l;
164 	unsigned char restricted, errexit, utf_flag;
165 	char *cp;
166 	const char *ccp, **wp;
167 	struct tbl *vp;
168 	struct stat s_stdin;
169 #if !defined(_PATH_DEFPATH) && defined(_CS_PATH)
170 	ssize_t k;
171 #endif
172 
173 	/* do things like getpgrp() et al. */
174 	chvt_reinit();
175 
176 	/* make sure argv[] is sane */
177 	if (!*argv) {
178 		argv = empty_argv;
179 		argc = 1;
180 	}
181 	kshname = argv[0];
182 
183 	/* initialise permanent Area */
184 	ainit(&aperm);
185 
186 	/* set up base environment */
187 	env.type = E_NONE;
188 	ainit(&env.area);
189 	/* set up global l->vars and l->funs */
190 	newblock();
191 
192 	/* Do this first so output routines (eg, errorf, shellf) can work */
193 	initio();
194 
195 	/* determine the basename (without '-' or path) of the executable */
196 	ccp = kshname;
197 	goto begin_parse_kshname;
198 	while ((i = ccp[argi++])) {
199 		if (i == '/') {
200 			ccp += argi;
201  begin_parse_kshname:
202 			argi = 0;
203 			if (*ccp == '-')
204 				++ccp;
205 		}
206 	}
207 	if (!*ccp)
208 		ccp = empty_argv[0];
209 
210 	/* define built-in commands and see if we were called as one */
211 	ktinit(APERM, &builtins,
212 	    /* currently 50 builtins -> 80% of 64 (2^6) */
213 	    6);
214 	for (i = 0; mkshbuiltins[i].name != NULL; i++)
215 		if (!strcmp(ccp, builtin(mkshbuiltins[i].name,
216 		    mkshbuiltins[i].func)))
217 			Flag(FAS_BUILTIN) = 1;
218 
219 	if (!Flag(FAS_BUILTIN)) {
220 		/* check for -T option early */
221 		argi = parse_args(argv, OF_FIRSTTIME, NULL);
222 		if (argi < 0)
223 			return (1);
224 
225 #ifdef MKSH_BINSHREDUCED
226 		/* set FSH if we're called as -sh or /bin/sh or so */
227 		if (!strcmp(ccp, "sh"))
228 			change_flag(FSH, OF_FIRSTTIME, 1);
229 #endif
230 	}
231 
232 	initvar();
233 
234 	initctypes();
235 
236 	inittraps();
237 
238 	coproc_init();
239 
240 	/* set up variable and command dictionaries */
241 	ktinit(APERM, &taliases, 0);
242 	ktinit(APERM, &aliases, 0);
243 #ifndef MKSH_NOPWNAM
244 	ktinit(APERM, &homedirs, 0);
245 #endif
246 
247 	/* define shell keywords */
248 	initkeywords();
249 
250 	init_histvec();
251 
252 #ifdef _PATH_DEFPATH
253 	def_path = _PATH_DEFPATH;
254 #else
255 #ifdef _CS_PATH
256 	if ((k = confstr(_CS_PATH, NULL, 0)) > 0 &&
257 	    confstr(_CS_PATH, cp = alloc(k + 1, APERM), k + 1) == k + 1)
258 		def_path = cp;
259 	else
260 #endif
261 		/*
262 		 * this is uniform across all OSes unless it
263 		 * breaks somewhere; don't try to optimise,
264 		 * e.g. add stuff for Interix or remove /usr
265 		 * for HURD, because e.g. Debian GNU/HURD is
266 		 * "keeping a regular /usr"; this is supposed
267 		 * to be a sane 'basic' default PATH
268 		 */
269 		def_path = "/bin:/usr/bin:/sbin:/usr/sbin";
270 #endif
271 
272 	/*
273 	 * Set PATH to def_path (will set the path global variable).
274 	 * (import of environment below will probably change this setting).
275 	 */
276 	vp = global("PATH");
277 	/* setstr can't fail here */
278 	setstr(vp, def_path, KSH_RETURN_ERROR);
279 
280 	/*
281 	 * Turn on nohup by default for now - will change to off
282 	 * by default once people are aware of its existence
283 	 * (AT&T ksh does not have a nohup option - it always sends
284 	 * the hup).
285 	 */
286 	Flag(FNOHUP) = 1;
287 
288 	/*
289 	 * Turn on brace expansion by default. AT&T kshs that have
290 	 * alternation always have it on.
291 	 */
292 	Flag(FBRACEEXPAND) = 1;
293 
294 	/*
295 	 * Set edit mode to emacs by default, may be overridden
296 	 * by the environment or the user. Also, we want tab completion
297 	 * on in vi by default.
298 	 */
299 	change_flag(FEMACS, OF_SPECIAL, 1);
300 #if !MKSH_S_NOVI
301 	Flag(FVITABCOMPLETE) = 1;
302 #endif
303 
304 	/* import environment */
305 	if (environ != NULL)
306 		for (wp = (const char **)environ; *wp != NULL; wp++)
307 			typeset(*wp, IMPORT | EXPORT, 0, 0, 0);
308 
309 	/* for security */
310 	typeset(initifs, 0, 0, 0, 0);
311 
312 	/* assign default shell variable values */
313 	substitute(initsubs, 0);
314 
315 	/* Figure out the current working directory and set $PWD */
316 	vp = global("PWD");
317 	cp = str_val(vp);
318 	/* Try to use existing $PWD if it is valid */
319 	set_current_wd((cp[0] == '/' && test_eval(NULL, TO_FILEQ, cp, ".",
320 	    true)) ? cp : NULL);
321 	if (current_wd[0])
322 		simplify_path(current_wd);
323 	/* Only set pwd if we know where we are or if it had a bogus value */
324 	if (current_wd[0] || *cp)
325 		/* setstr can't fail here */
326 		setstr(vp, current_wd, KSH_RETURN_ERROR);
327 
328 	for (wp = initcoms; *wp != NULL; wp++) {
329 		shcomexec(wp);
330 		while (*wp != NULL)
331 			wp++;
332 	}
333 	setint_n(global("COLUMNS"), 0);
334 	setint_n(global("LINES"), 0);
335 	setint_n(global("OPTIND"), 1);
336 
337 	kshuid = getuid();
338 	kshgid = getgid();
339 	kshegid = getegid();
340 
341 	safe_prompt = ksheuid ? "$ " : "# ";
342 	vp = global("PS1");
343 	/* Set PS1 if unset or we are root and prompt doesn't contain a # */
344 	if (!(vp->flag & ISSET) ||
345 	    (!ksheuid && !strchr(str_val(vp), '#')))
346 		/* setstr can't fail here */
347 		setstr(vp, safe_prompt, KSH_RETURN_ERROR);
348 	setint_n((vp = global("PGRP")), (mksh_uari_t)kshpgrp);
349 	vp->flag |= INT_U;
350 	setint_n((vp = global("PPID")), (mksh_uari_t)kshppid);
351 	vp->flag |= INT_U;
352 	setint_n((vp = global("USER_ID")), (mksh_uari_t)ksheuid);
353 	vp->flag |= INT_U;
354 	setint_n((vp = global("KSHUID")), (mksh_uari_t)kshuid);
355 	vp->flag |= INT_U;
356 	setint_n((vp = global("KSHEGID")), (mksh_uari_t)kshegid);
357 	vp->flag |= INT_U;
358 	setint_n((vp = global("KSHGID")), (mksh_uari_t)kshgid);
359 	vp->flag |= INT_U;
360 	setint_n((vp = global("RANDOM")), rndsetup());
361 	vp->flag |= INT_U;
362 	setint_n((vp_pipest = global("PIPESTATUS")), 0);
363 
364 	/* Set this before parsing arguments */
365 	Flag(FPRIVILEGED) = kshuid != ksheuid || kshgid != kshegid;
366 
367 	/* this to note if monitor is set on command line (see below) */
368 #ifndef MKSH_UNEMPLOYED
369 	Flag(FMONITOR) = 127;
370 #endif
371 	/* this to note if utf-8 mode is set on command line (see below) */
372 	UTFMODE = 2;
373 
374 	if (!Flag(FAS_BUILTIN)) {
375 		argi = parse_args(argv, OF_CMDLINE, NULL);
376 		if (argi < 0)
377 			return (1);
378 	}
379 
380 #ifdef DEBUG
381 	/* test wraparound of arithmetic types */
382 	{
383 		volatile long xl;
384 		volatile unsigned long xul;
385 		volatile int xi;
386 		volatile unsigned int xui;
387 		volatile mksh_ari_t xa;
388 		volatile mksh_uari_t xua, xua2;
389 		volatile uint8_t xc;
390 
391 		xa = 2147483647;
392 		xua = 2147483647;
393 		++xa;
394 		++xua;
395 		xua2 = xa;
396 		xl = xa;
397 		xul = xua;
398 		xa = 0;
399 		xua = 0;
400 		--xa;
401 		--xua;
402 		xi = xa;
403 		xui = xua;
404 		xa = -1;
405 		xua = xa;
406 		++xa;
407 		++xua;
408 		xc = 0;
409 		--xc;
410 		if ((xua2 != 2147483648UL) ||
411 		    (xl != -2147483648L) || (xul != 2147483648UL) ||
412 		    (xi != -1) || (xui != 4294967295U) ||
413 		    (xa != 0) || (xua != 0) || (xc != 255))
414 			errorf("integer wraparound test failed");
415 	}
416 #endif
417 
418 	/* process this later only, default to off (hysterical raisins) */
419 	utf_flag = UTFMODE;
420 	UTFMODE = 0;
421 
422 	if (Flag(FAS_BUILTIN)) {
423 		/* auto-detect from environment variables, always */
424 		utf_flag = 3;
425 	} else if (Flag(FCOMMAND)) {
426 		s = pushs(SSTRING, ATEMP);
427 		if (!(s->start = s->str = argv[argi++]))
428 			errorf("%s %s", "-c", "requires an argument");
429 #ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
430 		/* compatibility to MidnightBSD 0.1 /bin/sh (kludge) */
431 		if (Flag(FSH) && argv[argi] && !strcmp(argv[argi], "--"))
432 			++argi;
433 #endif
434 		if (argv[argi])
435 			kshname = argv[argi++];
436 	} else if (argi < argc && !Flag(FSTDIN)) {
437 		s = pushs(SFILE, ATEMP);
438 		s->file = argv[argi++];
439 		s->u.shf = shf_open(s->file, O_RDONLY, 0,
440 		    SHF_MAPHI | SHF_CLEXEC);
441 		if (s->u.shf == NULL) {
442 			shl_stdout_ok = false;
443 			warningf(true, "%s: %s", s->file, strerror(errno));
444 			/* mandated by SUSv4 */
445 			exstat = 127;
446 			unwind(LERROR);
447 		}
448 		kshname = s->file;
449 	} else {
450 		Flag(FSTDIN) = 1;
451 		s = pushs(SSTDIN, ATEMP);
452 		s->file = "<stdin>";
453 		s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0),
454 		    NULL);
455 		if (isatty(0) && isatty(2)) {
456 			Flag(FTALKING) = Flag(FTALKING_I) = 1;
457 			/* The following only if isatty(0) */
458 			s->flags |= SF_TTY;
459 			s->u.shf->flags |= SHF_INTERRUPT;
460 			s->file = NULL;
461 		}
462 	}
463 
464 	/* this bizarreness is mandated by POSIX */
465 	if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode) &&
466 	    Flag(FTALKING))
467 		reset_nonblock(0);
468 
469 	/* initialise job control */
470 	j_init();
471 	/* Do this after j_init(), as tty_fd is not initialised until then */
472 	if (Flag(FTALKING)) {
473 		if (utf_flag == 2) {
474 #ifndef MKSH_ASSUME_UTF8
475 			/* auto-detect from locale or environment */
476 			utf_flag = 4;
477 #elif MKSH_ASSUME_UTF8
478 			utf_flag = 1;
479 #else
480 			/* always disable UTF-8 (for interactive) */
481 			utf_flag = 0;
482 #endif
483 		}
484 		x_init();
485 	}
486 
487 #ifdef SIGWINCH
488 	sigtraps[SIGWINCH].flags |= TF_SHELL_USES;
489 	setsig(&sigtraps[SIGWINCH], x_sigwinch,
490 	    SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
491 #endif
492 
493 	l = e->loc;
494 	if (Flag(FAS_BUILTIN)) {
495 		l->argc = argc;
496 		l->argv = argv;
497 		l->argv[0] = ccp;
498 	} else {
499 		l->argc = argc - argi;
500 		l->argv = &argv[argi - 1];
501 		l->argv[0] = kshname;
502 		getopts_reset(1);
503 	}
504 
505 	/* divine the initial state of the utf8-mode Flag */
506 #define isuc(x)	(((x) != NULL) && \
507 	    (stristr((x), "UTF-8") || stristr((x), "utf8")))
508 	ccp = null;
509 	switch (utf_flag) {
510 
511 	/* auto-detect from locale or environment */
512 	case 4:
513 #if HAVE_SETLOCALE_CTYPE
514 		ccp = setlocale(LC_CTYPE, "");
515 #if HAVE_LANGINFO_CODESET
516 		if (!isuc(ccp))
517 			ccp = nl_langinfo(CODESET);
518 #endif
519 		if (!isuc(ccp))
520 			ccp = null;
521 		/* FALLTHROUGH */
522 #endif
523 
524 	/* auto-detect from environment */
525 	case 3:
526 		/* these were imported from environ earlier */
527 		if (ccp == null)
528 			ccp = str_val(global("LC_ALL"));
529 		if (ccp == null)
530 			ccp = str_val(global("LC_CTYPE"));
531 		if (ccp == null)
532 			ccp = str_val(global("LANG"));
533 		UTFMODE = isuc(ccp);
534 		break;
535 
536 	/* not set on command line, not FTALKING */
537 	case 2:
538 	/* unknown values */
539 	default:
540 		utf_flag = 0;
541 		/* FALLTHROUGH */
542 
543 	/* known values */
544 	case 1:
545 	case 0:
546 		UTFMODE = utf_flag;
547 		break;
548 	}
549 #undef isuc
550 
551 	/* Disable during .profile/ENV reading */
552 	restricted = Flag(FRESTRICTED);
553 	Flag(FRESTRICTED) = 0;
554 	errexit = Flag(FERREXIT);
555 	Flag(FERREXIT) = 0;
556 
557 	/*
558 	 * Do this before profile/$ENV so that if it causes problems in them,
559 	 * user will know why things broke.
560 	 */
561 	if (!current_wd[0] && Flag(FTALKING))
562 		warningf(false, "can't determine current directory");
563 
564 	if (Flag(FLOGIN)) {
565 		include(MKSH_SYSTEM_PROFILE, 0, NULL, 1);
566 		if (!Flag(FPRIVILEGED))
567 			include(substitute("$HOME/.profile", 0), 0,
568 			    NULL, 1);
569 	}
570 	if (Flag(FPRIVILEGED))
571 		include(MKSH_SUID_PROFILE, 0, NULL, 1);
572 	else if (Flag(FTALKING)) {
573 		char *env_file;
574 
575 		/* include $ENV */
576 		env_file = substitute(substitute("${ENV:-" MKSHRC_PATH "}", 0),
577 		    DOTILDE);
578 		if (*env_file != '\0')
579 			include(env_file, 0, NULL, 1);
580 	}
581 
582 	if (restricted) {
583 		shcomexec(restr_com);
584 		/* After typeset command... */
585 		Flag(FRESTRICTED) = 1;
586 	}
587 	Flag(FERREXIT) = errexit;
588 
589 	if (Flag(FTALKING))
590 		hist_init(s);
591 	else
592 		/* set after ENV */
593 		Flag(FTRACKALL) = 1;
594 
595 	alarm_init();
596 
597 	if (Flag(FAS_BUILTIN))
598 		return (shcomexec(l->argv));
599 
600 	/* doesn't return */
601 	shell(s, true);
602 	/* NOTREACHED */
603 	return (0);
604 }
605 
606 int
include(const char * name,int argc,const char ** argv,int intr_ok)607 include(const char *name, int argc, const char **argv, int intr_ok)
608 {
609 	Source *volatile s = NULL;
610 	struct shf *shf;
611 	const char **volatile old_argv;
612 	volatile int old_argc;
613 	int i;
614 
615 	shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC);
616 	if (shf == NULL)
617 		return (-1);
618 
619 	if (argv) {
620 		old_argv = e->loc->argv;
621 		old_argc = e->loc->argc;
622 	} else {
623 		old_argv = NULL;
624 		old_argc = 0;
625 	}
626 	newenv(E_INCL);
627 	i = sigsetjmp(e->jbuf, 0);
628 	if (i) {
629 		quitenv(s ? s->u.shf : NULL);
630 		if (old_argv) {
631 			e->loc->argv = old_argv;
632 			e->loc->argc = old_argc;
633 		}
634 		switch (i) {
635 		case LRETURN:
636 		case LERROR:
637 			/* see below */
638 			return (exstat & 0xFF);
639 		case LINTR:
640 			/*
641 			 * intr_ok is set if we are including .profile or $ENV.
642 			 * If user ^Cs out, we don't want to kill the shell...
643 			 */
644 			if (intr_ok && (exstat - 128) != SIGTERM)
645 				return (1);
646 			/* FALLTHROUGH */
647 		case LEXIT:
648 		case LLEAVE:
649 		case LSHELL:
650 			unwind(i);
651 			/* NOTREACHED */
652 		default:
653 			internal_errorf("%s %d", "include", i);
654 			/* NOTREACHED */
655 		}
656 	}
657 	if (argv) {
658 		e->loc->argv = argv;
659 		e->loc->argc = argc;
660 	}
661 	s = pushs(SFILE, ATEMP);
662 	s->u.shf = shf;
663 	strdupx(s->file, name, ATEMP);
664 	i = shell(s, false);
665 	quitenv(s->u.shf);
666 	if (old_argv) {
667 		e->loc->argv = old_argv;
668 		e->loc->argc = old_argc;
669 	}
670 	/* & 0xff to ensure value not -1 */
671 	return (i & 0xFF);
672 }
673 
674 /* spawn a command into a shell optionally keeping track of the line number */
675 int
command(const char * comm,int line)676 command(const char *comm, int line)
677 {
678 	Source *s;
679 
680 	s = pushs(SSTRING, ATEMP);
681 	s->start = s->str = comm;
682 	s->line = line;
683 	return (shell(s, false));
684 }
685 
686 /*
687  * run the commands from the input source, returning status.
688  */
689 int
shell(Source * volatile s,volatile int toplevel)690 shell(Source * volatile s, volatile int toplevel)
691 {
692 	struct op *t;
693 	volatile int wastty = s->flags & SF_TTY;
694 	volatile int attempts = 13;
695 	volatile int interactive = Flag(FTALKING) && toplevel;
696 	volatile bool sfirst = true;
697 	Source *volatile old_source = source;
698 	int i;
699 
700 	newenv(E_PARSE);
701 	if (interactive)
702 		really_exit = 0;
703 	i = sigsetjmp(e->jbuf, 0);
704 	if (i) {
705 		switch (i) {
706 		case LINTR:
707 			/* we get here if SIGINT not caught or ignored */
708 		case LERROR:
709 		case LSHELL:
710 			if (interactive) {
711 				if (i == LINTR)
712 					shellf("\n");
713 				/*
714 				 * Reset any eof that was read as part of a
715 				 * multiline command.
716 				 */
717 				if (Flag(FIGNOREEOF) && s->type == SEOF &&
718 				    wastty)
719 					s->type = SSTDIN;
720 				/*
721 				 * Used by exit command to get back to
722 				 * top level shell. Kind of strange since
723 				 * interactive is set if we are reading from
724 				 * a tty, but to have stopped jobs, one only
725 				 * needs FMONITOR set (not FTALKING/SF_TTY)...
726 				 */
727 				/* toss any input we have so far */
728 				s->start = s->str = null;
729 				break;
730 			}
731 			/* FALLTHROUGH */
732 		case LEXIT:
733 		case LLEAVE:
734 		case LRETURN:
735 			source = old_source;
736 			quitenv(NULL);
737 			/* keep on going */
738 			unwind(i);
739 			/* NOTREACHED */
740 		default:
741 			source = old_source;
742 			quitenv(NULL);
743 			internal_errorf("%s %d", "shell", i);
744 			/* NOTREACHED */
745 		}
746 	}
747 	while (/* CONSTCOND */ 1) {
748 		if (trap)
749 			runtraps(0);
750 
751 		if (s->next == NULL) {
752 			if (Flag(FVERBOSE))
753 				s->flags |= SF_ECHO;
754 			else
755 				s->flags &= ~SF_ECHO;
756 		}
757 		if (interactive) {
758 			j_notify();
759 			set_prompt(PS1, s);
760 		}
761 		t = compile(s, sfirst);
762 		sfirst = false;
763 		if (t != NULL && t->type == TEOF) {
764 			if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
765 				shellf("Use 'exit' to leave mksh\n");
766 				s->type = SSTDIN;
767 			} else if (wastty && !really_exit &&
768 			    j_stopped_running()) {
769 				really_exit = 1;
770 				s->type = SSTDIN;
771 			} else {
772 				/*
773 				 * this for POSIX which says EXIT traps
774 				 * shall be taken in the environment
775 				 * immediately after the last command
776 				 * executed.
777 				 */
778 				if (toplevel)
779 					unwind(LEXIT);
780 				break;
781 			}
782 		}
783 		if (t && (!Flag(FNOEXEC) || (s->flags & SF_TTY)))
784 			exstat = execute(t, 0, NULL);
785 
786 		if (t != NULL && t->type != TEOF && interactive && really_exit)
787 			really_exit = 0;
788 
789 		reclaim();
790 	}
791 	quitenv(NULL);
792 	source = old_source;
793 	return (exstat);
794 }
795 
796 /* return to closest error handler or shell(), exit if none found */
797 void
unwind(int i)798 unwind(int i)
799 {
800 	/* ordering for EXIT vs ERR is a bit odd (this is what AT&T ksh does) */
801 	if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR) &&
802 	    sigtraps[ksh_SIGEXIT].trap)) {
803 		++trap_nested;
804 		runtrap(&sigtraps[ksh_SIGEXIT], trap_nested == 1);
805 		--trap_nested;
806 		i = LLEAVE;
807 	} else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) {
808 		++trap_nested;
809 		runtrap(&sigtraps[ksh_SIGERR], trap_nested == 1);
810 		--trap_nested;
811 		i = LLEAVE;
812 	}
813 	while (/* CONSTCOND */ 1) {
814 		switch (e->type) {
815 		case E_PARSE:
816 		case E_FUNC:
817 		case E_INCL:
818 		case E_LOOP:
819 		case E_ERRH:
820 			siglongjmp(e->jbuf, i);
821 			/* NOTREACHED */
822 		case E_NONE:
823 			if (i == LINTR)
824 				e->flags |= EF_FAKE_SIGDIE;
825 			/* FALLTHROUGH */
826 		default:
827 			quitenv(NULL);
828 		}
829 	}
830 }
831 
832 void
newenv(int type)833 newenv(int type)
834 {
835 	struct env *ep;
836 	char *cp;
837 
838 	/*
839 	 * struct env includes ALLOC_ITEM for alignment constraints
840 	 * so first get the actually used memory, then assign it
841 	 */
842 	cp = alloc(sizeof(struct env) - ALLOC_SIZE, ATEMP);
843 	/* undo what alloc() did to the malloc result address */
844 	ep = (void *)(cp - ALLOC_SIZE);
845 	/* initialise public members of struct env (not the ALLOC_ITEM) */
846 	ainit(&ep->area);
847 	ep->oenv = e;
848 	ep->loc = e->loc;
849 	ep->savefd = NULL;
850 	ep->temps = NULL;
851 	ep->type = type;
852 	ep->flags = 0;
853 	/* jump buffer is invalid because flags == 0 */
854 	e = ep;
855 }
856 
857 void
quitenv(struct shf * shf)858 quitenv(struct shf *shf)
859 {
860 	struct env *ep = e;
861 	char *cp;
862 	int fd;
863 
864 	if (ep->oenv && ep->oenv->loc != ep->loc)
865 		popblock();
866 	if (ep->savefd != NULL) {
867 		for (fd = 0; fd < NUFILE; fd++)
868 			/* if ep->savefd[fd] < 0, means fd was closed */
869 			if (ep->savefd[fd])
870 				restfd(fd, ep->savefd[fd]);
871 		if (ep->savefd[2])
872 			/* Clear any write errors */
873 			shf_reopen(2, SHF_WR, shl_out);
874 	}
875 	/*
876 	 * Bottom of the stack.
877 	 * Either main shell is exiting or cleanup_parents_env() was called.
878 	 */
879 	if (ep->oenv == NULL) {
880 		if (ep->type == E_NONE) {
881 			/* Main shell exiting? */
882 #if HAVE_PERSISTENT_HISTORY
883 			if (Flag(FTALKING))
884 				hist_finish();
885 #endif
886 			j_exit();
887 			if (ep->flags & EF_FAKE_SIGDIE) {
888 				int sig = exstat - 128;
889 
890 				/*
891 				 * ham up our death a bit (AT&T ksh
892 				 * only seems to do this for SIGTERM)
893 				 * Don't do it for SIGQUIT, since we'd
894 				 * dump a core..
895 				 */
896 				if ((sig == SIGINT || sig == SIGTERM) &&
897 				    (kshpgrp == kshpid)) {
898 					setsig(&sigtraps[sig], SIG_DFL,
899 					    SS_RESTORE_CURR | SS_FORCE);
900 					kill(0, sig);
901 				}
902 			}
903 		}
904 		if (shf)
905 			shf_close(shf);
906 		reclaim();
907 		exit(exstat);
908 	}
909 	if (shf)
910 		shf_close(shf);
911 	reclaim();
912 
913 	e = e->oenv;
914 
915 	/* free the struct env - tricky due to the ALLOC_ITEM inside */
916 	cp = (void *)ep;
917 	afree(cp + ALLOC_SIZE, ATEMP);
918 }
919 
920 /* Called after a fork to cleanup stuff left over from parents environment */
921 void
cleanup_parents_env(void)922 cleanup_parents_env(void)
923 {
924 	struct env *ep;
925 	int fd;
926 
927 	mkssert(e != NULL);
928 
929 	/*
930 	 * Don't clean up temporary files - parent will probably need them.
931 	 * Also, can't easily reclaim memory since variables, etc. could be
932 	 * anywhere.
933 	 */
934 
935 	/* close all file descriptors hiding in savefd */
936 	for (ep = e; ep; ep = ep->oenv) {
937 		if (ep->savefd) {
938 			for (fd = 0; fd < NUFILE; fd++)
939 				if (ep->savefd[fd] > 0)
940 					close(ep->savefd[fd]);
941 			afree(ep->savefd, &ep->area);
942 			ep->savefd = NULL;
943 		}
944 	}
945 	e->oenv = NULL;
946 }
947 
948 /* Called just before an execve cleanup stuff temporary files */
949 void
cleanup_proc_env(void)950 cleanup_proc_env(void)
951 {
952 	struct env *ep;
953 
954 	for (ep = e; ep; ep = ep->oenv)
955 		remove_temps(ep->temps);
956 }
957 
958 /* remove temp files and free ATEMP Area */
959 static void
reclaim(void)960 reclaim(void)
961 {
962 	remove_temps(e->temps);
963 	e->temps = NULL;
964 	afreeall(&e->area);
965 }
966 
967 static void
remove_temps(struct temp * tp)968 remove_temps(struct temp *tp)
969 {
970 	for (; tp != NULL; tp = tp->next)
971 		if (tp->pid == procpid)
972 			unlink(tp->name);
973 }
974 
975 /*
976  * Initialise tty_fd. Used for saving/reseting tty modes upon
977  * foreground job completion and for setting up tty process group.
978  */
979 void
tty_init(bool init_ttystate,bool need_tty)980 tty_init(bool init_ttystate, bool need_tty)
981 {
982 	bool do_close = true;
983 	int tfd;
984 
985 	if (tty_fd >= 0) {
986 		close(tty_fd);
987 		tty_fd = -1;
988 	}
989 	tty_devtty = true;
990 
991 #ifdef _UWIN
992 	/*XXX imake style */
993 	if (isatty(3)) {
994 		/* fd 3 on UWIN _is_ /dev/tty (or our controlling tty) */
995 		tfd = 3;
996 		do_close = false;
997 	} else
998 #endif
999 	  if ((tfd = open("/dev/tty", O_RDWR, 0)) < 0) {
1000 		tty_devtty = false;
1001 		if (need_tty)
1002 			warningf(false, "%s: %s %s: %s",
1003 			    "No controlling tty", "open", "/dev/tty",
1004 			    strerror(errno));
1005 	}
1006 	if (tfd < 0) {
1007 		do_close = false;
1008 		if (isatty(0))
1009 			tfd = 0;
1010 		else if (isatty(2))
1011 			tfd = 2;
1012 		else {
1013 			if (need_tty)
1014 				warningf(false, "can't find tty fd");
1015 			return;
1016 		}
1017 	}
1018 	if ((tty_fd = fcntl(tfd, F_DUPFD, FDBASE)) < 0) {
1019 		if (need_tty)
1020 			warningf(false, "%s: %s %s: %s", "j_ttyinit",
1021 			    "dup of tty fd", "failed", strerror(errno));
1022 	} else if (fcntl(tty_fd, F_SETFD, FD_CLOEXEC) < 0) {
1023 		if (need_tty)
1024 			warningf(false, "%s: %s: %s", "j_ttyinit",
1025 			    "can't set close-on-exec flag", strerror(errno));
1026 		close(tty_fd);
1027 		tty_fd = -1;
1028 	} else if (init_ttystate)
1029 		tcgetattr(tty_fd, &tty_state);
1030 	if (do_close)
1031 		close(tfd);
1032 }
1033 
1034 void
tty_close(void)1035 tty_close(void)
1036 {
1037 	if (tty_fd >= 0) {
1038 		close(tty_fd);
1039 		tty_fd = -1;
1040 	}
1041 }
1042 
1043 /* A shell error occurred (eg, syntax error, etc.) */
1044 
1045 #define VWARNINGF_ERRORPREFIX	1
1046 #define VWARNINGF_FILELINE	2
1047 #define VWARNINGF_BUILTIN	4
1048 #define VWARNINGF_INTERNAL	8
1049 
1050 static void vwarningf(unsigned int, const char *, va_list)
1051     MKSH_A_FORMAT(__printf__, 2, 0);
1052 
1053 static void
vwarningf(unsigned int flags,const char * fmt,va_list ap)1054 vwarningf(unsigned int flags, const char *fmt, va_list ap)
1055 {
1056 	if (*fmt != 1) {
1057 		if (flags & VWARNINGF_INTERNAL)
1058 			shf_fprintf(shl_out, "internal error: ");
1059 		if (flags & VWARNINGF_ERRORPREFIX)
1060 			error_prefix(tobool(flags & VWARNINGF_FILELINE));
1061 		if ((flags & VWARNINGF_BUILTIN) &&
1062 		    /* not set when main() calls parse_args() */
1063 		    builtin_argv0 && builtin_argv0 != kshname)
1064 			shf_fprintf(shl_out, "%s: ", builtin_argv0);
1065 		shf_vfprintf(shl_out, fmt, ap);
1066 		shf_putchar('\n', shl_out);
1067 	}
1068 	shf_flush(shl_out);
1069 }
1070 
1071 void
errorfx(int rc,const char * fmt,...)1072 errorfx(int rc, const char *fmt, ...)
1073 {
1074 	va_list va;
1075 
1076 	exstat = rc;
1077 
1078 	/* debugging: note that stdout not valid */
1079 	shl_stdout_ok = false;
1080 
1081 	va_start(va, fmt);
1082 	vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE, fmt, va);
1083 	va_end(va);
1084 	unwind(LERROR);
1085 }
1086 
1087 void
errorf(const char * fmt,...)1088 errorf(const char *fmt, ...)
1089 {
1090 	va_list va;
1091 
1092 	exstat = 1;
1093 
1094 	/* debugging: note that stdout not valid */
1095 	shl_stdout_ok = false;
1096 
1097 	va_start(va, fmt);
1098 	vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE, fmt, va);
1099 	va_end(va);
1100 	unwind(LERROR);
1101 }
1102 
1103 /* like errorf(), but no unwind is done */
1104 void
warningf(bool fileline,const char * fmt,...)1105 warningf(bool fileline, const char *fmt, ...)
1106 {
1107 	va_list va;
1108 
1109 	va_start(va, fmt);
1110 	vwarningf(VWARNINGF_ERRORPREFIX | (fileline ? VWARNINGF_FILELINE : 0),
1111 	    fmt, va);
1112 	va_end(va);
1113 }
1114 
1115 /*
1116  * Used by built-in utilities to prefix shell and utility name to message
1117  * (also unwinds environments for special builtins).
1118  */
1119 void
bi_errorf(const char * fmt,...)1120 bi_errorf(const char *fmt, ...)
1121 {
1122 	va_list va;
1123 
1124 	/* debugging: note that stdout not valid */
1125 	shl_stdout_ok = false;
1126 
1127 	exstat = 1;
1128 
1129 	va_start(va, fmt);
1130 	vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE |
1131 	    VWARNINGF_BUILTIN, fmt, va);
1132 	va_end(va);
1133 
1134 	/*
1135 	 * POSIX special builtins and ksh special builtins cause
1136 	 * non-interactive shells to exit.
1137 	 * XXX odd use of KEEPASN; also may not want LERROR here
1138 	 */
1139 	if (builtin_flag & SPEC_BI) {
1140 		builtin_argv0 = NULL;
1141 		unwind(LERROR);
1142 	}
1143 }
1144 
1145 /* Called when something that shouldn't happen does */
1146 void
internal_errorf(const char * fmt,...)1147 internal_errorf(const char *fmt, ...)
1148 {
1149 	va_list va;
1150 
1151 	va_start(va, fmt);
1152 	vwarningf(VWARNINGF_INTERNAL, fmt, va);
1153 	va_end(va);
1154 	unwind(LERROR);
1155 }
1156 
1157 void
internal_warningf(const char * fmt,...)1158 internal_warningf(const char *fmt, ...)
1159 {
1160 	va_list va;
1161 
1162 	va_start(va, fmt);
1163 	vwarningf(VWARNINGF_INTERNAL, fmt, va);
1164 	va_end(va);
1165 }
1166 
1167 /* used by error reporting functions to print "ksh: .kshrc[25]: " */
1168 void
error_prefix(bool fileline)1169 error_prefix(bool fileline)
1170 {
1171 	/* Avoid foo: foo[2]: ... */
1172 	if (!fileline || !source || !source->file ||
1173 	    strcmp(source->file, kshname) != 0)
1174 		shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-'));
1175 	if (fileline && source && source->file != NULL) {
1176 		shf_fprintf(shl_out, "%s[%d]: ", source->file,
1177 		    source->errline > 0 ? source->errline : source->line);
1178 		source->errline = 0;
1179 	}
1180 }
1181 
1182 /* printf to shl_out (stderr) with flush */
1183 void
shellf(const char * fmt,...)1184 shellf(const char *fmt, ...)
1185 {
1186 	va_list va;
1187 
1188 	if (!initio_done)
1189 		/* shl_out may not be set up yet... */
1190 		return;
1191 	va_start(va, fmt);
1192 	shf_vfprintf(shl_out, fmt, va);
1193 	va_end(va);
1194 	shf_flush(shl_out);
1195 }
1196 
1197 /* printf to shl_stdout (stdout) */
1198 void
shprintf(const char * fmt,...)1199 shprintf(const char *fmt, ...)
1200 {
1201 	va_list va;
1202 
1203 	if (!shl_stdout_ok)
1204 		internal_errorf("shl_stdout not valid");
1205 	va_start(va, fmt);
1206 	shf_vfprintf(shl_stdout, fmt, va);
1207 	va_end(va);
1208 }
1209 
1210 /* test if we can seek backwards fd (returns 0 or SHF_UNBUF) */
1211 int
can_seek(int fd)1212 can_seek(int fd)
1213 {
1214 	struct stat statb;
1215 
1216 	return (fstat(fd, &statb) == 0 && !S_ISREG(statb.st_mode) ?
1217 	    SHF_UNBUF : 0);
1218 }
1219 
1220 struct shf shf_iob[3];
1221 
1222 void
initio(void)1223 initio(void)
1224 {
1225 	/* force buffer allocation */
1226 	shf_fdopen(1, SHF_WR, shl_stdout);
1227 	shf_fdopen(2, SHF_WR, shl_out);
1228 	/* force buffer allocation */
1229 	shf_fdopen(2, SHF_WR, shl_spare);
1230 	initio_done = 1;
1231 }
1232 
1233 /* A dup2() with error checking */
1234 int
ksh_dup2(int ofd,int nfd,bool errok)1235 ksh_dup2(int ofd, int nfd, bool errok)
1236 {
1237 	int rv;
1238 
1239 	if (((rv = dup2(ofd, nfd)) < 0) && !errok && (errno != EBADF))
1240 		errorf("too many files open in shell");
1241 
1242 #ifdef __ultrix
1243 	/*XXX imake style */
1244 	if (rv >= 0)
1245 		fcntl(nfd, F_SETFD, 0);
1246 #endif
1247 
1248 	return (rv);
1249 }
1250 
1251 /*
1252  * Move fd from user space (0 <= fd < 10) to shell space (fd >= 10),
1253  * set close-on-exec flag. See FDBASE in sh.h, maybe 24 not 10 here.
1254  */
1255 short
savefd(int fd)1256 savefd(int fd)
1257 {
1258 	int nfd = fd;
1259 
1260 	if (fd < FDBASE && (nfd = fcntl(fd, F_DUPFD, FDBASE)) < 0 &&
1261 	    errno == EBADF)
1262 		return (-1);
1263 	if (nfd < 0 || nfd > SHRT_MAX)
1264 		errorf("too many files open in shell");
1265 	fcntl(nfd, F_SETFD, FD_CLOEXEC);
1266 	return ((short)nfd);
1267 }
1268 
1269 void
restfd(int fd,int ofd)1270 restfd(int fd, int ofd)
1271 {
1272 	if (fd == 2)
1273 		shf_flush(&shf_iob[fd]);
1274 	if (ofd < 0)
1275 		/* original fd closed */
1276 		close(fd);
1277 	else if (fd != ofd) {
1278 		/*XXX: what to do if this dup fails? */
1279 		ksh_dup2(ofd, fd, true);
1280 		close(ofd);
1281 	}
1282 }
1283 
1284 void
openpipe(int * pv)1285 openpipe(int *pv)
1286 {
1287 	int lpv[2];
1288 
1289 	if (pipe(lpv) < 0)
1290 		errorf("can't create pipe - try again");
1291 	pv[0] = savefd(lpv[0]);
1292 	if (pv[0] != lpv[0])
1293 		close(lpv[0]);
1294 	pv[1] = savefd(lpv[1]);
1295 	if (pv[1] != lpv[1])
1296 		close(lpv[1]);
1297 }
1298 
1299 void
closepipe(int * pv)1300 closepipe(int *pv)
1301 {
1302 	close(pv[0]);
1303 	close(pv[1]);
1304 }
1305 
1306 /*
1307  * Called by iosetup() (deals with 2>&4, etc.), c_read, c_print to turn
1308  * a string (the X in 2>&X, read -uX, print -uX) into a file descriptor.
1309  */
1310 int
check_fd(const char * name,int mode,const char ** emsgp)1311 check_fd(const char *name, int mode, const char **emsgp)
1312 {
1313 	int fd, fl;
1314 
1315 	if (name[0] == 'p' && !name[1])
1316 		return (coproc_getfd(mode, emsgp));
1317 	for (fd = 0; ksh_isdigit(*name); ++name)
1318 		fd = (fd * 10) + *name - '0';
1319 	if (*name || fd >= FDBASE) {
1320 		if (emsgp)
1321 			*emsgp = "illegal file descriptor name";
1322 		return (-1);
1323 	}
1324 	if ((fl = fcntl(fd, F_GETFL, 0)) < 0) {
1325 		if (emsgp)
1326 			*emsgp = "bad file descriptor";
1327 		return (-1);
1328 	}
1329 	fl &= O_ACCMODE;
1330 	/*
1331 	 * X_OK is a kludge to disable this check for dups (x<&1):
1332 	 * historical shells never did this check (XXX don't know what
1333 	 * POSIX has to say).
1334 	 */
1335 	if (!(mode & X_OK) && fl != O_RDWR && (
1336 	    ((mode & R_OK) && fl != O_RDONLY) ||
1337 	    ((mode & W_OK) && fl != O_WRONLY))) {
1338 		if (emsgp)
1339 			*emsgp = (fl == O_WRONLY) ?
1340 			    "fd not open for reading" :
1341 			    "fd not open for writing";
1342 		return (-1);
1343 	}
1344 	return (fd);
1345 }
1346 
1347 /* Called once from main */
1348 void
coproc_init(void)1349 coproc_init(void)
1350 {
1351 	coproc.read = coproc.readw = coproc.write = -1;
1352 	coproc.njobs = 0;
1353 	coproc.id = 0;
1354 }
1355 
1356 /* Called by c_read() when eof is read - close fd if it is the co-process fd */
1357 void
coproc_read_close(int fd)1358 coproc_read_close(int fd)
1359 {
1360 	if (coproc.read >= 0 && fd == coproc.read) {
1361 		coproc_readw_close(fd);
1362 		close(coproc.read);
1363 		coproc.read = -1;
1364 	}
1365 }
1366 
1367 /*
1368  * Called by c_read() and by iosetup() to close the other side of the
1369  * read pipe, so reads will actually terminate.
1370  */
1371 void
coproc_readw_close(int fd)1372 coproc_readw_close(int fd)
1373 {
1374 	if (coproc.readw >= 0 && coproc.read >= 0 && fd == coproc.read) {
1375 		close(coproc.readw);
1376 		coproc.readw = -1;
1377 	}
1378 }
1379 
1380 /*
1381  * Called by c_print when a write to a fd fails with EPIPE and by iosetup
1382  * when co-process input is dup'd
1383  */
1384 void
coproc_write_close(int fd)1385 coproc_write_close(int fd)
1386 {
1387 	if (coproc.write >= 0 && fd == coproc.write) {
1388 		close(coproc.write);
1389 		coproc.write = -1;
1390 	}
1391 }
1392 
1393 /*
1394  * Called to check for existence of/value of the co-process file descriptor.
1395  * (Used by check_fd() and by c_read/c_print to deal with -p option).
1396  */
1397 int
coproc_getfd(int mode,const char ** emsgp)1398 coproc_getfd(int mode, const char **emsgp)
1399 {
1400 	int fd = (mode & R_OK) ? coproc.read : coproc.write;
1401 
1402 	if (fd >= 0)
1403 		return (fd);
1404 	if (emsgp)
1405 		*emsgp = "no coprocess";
1406 	return (-1);
1407 }
1408 
1409 /*
1410  * called to close file descriptors related to the coprocess (if any)
1411  * Should be called with SIGCHLD blocked.
1412  */
1413 void
coproc_cleanup(int reuse)1414 coproc_cleanup(int reuse)
1415 {
1416 	/* This to allow co-processes to share output pipe */
1417 	if (!reuse || coproc.readw < 0 || coproc.read < 0) {
1418 		if (coproc.read >= 0) {
1419 			close(coproc.read);
1420 			coproc.read = -1;
1421 		}
1422 		if (coproc.readw >= 0) {
1423 			close(coproc.readw);
1424 			coproc.readw = -1;
1425 		}
1426 	}
1427 	if (coproc.write >= 0) {
1428 		close(coproc.write);
1429 		coproc.write = -1;
1430 	}
1431 }
1432 
1433 struct temp *
maketemp(Area * ap,Temp_type type,struct temp ** tlist)1434 maketemp(Area *ap, Temp_type type, struct temp **tlist)
1435 {
1436 	struct temp *tp;
1437 	size_t len;
1438 	int fd;
1439 	char *pathname;
1440 	const char *dir;
1441 
1442 	dir = tmpdir ? tmpdir : MKSH_DEFAULT_TMPDIR;
1443 #if HAVE_MKSTEMP
1444 	len = strlen(dir) + 6 + 10 + 1;
1445 #else
1446 	pathname = tempnam(dir, "mksh.");
1447 	len = ((pathname == NULL) ? 0 : strlen(pathname)) + 1;
1448 #endif
1449 	/* reasonably sure that this will not overflow */
1450 	tp = alloc(sizeof(struct temp) + len, ap);
1451 	tp->name = (char *)&tp[1];
1452 #if !HAVE_MKSTEMP
1453 	if (pathname == NULL)
1454 		tp->name[0] = '\0';
1455 	else {
1456 		memcpy(tp->name, pathname, len);
1457 		free_ostempnam(pathname);
1458 	}
1459 #endif
1460 	pathname = tp->name;
1461 	tp->shf = NULL;
1462 	tp->type = type;
1463 #if HAVE_MKSTEMP
1464 	shf_snprintf(pathname, len, "%s%s", dir, "/mksh.XXXXXXXXXX");
1465 	if ((fd = mkstemp(pathname)) >= 0)
1466 #else
1467 	if (tp->name[0] && (fd = open(tp->name, O_CREAT | O_RDWR, 0600)) >= 0)
1468 #endif
1469 		tp->shf = shf_fdopen(fd, SHF_WR, NULL);
1470 	tp->pid = procpid;
1471 
1472 	tp->next = *tlist;
1473 	*tlist = tp;
1474 	return (tp);
1475 }
1476 
1477 /*
1478  * We use a similar collision resolution algorithm as Python 2.5.4
1479  * but with a slightly tweaked implementation written from scratch.
1480  */
1481 
1482 #define	INIT_TBLSHIFT	3	/* initial table shift (2^3 = 8) */
1483 #define PERTURB_SHIFT	5	/* see Python 2.5.4 Objects/dictobject.c */
1484 
1485 static void tgrow(struct table *);
1486 static int tnamecmp(const void *, const void *);
1487 
1488 static void
tgrow(struct table * tp)1489 tgrow(struct table *tp)
1490 {
1491 	size_t i, j, osize, mask, perturb;
1492 	struct tbl *tblp, **pp;
1493 	struct tbl **ntblp, **otblp = tp->tbls;
1494 
1495 	if (tp->tshift > 29)
1496 		internal_errorf("hash table size limit reached");
1497 
1498 	/* calculate old size, new shift and new size */
1499 	osize = (size_t)1 << (tp->tshift++);
1500 	i = osize << 1;
1501 
1502 	ntblp = alloc2(i, sizeof(struct tbl *), tp->areap);
1503 	/* multiplication cannot overflow: alloc2 checked that */
1504 	memset(ntblp, 0, i * sizeof(struct tbl *));
1505 
1506 	/* table can get 80% full except when reaching its limit */
1507 	tp->nfree = (tp->tshift == 30) ? 0x3FFF0000UL : ((i * 4) / 5);
1508 	tp->tbls = ntblp;
1509 	if (otblp == NULL)
1510 		return;
1511 
1512 	mask = i - 1;
1513 	for (i = 0; i < osize; i++)
1514 		if ((tblp = otblp[i]) != NULL) {
1515 			if ((tblp->flag & DEFINED)) {
1516 				/* search for free hash table slot */
1517 				j = (perturb = tblp->ua.hval) & mask;
1518 				goto find_first_empty_slot;
1519  find_next_empty_slot:
1520 				j = (j << 2) + j + perturb + 1;
1521 				perturb >>= PERTURB_SHIFT;
1522  find_first_empty_slot:
1523 				pp = &ntblp[j & mask];
1524 				if (*pp != NULL)
1525 					goto find_next_empty_slot;
1526 				/* found an empty hash table slot */
1527 				*pp = tblp;
1528 				tp->nfree--;
1529 			} else if (!(tblp->flag & FINUSE)) {
1530 				afree(tblp, tp->areap);
1531 			}
1532 		}
1533 	afree(otblp, tp->areap);
1534 }
1535 
1536 void
ktinit(Area * ap,struct table * tp,uint8_t initshift)1537 ktinit(Area *ap, struct table *tp, uint8_t initshift)
1538 {
1539 	tp->areap = ap;
1540 	tp->tbls = NULL;
1541 	tp->tshift = ((initshift > INIT_TBLSHIFT) ?
1542 	    initshift : INIT_TBLSHIFT) - 1;
1543 	tgrow(tp);
1544 }
1545 
1546 /* table, name (key) to search for, hash(name), rv pointer to tbl ptr */
1547 struct tbl *
ktscan(struct table * tp,const char * name,uint32_t h,struct tbl *** ppp)1548 ktscan(struct table *tp, const char *name, uint32_t h, struct tbl ***ppp)
1549 {
1550 	size_t j, perturb, mask;
1551 	struct tbl **pp, *p;
1552 
1553 	mask = ((size_t)1 << (tp->tshift)) - 1;
1554 	/* search for hash table slot matching name */
1555 	j = (perturb = h) & mask;
1556 	goto find_first_slot;
1557  find_next_slot:
1558 	j = (j << 2) + j + perturb + 1;
1559 	perturb >>= PERTURB_SHIFT;
1560  find_first_slot:
1561 	pp = &tp->tbls[j & mask];
1562 	if ((p = *pp) != NULL && (p->ua.hval != h || !(p->flag & DEFINED) ||
1563 	    strcmp(p->name, name)))
1564 		goto find_next_slot;
1565 	/* p == NULL if not found, correct found entry otherwise */
1566 	if (ppp)
1567 		*ppp = pp;
1568 	return (p);
1569 }
1570 
1571 /* table, name (key) to enter, hash(n) */
1572 struct tbl *
ktenter(struct table * tp,const char * n,uint32_t h)1573 ktenter(struct table *tp, const char *n, uint32_t h)
1574 {
1575 	struct tbl **pp, *p;
1576 	size_t len;
1577 
1578  Search:
1579 	if ((p = ktscan(tp, n, h, &pp)))
1580 		return (p);
1581 
1582 	if (tp->nfree == 0) {
1583 		/* too full */
1584 		tgrow(tp);
1585 		goto Search;
1586 	}
1587 
1588 	/* create new tbl entry */
1589 	len = strlen(n);
1590 	checkoktoadd(len, offsetof(struct tbl, name[0]) + 1);
1591 	p = alloc(offsetof(struct tbl, name[0]) + ++len, tp->areap);
1592 	p->flag = 0;
1593 	p->type = 0;
1594 	p->areap = tp->areap;
1595 	p->ua.hval = h;
1596 	p->u2.field = 0;
1597 	p->u.array = NULL;
1598 	memcpy(p->name, n, len);
1599 
1600 	/* enter in tp->tbls */
1601 	tp->nfree--;
1602 	*pp = p;
1603 	return (p);
1604 }
1605 
1606 void
ktwalk(struct tstate * ts,struct table * tp)1607 ktwalk(struct tstate *ts, struct table *tp)
1608 {
1609 	ts->left = (size_t)1 << (tp->tshift);
1610 	ts->next = tp->tbls;
1611 }
1612 
1613 struct tbl *
ktnext(struct tstate * ts)1614 ktnext(struct tstate *ts)
1615 {
1616 	while (--ts->left >= 0) {
1617 		struct tbl *p = *ts->next++;
1618 		if (p != NULL && (p->flag & DEFINED))
1619 			return (p);
1620 	}
1621 	return (NULL);
1622 }
1623 
1624 static int
tnamecmp(const void * p1,const void * p2)1625 tnamecmp(const void *p1, const void *p2)
1626 {
1627 	const struct tbl *a = *((const struct tbl * const *)p1);
1628 	const struct tbl *b = *((const struct tbl * const *)p2);
1629 
1630 	return (strcmp(a->name, b->name));
1631 }
1632 
1633 struct tbl **
ktsort(struct table * tp)1634 ktsort(struct table *tp)
1635 {
1636 	size_t i;
1637 	struct tbl **p, **sp, **dp;
1638 
1639 	/*
1640 	 * since the table is never entirely full, no need to reserve
1641 	 * additional space for the trailing NULL appended below
1642 	 */
1643 	i = (size_t)1 << (tp->tshift);
1644 	p = alloc2(i, sizeof(struct tbl *), ATEMP);
1645 	sp = tp->tbls;		/* source */
1646 	dp = p;			/* dest */
1647 	while (i--)
1648 		if ((*dp = *sp++) != NULL && (((*dp)->flag & DEFINED) ||
1649 		    ((*dp)->flag & ARRAY)))
1650 			dp++;
1651 	qsort(p, (i = dp - p), sizeof(struct tbl *), tnamecmp);
1652 	p[i] = NULL;
1653 	return (p);
1654 }
1655 
1656 #ifdef SIGWINCH
1657 static void
x_sigwinch(int sig MKSH_A_UNUSED)1658 x_sigwinch(int sig MKSH_A_UNUSED)
1659 {
1660 	/* this runs inside interrupt context, with errno saved */
1661 
1662 	got_winch = 1;
1663 }
1664 #endif
1665