Example #1
0
void
set_prompt(int to, Source *s)
{
	char *ps1;
	Area *saved_atemp;

	cur_prompt = to;

	switch (to) {
	case PS1: /* command */
		ps1 = str_save(str_val(global("PS1")), ATEMP);
		saved_atemp = ATEMP;	/* ps1 is freed by substitute() */
		newenv(E_ERRH);
		if (_setjmp(e->jbuf)) {
			prompt = safe_prompt;
			/* Don't print an error - assume it has already
			 * been printed.  Reason is we may have forked
			 * to run a command and the child may be
			 * unwinding its stack through this code as it
			 * exits.
			 */
		} else
			prompt = str_save(substitute(ps1, 0),
					 saved_atemp);
		quitenv(NULL);
		break;
	case PS2: /* command continuation */
		prompt = str_val(global("PS2"));
		break;
	}
}
/*
 *	ksh special - the select command processing section
 *	print the args in column form - assuming that we can
 */
static const char *
do_selectargs(const char **ap, bool print_menu)
{
	static const char *read_args[] = {
		"read", "-r", "REPLY", NULL
	};
	char *s;
	int i, argct;

	for (argct = 0; ap[argct]; argct++)
		;
	while (/* CONSTCOND */ 1) {
		/*-
		 * Menu is printed if
		 *	- this is the first time around the select loop
		 *	- the user enters a blank line
		 *	- the REPLY parameter is empty
		 */
		if (print_menu || !*str_val(global("REPLY")))
			pr_menu(ap);
		shellf("%s", str_val(global("PS3")));
		if (call_builtin(findcom("read", FC_BI), read_args, Tselect))
			return (NULL);
		s = str_val(global("REPLY"));
		if (*s) {
			getn(s, &i);
			return ((i >= 1 && i <= argct) ? ap[i - 1] : null);
		}
		print_menu = true;
	}
}
Example #3
0
void
set_prompt(int to, Source *s)
{
	cur_prompt = (uint8_t)to;

	switch (to) {
	/* command */
	case PS1:
		/*
		 * Substitute ! and !! here, before substitutions are done
		 * so ! in expanded variables are not expanded.
		 * NOTE: this is not what AT&T ksh does (it does it after
		 * substitutions, POSIX doesn't say which is to be done.
		 */
		{
			struct shf *shf;
			char * volatile ps1;
			Area *saved_atemp;
			int saved_lineno;

			ps1 = str_val(global("PS1"));
			shf = shf_sopen(NULL, strlen(ps1) * 2,
			    SHF_WR | SHF_DYNAMIC, NULL);
			while (*ps1)
				if (*ps1 != '!' || *++ps1 == '!')
					shf_putchar(*ps1++, shf);
				else
					shf_fprintf(shf, "%lu", s ?
					    (unsigned long)s->line + 1 : 0UL);
			ps1 = shf_sclose(shf);
			saved_lineno = current_lineno;
			if (s)
				current_lineno = s->line + 1;
			saved_atemp = ATEMP;
			newenv(E_ERRH);
			if (kshsetjmp(e->jbuf)) {
				prompt = safe_prompt;
				/*
				 * Don't print an error - assume it has already
				 * been printed. Reason is we may have forked
				 * to run a command and the child may be
				 * unwinding its stack through this code as it
				 * exits.
				 */
			} else {
				char *cp = substitute(ps1, 0);
				strdupx(prompt, cp, saved_atemp);
			}
			current_lineno = saved_lineno;
			quitenv(NULL);
		}
		break;
	/* command continuation */
	case PS2:
		prompt = str_val(global("PS2"));
		break;
	}
}
Example #4
0
/*
 * if argument string contains any special characters, they will
 * be escaped and the result will be put into edit buffer by
 * keybinding-specific function
 */
int
x_escape(const char *s, size_t len, int (*putbuf_func) (const char *, size_t))
{
	size_t add, wlen;
	const char *ifs = str_val(local("IFS", 0));
	int rval = 0;

	for (add = 0, wlen = len; wlen - add > 0; add++) {
		if (strchr("!\"#$&'()*:;<=>?[\\]`{|}", s[add]) ||
		    strchr(ifs, s[add])) {
			if (putbuf_func(s, add) != 0) {
				rval = -1;
				break;
			}

			putbuf_func("\\", 1);
			putbuf_func(&s[add], 1);

			add++;
			wlen -= add;
			s += add;
			add = -1; /* after the increment it will go to 0 */
		}
	}
	if (wlen > 0 && rval == 0)
		rval = putbuf_func(s, wlen);

	return (rval);
}
	void string_index_object_t::test<17>()
	{
		BOOL value;
		std::string str_val("1");
		ensure("convertToBOOL 1 failed", LLStringUtil::convertToBOOL(str_val, value) && value);
		str_val = "T";
		ensure("convertToBOOL T failed", LLStringUtil::convertToBOOL(str_val, value) && value);
		str_val = "t";
		ensure("convertToBOOL t failed", LLStringUtil::convertToBOOL(str_val, value) && value);
		str_val = "TRUE";
		ensure("convertToBOOL TRUE failed", LLStringUtil::convertToBOOL(str_val, value) && value);
		str_val = "True";
		ensure("convertToBOOL True failed", LLStringUtil::convertToBOOL(str_val, value) && value);
		str_val = "true";
		ensure("convertToBOOL true failed", LLStringUtil::convertToBOOL(str_val, value) && value);

		str_val = "0";
		ensure("convertToBOOL 0 failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
		str_val = "F";
		ensure("convertToBOOL F failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
		str_val = "f";
		ensure("convertToBOOL f failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
		str_val = "FALSE";
		ensure("convertToBOOL FASLE failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
		str_val = "False";
		ensure("convertToBOOL False failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
		str_val = "false";
		ensure("convertToBOOL false failed", LLStringUtil::convertToBOOL(str_val, value) && !value);

		str_val = "Tblah";
		ensure("convertToBOOL false failed", !LLStringUtil::convertToBOOL(str_val, value));
	}
	void string_index_object_t::test<14>()
	{
		std::string str_val("Hello.\n\t\r\nABCDEFGHIABABAB");
		LLStringUtil::replaceChar(str_val, 'A', 'X');
		ensure_equals("1: replaceChar failed", str_val, "Hello.\n\t\r\nXBCDEFGHIXBXBXB");
		std::string str_val1("Hello.\n\t\r\nABCDEFGHIABABAB");
	}
	void string_index_object_t::test<15>()
	{
		std::string str_val("Hello.\n\r\t");
		ensure("containsNonprintable failed", LLStringUtil::containsNonprintable(str_val) == TRUE);

		str_val = "ABC ";
		ensure("containsNonprintable failed", LLStringUtil::containsNonprintable(str_val) == FALSE);
	}
	void string_index_object_t::test<10>()
	{
		std::string str_val("Second");
		ensure("1. isHead failed", LLStringUtil::isHead(str_val, "SecondLife Source") == TRUE);
		ensure("2. isHead failed", LLStringUtil::isHead(str_val, " SecondLife Source") == FALSE);
		std::string str_val2("");
		ensure("3. isHead failed", LLStringUtil::isHead(str_val2, "") == FALSE);
	}
	void string_index_object_t::test<11>()
	{
		std::string str_val("Hello.\n\n Lindenlabs. \n This is \na simple test.\n");
		std::string orig_str_val(str_val);
		LLStringUtil::addCRLF(str_val);
		ensure_equals("addCRLF failed", str_val, "Hello.\r\n\r\n Lindenlabs. \r\n This is \r\na simple test.\r\n");
		LLStringUtil::removeCRLF(str_val);
		ensure_equals("removeCRLF failed", str_val, orig_str_val);
	}
	void string_index_object_t::test<5>()
	{
		std::string str_val("  Testing the   extra     whitespaces         ");
		LLStringUtil::trimTail(str_val);
		ensure_equals("1: trimTail failed", str_val, "  Testing the   extra     whitespaces");

		std::string str_val1("\n  Testing the extra whitespaces  \n\t\r\n   ");
		LLStringUtil::trimTail(str_val1);
		ensure_equals("2: trimTail failed", str_val1, "\n  Testing the extra whitespaces");
	}
	void string_index_object_t::test<22>()
	{
		U32 value;
		std::string str_val("4294967295"); //0xFFFFFFFF
		ensure("1: convertToU32 failed", LLStringUtil::convertToU32(str_val, value) && value == 4294967295UL);

		str_val = "0";
		ensure("2: convertToU32 failed", LLStringUtil::convertToU32(str_val, value) && value == 0);

		str_val = "4294967296"; 
		ensure("3: convertToU32 failed", !LLStringUtil::convertToU32(str_val, value));
	}
Example #12
0
/* get variable integer value, with error checking */
long
intval(struct tbl *vp)
{
	long num;
	int base;

	base = getint(vp, &num, false);
	if (base == -1)
		/* XXX check calls - is error here ok by POSIX? */
		errorf("%s: bad number", str_val(vp));
	return num;
}
Example #13
0
int	strch(int och,char *tmp0)
{
	int	ex;

	jstrupr(tmp0);

	if(tmp0[0]=='A'||tmp0[0]=='B'){
		ex=str_val(&tmp0[1]);
		if(ex<1 || ex>16){ex=1;}

		if(tmp0[1]==0 && och>0){ex=((och-1)&15)+1;}

		ex+=(tmp0[0]-'A')*16;
	}else{
		ex=str_val(tmp0);
		if(ex<0 || ex>32){ex=0;}
		if(och>16 && ex<17){ex+=16;}
	}

	return(ex);
}
	void string_index_object_t::test<12>()
	{
		std::string str_val("Hello.\n\n\t \t Lindenlabs. \t\t");
		std::string orig_str_val(str_val);
		LLStringUtil::replaceTabsWithSpaces(str_val, 1);
		ensure_equals("replaceTabsWithSpaces failed", str_val, "Hello.\n\n    Lindenlabs.   ");
		LLStringUtil::replaceTabsWithSpaces(orig_str_val, 0);
		ensure_equals("replaceTabsWithSpaces failed for 0", orig_str_val, "Hello.\n\n  Lindenlabs. ");

		str_val = "\t\t\t\t";
		LLStringUtil::replaceTabsWithSpaces(str_val, 0);
		ensure_equals("replaceTabsWithSpaces failed for all tabs", str_val, "");
	}
	void string_index_object_t::test<16>()
	{
		std::string str_val("Hello.\n\r\t Again!");
		LLStringUtil::stripNonprintable(str_val);
		ensure_equals("stripNonprintable failed", str_val, "Hello. Again!");

		str_val = "\r\n\t\t";
		LLStringUtil::stripNonprintable(str_val);
		ensure_equals("stripNonprintable resulting in empty string failed", str_val, "");

		str_val = "";
		LLStringUtil::stripNonprintable(str_val);
		ensure_equals("stripNonprintable of empty string resulting in empty string failed", str_val, "");
	}
	void string_index_object_t::test<18>()
	{
		U8 value;
		std::string str_val("255");
		ensure("1: convertToU8 failed", LLStringUtil::convertToU8(str_val, value) && value == 255);

		str_val = "0";
		ensure("2: convertToU8 failed", LLStringUtil::convertToU8(str_val, value) && value == 0);

		str_val = "-1";
		ensure("3: convertToU8 failed", !LLStringUtil::convertToU8(str_val, value));

		str_val = "256"; // bigger than MAX_U8
		ensure("4: convertToU8 failed", !LLStringUtil::convertToU8(str_val, value));
	}
	void string_index_object_t::test<21>()
	{
		U16 value;
		std::string str_val("65535"); //0xFFFF
		ensure("1: convertToU16 failed", LLStringUtil::convertToU16(str_val, value) && value == 65535);

		str_val = "0";
		ensure("2: convertToU16 failed", LLStringUtil::convertToU16(str_val, value) && value == 0);

		str_val = "-1"; 
		ensure("3: convertToU16 failed", !LLStringUtil::convertToU16(str_val, value));

		str_val = "65536"; 
		ensure("4: convertToU16 failed", !LLStringUtil::convertToU16(str_val, value));
	}
Example #18
0
File: var.c Project: tomgrean/mksh
/* set variable to integer */
void
setint(struct tbl *vq, mksh_ari_t n)
{
	if (!(vq->flag&INTEGER)) {
		vtemp->flag = (ISSET|INTEGER);
		vtemp->type = 0;
		vtemp->areap = ATEMP;
		vtemp->val.i = n;
		/* setstr can't fail here */
		setstr(vq, str_val(vtemp), KSH_RETURN_ERROR);
	} else
		vq->val.i = n;
	vq->flag |= ISSET;
	if ((vq->flag&SPECIAL))
		setspec(vq);
}
Example #19
0
/*
 * Used to calculate an array index for global()/local(). Sets *arrayp
 * to true if this is an array, sets *valp to the array index, returns
 * the basename of the array. May only be called from global()/local()
 * and must be their first callee.
 */
static const char *
array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
{
	const char *p;
	size_t len;
	char *ap = NULL;

	*arrayp = false;
 redo_from_ref:
	p = skip_varname(n, false);
	if (innermost_refflag == SRF_NOP && (p != n) && ksh_isalphx(n[0])) {
		struct tbl *vp;
		char *vn;

		strndupx(vn, n, p - n, ATEMP);
		/* check if this is a reference */
		varsearch(e->loc, &vp, vn, hash(vn));
		afree(vn, ATEMP);
		if (vp && (vp->flag & (DEFINED | ASSOC | ARRAY)) ==
		    (DEFINED | ASSOC)) {
			char *cp;

			/* gotcha! */
			cp = shf_smprintf("%s%s", str_val(vp), p);
			afree(ap, ATEMP);
			n = ap = cp;
			goto redo_from_ref;
		}
	}
	innermost_refflag = SRF_NOP;

	if (p != n && *p == '[' && (len = array_ref_len(p))) {
		char *sub, *tmp;
		mksh_ari_t rval;

		/* calculate the value of the subscript */
		*arrayp = true;
		strndupx(tmp, p + 1, len - 2, ATEMP);
		sub = substitute(tmp, 0);
		afree(tmp, ATEMP);
		strndupx(n, n, p - n, ATEMP);
		evaluate(sub, &rval, KSH_UNWIND_ERROR, true);
		*valp = (uint32_t)rval;
		afree(sub, ATEMP);
	}
	return (n);
}
Example #20
0
int	ctc(int da,char *s,int skey)
{
	int	ct,co,dd=0,i,a;

	if( da>127 ){da=60;if( da>255 ){dd=1;}}
	if( s[0]==0 ){return(-1);}
	jstrupr(s);ct=0;
	i=0;while(s[i]!=0){
		a=s[i++];
		if( a=='H' ){ct=0;break;}
		if( a=='$' ){strcpy(s,&s[1]);ct=0;break;}

		if( a=='C' ){ct=1;}
		if( a=='D' ){ct=3;}
		if( a=='E' ){ct=5;}
		if( a=='F' ){ct=6;}
		if( a=='G' ){ct=8;}
		if( a=='A' ){ct=10;}
		if( a=='B' ){ct=12;}

		if( ct!=0 && s[i]=='B'){i++;ct--;skey=0;}
		if( a=='N' || a=='=' || a=='#' || a=='+' || a=='-' ){skey=0;}
	}

	if( da<0 ){if( ct>0 ){return(1);}else{return(0);}}
	if( ct==0 ){
		da=str_val(s);
	}else{
		ct=ct/*+key_shi[skey&15][ct-1]*/;

		co=(da/12)-1;
		i=0;while(s[i]!=0){
			a=s[i++];
			if( a=='#' || a=='+' ){ct++;}
			if( a=='-' ){ct--;}
			if( a>='0' && a<='9' ){co=a-'0';}
			if( a=='.' ){co=-1;}
			if( a=='<' && dd==0 ){co++;}
			if( a=='>' && dd==0 ){co--;}
		}
		da=((co+1)*12+ct-1);
	}
	if(da<0||da>127){da=-1;}
	return(da);
}
	void string_index_object_t::test<20>()
	{
		S16 value;
		std::string str_val("32767"); 
		ensure("1: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == 32767);

		str_val = "0";
		ensure("2: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == 0);

		str_val = "-32768";
		ensure("3: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == -32768);

		str_val = "32768"; 
		ensure("4: convertToS16 failed", !LLStringUtil::convertToS16(str_val, value));

		str_val = "-32769";
		ensure("5: convertToS16 failed", !LLStringUtil::convertToS16(str_val, value));
	}
	void string_index_object_t::test<19>()
	{
		S8 value;
		std::string str_val("127");
		ensure("1: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == 127);

		str_val = "0";
		ensure("2: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == 0);

		str_val = "-128";
		ensure("3: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == -128);

		str_val = "128"; // bigger than MAX_S8
		ensure("4: convertToS8 failed", !LLStringUtil::convertToS8(str_val, value));

		str_val = "-129"; 
		ensure("5: convertToS8 failed", !LLStringUtil::convertToS8(str_val, value));
	}
	void string_index_object_t::test<23>()
	{
		S32 value;
		std::string str_val("2147483647"); //0x7FFFFFFF
		ensure("1: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 2147483647);

		str_val = "0";
		ensure("2: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 0);

		// Avoid "unary minus operator applied to unsigned type" warning on VC++. JC
		S32 min_val = -2147483647 - 1;
		str_val = "-2147483648"; 
		ensure("3: convertToS32 failed", LLStringUtil::convertToS32(str_val, value)  && value == min_val);

		str_val = "2147483648"; 
		ensure("4: convertToS32 failed", !LLStringUtil::convertToS32(str_val, value));

		str_val = "-2147483649"; 
		ensure("5: convertToS32 failed", !LLStringUtil::convertToS32(str_val, value));
	}
	void string_index_object_t::test<24>()
	{
		F32 value;
		std::string str_val("2147483647"); //0x7FFFFFFF
		ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647);

		str_val = "0";
		ensure("2: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 0);

		/* Need to find max/min F32 values
		str_val = "-2147483648"; 
		ensure("3: convertToF32 failed", LLStringUtil::convertToF32(str_val, value)  && value == -2147483648);

		str_val = "2147483648"; 
		ensure("4: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));

		str_val = "-2147483649"; 
		ensure("5: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
		*/
	}
	void string_index_object_t::test<25>()
	{
		F64 value;
		std::string str_val("9223372036854775807"); //0x7FFFFFFFFFFFFFFF
		ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807LL);

		str_val = "0";
		ensure("2: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 0.0F);

		/* Need to find max/min F64 values
		str_val = "-2147483648"; 
		ensure("3: convertToF32 failed", LLStringUtil::convertToF32(str_val, value)  && value == -2147483648);

		str_val = "2147483648"; 
		ensure("4: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));

		str_val = "-2147483649"; 
		ensure("5: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
		*/
	}
Example #26
0
void
change_xtrace(unsigned char newval, bool dosnapshot)
{
	static bool in_xtrace;

	if (in_xtrace)
		return;

	if (!dosnapshot && newval == Flag(FXTRACE))
		return;

	if (Flag(FXTRACE) == 2) {
		shf_putc('\n', shl_xtrace);
		Flag(FXTRACE) = 1;
		shf_flush(shl_xtrace);
	}

	if (!dosnapshot && Flag(FXTRACE) == 1)
		switch (newval) {
		case 1:
			return;
		case 2:
			goto changed_xtrace;
		}

	shf_flush(shl_xtrace);
	if (shl_xtrace->fd != 2)
		close(shl_xtrace->fd);
	if (!newval || (shl_xtrace->fd = savefd(2)) == -1)
		shl_xtrace->fd = 2;

 changed_xtrace:
	if ((Flag(FXTRACE) = newval) == 2) {
		in_xtrace = true;
		Flag(FXTRACE) = 0;
		shf_puts(substitute(str_val(global("PS4")), 0), shl_xtrace);
		Flag(FXTRACE) = 2;
		in_xtrace = false;
	}
}
static void
scriptexec(struct op *tp, const char **ap)
{
	const char *sh;
#ifndef MKSH_SMALL
	unsigned char *cp;
	/* 64 == MAXINTERP in MirBSD <sys/param.h> */
	char buf[64];
	int fd;
#endif
	union mksh_ccphack args, cap;

	sh = str_val(global("EXECSHELL"));
	if (sh && *sh)
		sh = search_path(sh, path, X_OK, NULL);
	if (!sh || !*sh)
		sh = MKSH_DEFAULT_EXECSHELL;

	*tp->args-- = tp->str;

#ifndef MKSH_SMALL
	if ((fd = open(tp->str, O_RDONLY)) >= 0) {
		/* read first MAXINTERP octets from file */
		if (read(fd, buf, sizeof(buf)) <= 0)
			/* read error -> no good */
			buf[0] = '\0';
		close(fd);

		/* skip UTF-8 Byte Order Mark, if present */
		cp = (unsigned char *)buf;
		if ((cp[0] == 0xEF) && (cp[1] == 0xBB) && (cp[2] == 0xBF))
			cp += 3;
		/* save begin of shebang for later */
		fd = (char *)cp - buf;		/* either 0 or (if BOM) 3 */

		/* scan for newline (or CR) or NUL _before_ end of buffer */
		while ((char *)cp < (buf + sizeof(buf)))
			if (*cp == '\0' || *cp == '\n' || *cp == '\r') {
				*cp = '\0';
				break;
			} else
				++cp;
		/* if the shebang line is longer than MAXINTERP, bail out */
		if ((char *)cp >= (buf + sizeof(buf)))
			goto noshebang;

		/* restore begin of shebang position (buf+0 or buf+3) */
		cp = (unsigned char *)(buf + fd);
		/* bail out if read error (above) or no shebang */
		if ((cp[0] != '#') || (cp[1] != '!'))
			goto noshebang;

		cp += 2;
		/* skip whitespace before shell name */
		while (*cp == ' ' || *cp == '\t')
			++cp;
		/* just whitespace on the line? */
		if (*cp == '\0')
			goto noshebang;
		/* no, we actually found an interpreter name */
		sh = (char *)cp;
		/* look for end of shell/interpreter name */
		while (*cp != ' ' && *cp != '\t' && *cp != '\0')
			++cp;
		/* any arguments? */
		if (*cp) {
			*cp++ = '\0';
			/* skip spaces before arguments */
			while (*cp == ' ' || *cp == '\t')
				++cp;
			/* pass it all in ONE argument (historic reasons) */
			if (*cp)
				*tp->args-- = (char *)cp;
		}
 noshebang:
		fd = buf[0] << 8 | buf[1];
		if ((fd == /* OMAGIC */ 0407) ||
		    (fd == /* NMAGIC */ 0410) ||
		    (fd == /* ZMAGIC */ 0413) ||
		    (fd == /* QMAGIC */ 0314) ||
		    (fd == /* ECOFF_I386 */ 0x4C01) ||
		    (fd == /* ECOFF_M68K */ 0x0150 || fd == 0x5001) ||
		    (fd == /* ECOFF_SH */   0x0500 || fd == 0x0005) ||
		    (fd == 0x7F45 && buf[2] == 'L' && buf[3] == 'F') ||
		    (fd == /* "MZ" */ 0x4D5A) ||
		    (fd == /* gzip */ 0x1F8B))
			errorf("%s: not executable: magic %04X", tp->str, fd);
	}
#endif
	args.ro = tp->args;
	*args.ro = sh;

	cap.ro = ap;
	execve(args.rw[0], args.rw, cap.rw);

	/* report both the programme that was run and the bogus interpreter */
	errorf("%s: %s: %s", tp->str, sh, cstrerror(errno));
}
static int
comexec(struct op *t, struct tbl * volatile tp, const char **ap,
    volatile int flags, volatile int *xerrok)
{
	int i;
	volatile int rv = 0;
	const char *cp;
	const char **lastp;
	/* Must be static (XXX but why?) */
	static struct op texec;
	int type_flags;
	bool keepasn_ok;
	int fcflags = FC_BI|FC_FUNC|FC_PATH;
	bool bourne_function_call = false;
	struct block *l_expand, *l_assign;

	/*
	 * snag the last argument for $_ XXX not the same as AT&T ksh,
	 * which only seems to set $_ after a newline (but not in
	 * functions/dot scripts, but in interactive and script) -
	 * perhaps save last arg here and set it in shell()?.
	 */
	if (Flag(FTALKING) && *(lastp = ap)) {
		while (*++lastp)
			;
		/* setstr() can't fail here */
		setstr(typeset("_", LOCAL, 0, INTEGER, 0), *--lastp,
		    KSH_RETURN_ERROR);
	}

	/**
	 * Deal with the shell builtins builtin, exec and command since
	 * they can be followed by other commands. This must be done before
	 * we know if we should create a local block which must be done
	 * before we can do a path search (in case the assignments change
	 * PATH).
	 * Odd cases:
	 *	FOO=bar exec >/dev/null		FOO is kept but not exported
	 *	FOO=bar exec foobar		FOO is exported
	 *	FOO=bar command exec >/dev/null	FOO is neither kept nor exported
	 *	FOO=bar command			FOO is neither kept nor exported
	 *	PATH=... foobar			use new PATH in foobar search
	 */
	keepasn_ok = true;
	while (tp && tp->type == CSHELL) {
		/* undo effects of command */
		fcflags = FC_BI|FC_FUNC|FC_PATH;
		if (tp->val.f == c_builtin) {
			if ((cp = *++ap) == NULL ||
			    (!strcmp(cp, "--") && (cp = *++ap) == NULL)) {
				tp = NULL;
				break;
			}
			if ((tp = findcom(cp, FC_BI)) == NULL)
				errorf("%s: %s: %s", Tbuiltin, cp, "not a builtin");
			continue;
		} else if (tp->val.f == c_exec) {
			if (ap[1] == NULL)
				break;
			ap++;
			flags |= XEXEC;
		} else if (tp->val.f == c_command) {
			int optc, saw_p = 0;

			/*
			 * Ugly dealing with options in two places (here
			 * and in c_command(), but such is life)
			 */
			ksh_getopt_reset(&builtin_opt, 0);
			while ((optc = ksh_getopt(ap, &builtin_opt, ":p")) == 'p')
				saw_p = 1;
			if (optc != EOF)
				/* command -vV or something */
				break;
			/* don't look for functions */
			fcflags = FC_BI|FC_PATH;
			if (saw_p) {
				if (Flag(FRESTRICTED)) {
					warningf(true, "%s: %s",
					    "command -p", "restricted");
					rv = 1;
					goto Leave;
				}
				fcflags |= FC_DEFPATH;
			}
			ap += builtin_opt.optind;
			/*
			 * POSIX says special builtins lose their status
			 * if accessed using command.
			 */
			keepasn_ok = false;
			if (!ap[0]) {
				/* ensure command with no args exits with 0 */
				subst_exstat = 0;
				break;
			}
#ifndef MKSH_NO_EXTERNAL_CAT
		} else if (tp->val.f == c_cat) {
			/*
			 * if we have any flags, do not use the builtin
			 * in theory, we could allow -u, but that would
			 * mean to use ksh_getopt here and possibly ad-
			 * ded complexity and more code and isn't worth
			 * additional hassle (and the builtin must call
			 * ksh_getopt already but can't come back here)
			 */
			if (ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' &&
			    /* argument, begins with -, is not - or -- */
			    (ap[1][1] != '-' || ap[1][2] != '\0'))
				/* don't look for builtins or functions */
				fcflags = FC_PATH;
			else
				/* go on, use the builtin */
				break;
#endif
#if !defined(MKSH_SMALL)
		} else if (tp->val.f == c_trap) {
			t->u.evalflags &= ~DOTCOMEXEC;
			break;
#endif
		} else
			break;
		tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC));
	}
#if !defined(MKSH_SMALL)
	if (t->u.evalflags & DOTCOMEXEC)
		flags |= XEXEC;
#endif
	l_expand = e->loc;
	if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN))))
		type_flags = 0;
	else {
		/* create new variable/function block */
		newblock();
		/* ksh functions don't keep assignments, POSIX functions do. */
		if (keepasn_ok && tp && tp->type == CFUNC &&
		    !(tp->flag & FKSH)) {
			bourne_function_call = true;
			type_flags = EXPORT;
		} else
			type_flags = LOCAL|LOCAL_COPY|EXPORT;
	}
	l_assign = e->loc;
	if (Flag(FEXPORT))
		type_flags |= EXPORT;
	for (i = 0; t->vars[i]; i++) {
		/* do NOT lookup in the new var/fn block just created */
		e->loc = l_expand;
		cp = evalstr(t->vars[i], DOASNTILDE);
		e->loc = l_assign;
		/* but assign in there as usual */

		if (Flag(FXTRACE)) {
			if (i == 0)
				shf_puts(substitute(str_val(global("PS4")), 0),
				    shl_out);
			shf_fprintf(shl_out, "%s%c", cp,
			    t->vars[i + 1] ? ' ' : '\n');
			if (!t->vars[i + 1])
				shf_flush(shl_out);
		}
		typeset(cp, type_flags, 0, 0, 0);
		if (bourne_function_call && !(type_flags & EXPORT))
			typeset(cp, LOCAL|LOCAL_COPY|EXPORT, 0, 0, 0);
	}

	if ((cp = *ap) == NULL) {
		rv = subst_exstat;
		goto Leave;
	} else if (!tp) {
		if (Flag(FRESTRICTED) && vstrchr(cp, '/')) {
			warningf(true, "%s: %s", cp, "restricted");
			rv = 1;
			goto Leave;
		}
		tp = findcom(cp, fcflags);
	}

	switch (tp->type) {

	/* shell built-in */
	case CSHELL:
		rv = call_builtin(tp, (const char **)ap, null);
		if (!keepasn_ok && tp->val.f == c_shift) {
			l_expand->argc = l_assign->argc;
			l_expand->argv = l_assign->argv;
		}
		break;

	/* function call */
	case CFUNC: {
		volatile unsigned char old_xflag;
		volatile uint32_t old_inuse;
		const char * volatile old_kshname;

		if (!(tp->flag & ISSET)) {
			struct tbl *ftp;

			if (!tp->u.fpath) {
				rv = (tp->u2.errnov == ENOENT) ? 127 : 126;
				warningf(true, "%s: %s %s: %s", cp,
				    "can't find", "function definition file",
				    cstrerror(tp->u2.errnov));
				break;
			}
			if (include(tp->u.fpath, 0, NULL, false) < 0) {
				rv = errno;
				warningf(true, "%s: %s %s %s: %s", cp,
				    "can't open", "function definition file",
				    tp->u.fpath, cstrerror(rv));
				rv = 127;
				break;
			}
			if (!(ftp = findfunc(cp, hash(cp), false)) ||
			    !(ftp->flag & ISSET)) {
				warningf(true, "%s: %s %s", cp,
				    "function not defined by", tp->u.fpath);
				rv = 127;
				break;
			}
			tp = ftp;
		}

		/*
		 * ksh functions set $0 to function name, POSIX
		 * functions leave $0 unchanged.
		 */
		old_kshname = kshname;
		if (tp->flag & FKSH)
			kshname = ap[0];
		else
			ap[0] = kshname;
		e->loc->argv = ap;
		for (i = 0; *ap++ != NULL; i++)
			;
		e->loc->argc = i - 1;
		/*
		 * ksh-style functions handle getopts sanely,
		 * Bourne/POSIX functions are insane...
		 */
		if (tp->flag & FKSH) {
			e->loc->flags |= BF_DOGETOPTS;
			e->loc->getopts_state = user_opt;
			getopts_reset(1);
		}

		old_xflag = Flag(FXTRACE);
		Flag(FXTRACE) |= tp->flag & TRACE ? 1 : 0;

		old_inuse = tp->flag & FINUSE;
		tp->flag |= FINUSE;

		e->type = E_FUNC;
		if (!(i = kshsetjmp(e->jbuf))) {
			execute(tp->val.t, flags & XERROK, NULL);
			i = LRETURN;
		}
		kshname = old_kshname;
		Flag(FXTRACE) = old_xflag;
		tp->flag = (tp->flag & ~FINUSE) | old_inuse;
		/*
		 * Were we deleted while executing? If so, free the
		 * execution tree. TODO: Unfortunately, the table entry
		 * is never re-used until the lookup table is expanded.
		 */
		if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) {
			if (tp->flag & ALLOC) {
				tp->flag &= ~ALLOC;
				tfree(tp->val.t, tp->areap);
			}
			tp->flag = 0;
		}
		switch (i) {
		case LRETURN:
		case LERROR:
			rv = exstat & 0xFF;
			break;
		case LINTR:
		case LEXIT:
		case LLEAVE:
		case LSHELL:
			quitenv(NULL);
			unwind(i);
			/* NOTREACHED */
		default:
			quitenv(NULL);
			internal_errorf("%s %d", "CFUNC", i);
		}
		break;
	}

	/* executable command */
	case CEXEC:
	/* tracked alias */
	case CTALIAS:
		if (!(tp->flag&ISSET)) {
			if (tp->u2.errnov == ENOENT) {
				rv = 127;
				warningf(true, "%s: %s", cp, "not found");
			} else {
				rv = 126;
				warningf(true, "%s: %s: %s", cp, "can't execute",
				    cstrerror(tp->u2.errnov));
			}
			break;
		}

		/* set $_ to programme's full path */
		/* setstr() can't fail here */
		setstr(typeset("_", LOCAL|EXPORT, 0, INTEGER, 0),
		    tp->val.s, KSH_RETURN_ERROR);

		if (flags&XEXEC) {
			j_exit();
			if (!(flags&XBGND)
#ifndef MKSH_UNEMPLOYED
			    || Flag(FMONITOR)
#endif
			    ) {
				setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG);
				setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG);
			}
		}

		/* to fork we set up a TEXEC node and call execute */
		texec.type = TEXEC;
		/* for tprint */
		texec.left = t;
		texec.str = tp->val.s;
		texec.args = ap;
		rv = exchild(&texec, flags, xerrok, -1);
		break;
	}
 Leave:
	if (flags & XEXEC) {
		exstat = rv & 0xFF;
		unwind(LLEAVE);
	}
	return (rv);
}
/*
 * execute command tree
 */
int
execute(struct op * volatile t,
    /* if XEXEC don't fork */
    volatile int flags,
    volatile int * volatile xerrok)
{
	int i;
	volatile int rv = 0, dummy = 0;
	int pv[2];
	const char ** volatile ap = NULL;
	char ** volatile up;
	const char *s, *ccp;
	struct ioword **iowp;
	struct tbl *tp = NULL;
	char *cp;

	if (t == NULL)
		return (0);

	/* Caller doesn't care if XERROK should propagate. */
	if (xerrok == NULL)
		xerrok = &dummy;

	if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE)
		/* run in sub-process */
		return (exchild(t, flags & ~XTIME, xerrok, -1));

	newenv(E_EXEC);
	if (trap)
		runtraps(0);

	/* we want to run an executable, do some variance checks */
	if (t->type == TCOM) {
		/* check if this is 'var=<<EOF' */
		if (
		    /* we have zero arguments, i.e. no programme to run */
		    t->args[0] == NULL &&
		    /* we have exactly one variable assignment */
		    t->vars[0] != NULL && t->vars[1] == NULL &&
		    /* we have exactly one I/O redirection */
		    t->ioact != NULL && t->ioact[0] != NULL &&
		    t->ioact[1] == NULL &&
		    /* of type "here document" (or "here string") */
		    (t->ioact[0]->flag & IOTYPE) == IOHERE &&
		    /* the variable assignment begins with a valid varname */
		    (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] &&
		    /* and has no right-hand side (i.e. "varname=") */
		    ccp[0] == CHAR && ccp[1] == '=' && ccp[2] == EOS &&
		    /* plus we can have a here document content */
		    herein(t->ioact[0], &cp) == 0 && cp && *cp) {
			char *sp = cp, *dp;
			size_t n = ccp - t->vars[0] + 2, z;

			/* drop redirection (will be garbage collected) */
			t->ioact = NULL;

			/* set variable to its expanded value */
			z = strlen(cp) + 1;
			if (notoktomul(z, 2) || notoktoadd(z * 2, n))
				internal_errorf(Toomem, (unsigned long)-1);
			dp = alloc(z * 2 + n, ATEMP);
			memcpy(dp, t->vars[0], n);
			t->vars[0] = dp;
			dp += n;
			while (*sp) {
				*dp++ = QCHAR;
				*dp++ = *sp++;
			}
			*dp = EOS;
			/* free the expanded value */
			afree(cp, APERM);
		}

		/*
		 * Clear subst_exstat before argument expansion. Used by
		 * null commands (see comexec() and c_eval()) and by c_set().
		 */
		subst_exstat = 0;

		/* for $LINENO */
		current_lineno = t->lineno;

		/*
		 * POSIX says expand command words first, then redirections,
		 * and assignments last..
		 */
		up = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE);
		if (flags & XTIME)
			/* Allow option parsing (bizarre, but POSIX) */
			timex_hook(t, &up);
		ap = (const char **)up;
		if (Flag(FXTRACE) && ap[0]) {
			shf_puts(substitute(str_val(global("PS4")), 0),
			    shl_out);
			for (i = 0; ap[i]; i++)
				shf_fprintf(shl_out, "%s%c", ap[i],
				    ap[i + 1] ? ' ' : '\n');
			shf_flush(shl_out);
		}
		if (ap[0])
			tp = findcom(ap[0], FC_BI|FC_FUNC);
	}
	flags &= ~XTIME;

	if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) {
		e->savefd = alloc2(NUFILE, sizeof(short), ATEMP);
		/* initialise to not redirected */
		memset(e->savefd, 0, NUFILE * sizeof(short));
	}

	/* mark for replacement later (unless TPIPE) */
	vp_pipest->flag |= INT_L;

	/* do redirection, to be restored in quitenv() */
	if (t->ioact != NULL)
		for (iowp = t->ioact; *iowp != NULL; iowp++) {
			if (iosetup(*iowp, tp) < 0) {
				exstat = rv = 1;
				/*
				 * Redirection failures for special commands
				 * cause (non-interactive) shell to exit.
				 */
				if (tp && tp->type == CSHELL &&
				    (tp->flag & SPEC_BI))
					errorfz();
				/* Deal with FERREXIT, quitenv(), etc. */
				goto Break;
			}
		}

	switch (t->type) {
	case TCOM:
		rv = comexec(t, tp, (const char **)ap, flags, xerrok);
		break;

	case TPAREN:
		rv = execute(t->left, flags | XFORK, xerrok);
		break;

	case TPIPE:
		flags |= XFORK;
		flags &= ~XEXEC;
		e->savefd[0] = savefd(0);
		e->savefd[1] = savefd(1);
		while (t->type == TPIPE) {
			openpipe(pv);
			/* stdout of curr */
			ksh_dup2(pv[1], 1, false);
			/**
			 * Let exchild() close pv[0] in child
			 * (if this isn't done, commands like
			 *	(: ; cat /etc/termcap) | sleep 1
			 * will hang forever).
			 */
			exchild(t->left, flags | XPIPEO | XCCLOSE,
			    NULL, pv[0]);
			/* stdin of next */
			ksh_dup2(pv[0], 0, false);
			closepipe(pv);
			flags |= XPIPEI;
			t = t->right;
		}
		/* stdout of last */
		restfd(1, e->savefd[1]);
		/* no need to re-restore this */
		e->savefd[1] = 0;
		/* Let exchild() close 0 in parent, after fork, before wait */
		i = exchild(t, flags | XPCLOSE | XPIPEST, xerrok, 0);
		if (!(flags&XBGND) && !(flags&XXCOM))
			rv = i;
		break;

	case TLIST:
		while (t->type == TLIST) {
			execute(t->left, flags & XERROK, NULL);
			t = t->right;
		}
		rv = execute(t, flags & XERROK, xerrok);
		break;

	case TCOPROC: {
#ifndef MKSH_NOPROSPECTOFWORK
		sigset_t omask;

		/*
		 * Block sigchild as we are using things changed in the
		 * signal handler
		 */
		sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
		e->type = E_ERRH;
		if ((i = kshsetjmp(e->jbuf))) {
			sigprocmask(SIG_SETMASK, &omask, NULL);
			quitenv(NULL);
			unwind(i);
			/* NOTREACHED */
		}
#endif
		/* Already have a (live) co-process? */
		if (coproc.job && coproc.write >= 0)
			errorf("coprocess already exists");

		/* Can we re-use the existing co-process pipe? */
		coproc_cleanup(true);

		/* do this before opening pipes, in case these fail */
		e->savefd[0] = savefd(0);
		e->savefd[1] = savefd(1);

		openpipe(pv);
		if (pv[0] != 0) {
			ksh_dup2(pv[0], 0, false);
			close(pv[0]);
		}
		coproc.write = pv[1];
		coproc.job = NULL;

		if (coproc.readw >= 0)
			ksh_dup2(coproc.readw, 1, false);
		else {
			openpipe(pv);
			coproc.read = pv[0];
			ksh_dup2(pv[1], 1, false);
			/* closed before first read */
			coproc.readw = pv[1];
			coproc.njobs = 0;
			/* create new coprocess id */
			++coproc.id;
		}
#ifndef MKSH_NOPROSPECTOFWORK
		sigprocmask(SIG_SETMASK, &omask, NULL);
		/* no more need for error handler */
		e->type = E_EXEC;
#endif

		/*
		 * exchild() closes coproc.* in child after fork,
		 * will also increment coproc.njobs when the
		 * job is actually created.
		 */
		flags &= ~XEXEC;
		exchild(t->left, flags | XBGND | XFORK | XCOPROC | XCCLOSE,
		    NULL, coproc.readw);
		break;
	}

	case TASYNC:
		/*
		 * XXX non-optimal, I think - "(foo &)", forks for (),
		 * forks again for async... parent should optimise
		 * this to "foo &"...
		 */
		rv = execute(t->left, (flags&~XEXEC)|XBGND|XFORK, xerrok);
		break;

	case TOR:
	case TAND:
		rv = execute(t->left, XERROK, xerrok);
		if ((rv == 0) == (t->type == TAND))
			rv = execute(t->right, XERROK, xerrok);
		flags |= XERROK;
		if (xerrok)
			*xerrok = 1;
		break;

	case TBANG:
		rv = !execute(t->right, XERROK, xerrok);
		flags |= XERROK;
		if (xerrok)
			*xerrok = 1;
		break;

	case TDBRACKET: {
		Test_env te;

		te.flags = TEF_DBRACKET;
		te.pos.wp = t->args;
		te.isa = dbteste_isa;
		te.getopnd = dbteste_getopnd;
		te.eval = test_eval;
		te.error = dbteste_error;

		rv = test_parse(&te);
		break;
	}

	case TFOR:
	case TSELECT: {
		volatile bool is_first = true;
		ap = (t->vars == NULL) ? e->loc->argv + 1 :
		    (const char **)eval((const char **)t->vars,
		    DOBLANK | DOGLOB | DOTILDE);
		e->type = E_LOOP;
		while ((i = kshsetjmp(e->jbuf))) {
			if ((e->flags&EF_BRKCONT_PASS) ||
			    (i != LBREAK && i != LCONTIN)) {
				quitenv(NULL);
				unwind(i);
			} else if (i == LBREAK) {
				rv = 0;
				goto Break;
			}
		}
		/* in case of a continue */
		rv = 0;
		if (t->type == TFOR) {
			while (*ap != NULL) {
				setstr(global(t->str), *ap++, KSH_UNWIND_ERROR);
				rv = execute(t->left, flags & XERROK, xerrok);
			}
		} else {
			/* TSELECT */
			for (;;) {
				if (!(ccp = do_selectargs(ap, is_first))) {
					rv = 1;
					break;
				}
				is_first = false;
				setstr(global(t->str), ccp, KSH_UNWIND_ERROR);
				execute(t->left, flags & XERROK, xerrok);
			}
		}
		break;
	}

	case TWHILE:
	case TUNTIL:
		e->type = E_LOOP;
		while ((i = kshsetjmp(e->jbuf))) {
			if ((e->flags&EF_BRKCONT_PASS) ||
			    (i != LBREAK && i != LCONTIN)) {
				quitenv(NULL);
				unwind(i);
			} else if (i == LBREAK) {
				rv = 0;
				goto Break;
			}
		}
		/* in case of a continue */
		rv = 0;
		while ((execute(t->left, XERROK, NULL) == 0) ==
		    (t->type == TWHILE))
			rv = execute(t->right, flags & XERROK, xerrok);
		break;

	case TIF:
	case TELIF:
		if (t->right == NULL)
			/* should be error */
			break;
		rv = execute(t->left, XERROK, NULL) == 0 ?
		    execute(t->right->left, flags & XERROK, xerrok) :
		    execute(t->right->right, flags & XERROK, xerrok);
		break;

	case TCASE:
		i = 0;
		ccp = evalstr(t->str, DOTILDE);
		for (t = t->left; t != NULL && t->type == TPAT; t = t->right) {
			for (ap = (const char **)t->vars; *ap; ap++) {
				if (i || ((s = evalstr(*ap, DOTILDE|DOPAT)) &&
				    gmatchx(ccp, s, false))) {
					rv = execute(t->left, flags & XERROK,
					    xerrok);
					i = 0;
					switch (t->u.charflag) {
					case '&':
						i = 1;
						/* FALLTHROUGH */
					case '|':
						goto TCASE_next;
					}
					goto TCASE_out;
				}
			}
			i = 0;
 TCASE_next:
			/* empty */;
		}
 TCASE_out:
		break;

	case TBRACE:
		rv = execute(t->left, flags & XERROK, xerrok);
		break;

	case TFUNCT:
		rv = define(t->str, t);
		break;

	case TTIME:
		/*
		 * Clear XEXEC so nested execute() call doesn't exit
		 * (allows "ls -l | time grep foo").
		 */
		rv = timex(t, flags & ~XEXEC, xerrok);
		break;

	case TEXEC:
		/* an eval'd TCOM */
		s = t->args[0];
		up = makenv();
		restoresigs();
		cleanup_proc_env();
		{
			union mksh_ccphack cargs;

			cargs.ro = t->args;
			execve(t->str, cargs.rw, up);
			rv = errno;
		}
		if (rv == ENOEXEC)
			scriptexec(t, (const char **)up);
		else
			errorf("%s: %s", s, cstrerror(rv));
	}
 Break:
	exstat = rv & 0xFF;
	if (vp_pipest->flag & INT_L) {
		unset(vp_pipest, 1);
		vp_pipest->flag = DEFINED | ISSET | INTEGER | RDONLY |
		    ARRAY | INT_U;
		vp_pipest->val.i = rv;
	}

	/* restores IO */
	quitenv(NULL);
	if ((flags&XEXEC))
		/* exit child */
		unwind(LEXIT);
	if (rv != 0 && !(flags & XERROK) &&
	    (xerrok == NULL || !*xerrok)) {
		if (Flag(FERREXIT) & 0x80) {
			/* inside eval */
			Flag(FERREXIT) = 0;
		} else {
			trapsig(ksh_SIGERR);
			if (Flag(FERREXIT))
				unwind(LERROR);
		}
	}
	return (rv);
}
/*
 * set up redirection, saving old fds in e->savefd
 */
static int
iosetup(struct ioword *iop, struct tbl *tp)
{
	int u = -1;
	char *cp = iop->name;
	int iotype = iop->flag & IOTYPE;
	bool do_open = true, do_close = false;
	int flags = 0;
	struct ioword iotmp;
	struct stat statb;

	if (iotype != IOHERE)
		cp = evalonestr(cp, DOTILDE|(Flag(FTALKING_I) ? DOGLOB : 0));

	/* Used for tracing and error messages to print expanded cp */
	iotmp = *iop;
	iotmp.name = (iotype == IOHERE) ? NULL : cp;
	iotmp.flag |= IONAMEXP;

	if (Flag(FXTRACE))
		shellf("%s%s\n",
		    substitute(str_val(global("PS4")), 0),
		    snptreef(NULL, 32, "%R", &iotmp));

	switch (iotype) {
	case IOREAD:
		flags = O_RDONLY;
		break;

	case IOCAT:
		flags = O_WRONLY | O_APPEND | O_CREAT;
		break;

	case IOWRITE:
		flags = O_WRONLY | O_CREAT | O_TRUNC;
		/*
		 * The stat() is here to allow redirections to
		 * things like /dev/null without error.
		 */
		if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB) &&
		    (stat(cp, &statb) < 0 || S_ISREG(statb.st_mode)))
			flags |= O_EXCL;
		break;

	case IORDWR:
		flags = O_RDWR | O_CREAT;
		break;

	case IOHERE:
		do_open = false;
		/* herein() returns -2 if error has been printed */
		u = herein(iop, NULL);
		/* cp may have wrong name */
		break;

	case IODUP: {
		const char *emsg;

		do_open = false;
		if (*cp == '-' && !cp[1]) {
			/* prevent error return below */
			u = 1009;
			do_close = true;
		} else if ((u = check_fd(cp,
		    X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK),
		    &emsg)) < 0) {
			warningf(true, "%s: %s",
			    snptreef(NULL, 32, "%R", &iotmp), emsg);
			return (-1);
		}
		if (u == iop->unit)
			/* "dup from" == "dup to" */
			return (0);
		break;
	    }
	}

	if (do_open) {
		if (Flag(FRESTRICTED) && (flags & O_CREAT)) {
			warningf(true, "%s: %s", cp, "restricted");
			return (-1);
		}
		u = open(cp, flags, 0666);
	}
	if (u < 0) {
		/* herein() may already have printed message */
		if (u == -1) {
			u = errno;
			warningf(true, "can't %s %s: %s",
			    iotype == IODUP ? "dup" :
			    (iotype == IOREAD || iotype == IOHERE) ?
			    "open" : "create", cp, cstrerror(u));
		}
		return (-1);
	}
	/* Do not save if it has already been redirected (i.e. "cat >x >y"). */
	if (e->savefd[iop->unit] == 0) {
		/* If these are the same, it means unit was previously closed */
		if (u == iop->unit)
			e->savefd[iop->unit] = -1;
		else
			/*
			 * c_exec() assumes e->savefd[fd] set for any
			 * redirections. Ask savefd() not to close iop->unit;
			 * this allows error messages to be seen if iop->unit
			 * is 2; also means we can't lose the fd (eg, both
			 * dup2 below and dup2 in restfd() failing).
			 */
			e->savefd[iop->unit] = savefd(iop->unit);
	}

	if (do_close)
		close(iop->unit);
	else if (u != iop->unit) {
		if (ksh_dup2(u, iop->unit, true) < 0) {
			int eno;

			eno = errno;
			warningf(true, "%s %s %s",
			    "can't finish (dup) redirection",
			    snptreef(NULL, 32, "%R", &iotmp),
			    cstrerror(eno));
			if (iotype != IODUP)
				close(u);
			return (-1);
		}
		if (iotype != IODUP)
			close(u);
		/*
		 * Touching any co-process fd in an empty exec
		 * causes the shell to close its copies
		 */
		else if (tp && tp->type == CSHELL && tp->val.f == c_exec) {
			if (iop->flag & IORDUP)
				/* possible exec <&p */
				coproc_read_close(u);
			else
				/* possible exec >&p */
				coproc_write_close(u);
		}
	}
	if (u == 2)
		/* Clear any write errors */
		shf_reopen(2, SHF_WR, shl_out);
	return (0);
}