Ejemplo n.º 1
0
/* Used to calculate an array index for global()/local().  Sets *arrayp to
 * non-zero if this is an array, sets *valp to the array index, returns
 * the basename of the array.
 */
static const char *
array_index_calc(const char *n, bool *arrayp, int *valp)
{
	const char *p;
	int len;

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

		/* Calculate the value of the subscript */
		*arrayp = true;
		tmp = str_nsave(p+1, len-2, ATEMP);
		sub = substitute(tmp, 0);
		afree(tmp, ATEMP);
		n = str_nsave(n, p - n, ATEMP);
		evaluate(sub, &rval, KSH_UNWIND_ERROR, true);
		if (rval < 0 || rval > ARRAYMAX)
			errorf("%s: subscript %ld out of range", n, rval);
		*valp = rval;
		afree(sub, ATEMP);
	}
	return n;
}
Ejemplo n.º 2
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);
}
Ejemplo n.º 3
0
Archivo: misc.c Proyecto: adtools/abcsh
/* parse command line & set command arguments.  returns the index of
 * non-option arguments, -1 if there is an error.
 */
int
parse_args(char **argv,
        int     what,           /* OF_CMDLINE or OF_SET */
        int     *setargsp)
{
        static char cmd_opts[NELEM(options) + 3]; /* o:\0 */
        static char set_opts[NELEM(options) + 5]; /* Ao;s\0 */
        char *opts;
        char *array = (char *) 0;
        Getopt go;
        int i, optc, set, sortargs = 0, arrayset = 0;

        /* First call?  Build option strings... */
        if (cmd_opts[0] == '\0') {
                char *p, *q;

                /* see cmd_opts[] declaration */
                strlcpy(cmd_opts, "o:", sizeof cmd_opts);
                p = cmd_opts + strlen(cmd_opts);
                /* see set_opts[] declaration */
                strlcpy(set_opts, "A:o;s", sizeof set_opts);
                q = set_opts + strlen(set_opts);
                for (i = 0; i < NELEM(options); i++) {
                        if (options[i].c) {
                                if (options[i].flags & OF_CMDLINE)
                                        *p++ = options[i].c;
                                if (options[i].flags & OF_SET)
                                        *q++ = options[i].c;
                        }
                }
                *p = '\0';
                *q = '\0';
        }

        if (what == OF_CMDLINE) {
                opts = cmd_opts;
        } else
                opts = set_opts;
        ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
        while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
                set = (go.info & GI_PLUS) ? 0 : 1;
                switch (optc) {
                  case 'A':
                        arrayset = set ? 1 : -1;
                        array = go.optarg;
                        break;

                  case 'o':
                        if (go.optarg == (char *) 0) {
                                /* lone -o: print options
                                 *
                                 * Note that on the command line, -o requires
                                 * an option (ie, can't get here if what is
                                 * OF_CMDLINE).
                                 */
                                printoptions(set);
                                break;
                        }
                        i = option(go.optarg);
                        if (i >= 0 && set == Flag(i))
                                /* Don't check the context if the flag
                                 * isn't changing - makes "set -o interactive"
                                 * work if you're already interactive.  Needed
                                 * if the output of "set +o" is to be used.
                                 */
                                ;
                        else if (i >= 0 && (options[i].flags & what))
                                change_flag((enum sh_flag) i, what, set);
                        else {
                                bi_errorf("%s: bad option", go.optarg);
                                return -1;
                        }
                        break;

                  case '?':
                        return -1;

                  default:
                        /* -s: sort positional params (at&t ksh stupidity) */
                        if (what == OF_SET && optc == 's') {
                                sortargs = 1;
                                break;
                        }
                        for (i = 0; i < NELEM(options); i++)
                                if (optc == options[i].c
                                    && (what & options[i].flags))
                                {
                                        change_flag((enum sh_flag) i, what,
                                                set);
                                        break;
                                }
                        if (i == NELEM(options)) {
                                internal_errorf(1, "parse_args: `%c'", optc);
                                return -1; /* not reached */
                        }
                }
        }
        if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
                (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') &&
                argv[go.optind][1] == '\0')
        {
                /* lone - clears -v and -x flags */
                if (argv[go.optind][0] == '-' && !Flag(FPOSIX))
                        Flag(FVERBOSE) = Flag(FXTRACE) = 0;
                /* set skips lone - or + option */
                go.optind++;
        }
        if (setargsp)
                /* -- means set $#/$* even if there are no arguments */
                *setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
                        argv[go.optind]);

        if (arrayset && (!*array || *skip_varname(array, false))) {
                bi_errorf("%s: is not an identifier", array);
                return -1;
        }
        if (sortargs) {
                for (i = go.optind; argv[i]; i++)
                        ;
                qsortp((void **) &argv[go.optind], (size_t) (i - go.optind),
                        xstrcmp);
        }
        if (arrayset) {
                set_array(array, arrayset, argv + go.optind);
                for (; argv[go.optind]; go.optind++)
                        ;
        }

        return go.optind;
}
Ejemplo n.º 4
0
/* Parse command line & set command arguments. Returns the index of
 * non-option arguments, -1 if there is an error.
 */
int
parse_args(const char **argv,
    int what,			/* OF_CMDLINE or OF_SET */
    bool *setargsp)
{
	static char cmd_opts[NELEM(options) + 5]; /* o:T:\0 */
	static char set_opts[NELEM(options) + 6]; /* A:o;s\0 */
	char set, *opts;
	const char *array = NULL;
	Getopt go;
	size_t i;
	int optc, sortargs = 0, arrayset = 0;

	/* First call? Build option strings... */
	if (cmd_opts[0] == '\0') {
		char *p = cmd_opts, *q = set_opts;

		/* see cmd_opts[] declaration */
		*p++ = 'o';
		*p++ = ':';
#if !defined(MKSH_SMALL) || defined(TIOCSCTTY)
		*p++ = 'T';
		*p++ = ':';
#endif
		/* see set_opts[] declaration */
		*q++ = 'A';
		*q++ = ':';
		*q++ = 'o';
		*q++ = ';';
		*q++ = 's';

		for (i = 0; i < NELEM(options); i++) {
			if (options[i].c) {
				if (options[i].flags & OF_CMDLINE)
					*p++ = options[i].c;
				if (options[i].flags & OF_SET)
					*q++ = options[i].c;
			}
		}
		*p = '\0';
		*q = '\0';
	}

	if (what == OF_CMDLINE) {
		const char *p = argv[0], *q;
		/* Set FLOGIN before parsing options so user can clear
		 * flag using +l.
		 */
		if (*p != '-')
			for (q = p; *q; )
				if (*q++ == '/')
					p = q;
		Flag(FLOGIN) = (*p == '-');
		opts = cmd_opts;
	} else if (what == OF_FIRSTTIME) {
		opts = cmd_opts;
	} else
		opts = set_opts;
	ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
	while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
		set = (go.info & GI_PLUS) ? 0 : 1;
		switch (optc) {
		case 'A':
			if (what == OF_FIRSTTIME)
				break;
			arrayset = set ? 1 : -1;
			array = go.optarg;
			break;

		case 'o':
			if (what == OF_FIRSTTIME)
				break;
			if (go.optarg == NULL) {
				/* lone -o: print options
				 *
				 * Note that on the command line, -o requires
				 * an option (ie, can't get here if what is
				 * OF_CMDLINE).
				 */
				printoptions(set);
				break;
			}
			i = option(go.optarg);
			if ((enum sh_flag)i == FARC4RANDOM) {
				warningf(true, "Do not use set ±o arc4random,"
				    " it will be removed in the next version"
				    " of mksh!");
				return (0);
			}
			if ((i != (size_t)-1) && set == Flag(i))
				/* Don't check the context if the flag
				 * isn't changing - makes "set -o interactive"
				 * work if you're already interactive. Needed
				 * if the output of "set +o" is to be used.
				 */
				;
			else if ((i != (size_t)-1) && (options[i].flags & what))
				change_flag((enum sh_flag)i, what, set);
			else {
				bi_errorf("%s: bad option", go.optarg);
				return (-1);
			}
			break;

#if !defined(MKSH_SMALL) || defined(TIOCSCTTY)
		case 'T':
			if (what != OF_FIRSTTIME)
				break;
#ifndef TIOCSCTTY
			errorf("no TIOCSCTTY ioctl");
#else
			change_flag(FTALKING, OF_CMDLINE, 1);
			chvt(go.optarg);
			break;
#endif
#endif

		case '?':
			return (-1);

		default:
			if (what == OF_FIRSTTIME)
				break;
			/* -s: sort positional params (AT&T ksh stupidity) */
			if (what == OF_SET && optc == 's') {
				sortargs = 1;
				break;
			}
			for (i = 0; i < NELEM(options); i++)
				if (optc == options[i].c &&
				    (what & options[i].flags)) {
					change_flag((enum sh_flag)i, what, set);
					break;
				}
			if (i == NELEM(options))
				internal_errorf("parse_args: '%c'", optc);
		}
	}
	if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
	    (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') &&
	    argv[go.optind][1] == '\0') {
		/* lone - clears -v and -x flags */
		if (argv[go.optind][0] == '-')
			Flag(FVERBOSE) = Flag(FXTRACE) = 0;
		/* set skips lone - or + option */
		go.optind++;
	}
	if (setargsp)
		/* -- means set $#/$* even if there are no arguments */
		*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
		    argv[go.optind]);

	if (arrayset && (!*array || *skip_varname(array, false))) {
		bi_errorf("%s: is not an identifier", array);
		return (-1);
	}
	if (sortargs) {
		for (i = go.optind; argv[i]; i++)
			;
		qsort(&argv[go.optind], i - go.optind, sizeof(void *),
		    xstrcmp);
	}
	if (arrayset)
		go.optind += set_array(array, arrayset > 0 ? true : false,
		    argv + go.optind);

	return (go.optind);
}
Ejemplo n.º 5
0
/*
 * Parse command line and set command arguments. Returns the index of
 * non-option arguments, -1 if there is an error.
 */
int
parse_args(const char **argv,
    /* OF_CMDLINE or OF_SET */
    int what,
    bool *setargsp)
{
	static const char cmd_opts[] =
#define SHFLAGS_NOT_SET
#define SHFLAGS_OPTCS
#include "sh_flags.gen"
#undef SHFLAGS_NOT_SET
	    ;
	static const char set_opts[] =
#define SHFLAGS_NOT_CMD
#define SHFLAGS_OPTCS
#include "sh_flags.gen"
#undef SHFLAGS_NOT_CMD
	    ;
	bool set;
	const char *opts;
	const char *array = NULL;
	Getopt go;
	size_t i;
	int optc, arrayset = 0;
	bool sortargs = false;
	bool fcompatseen = false;

	if (what == OF_CMDLINE) {
		const char *p = argv[0], *q;
		/*
		 * Set FLOGIN before parsing options so user can clear
		 * flag using +l.
		 */
		if (*p != '-')
			for (q = p; *q; )
				if (*q++ == '/')
					p = q;
		Flag(FLOGIN) = (*p == '-');
		opts = cmd_opts;
	} else if (what == OF_FIRSTTIME) {
		opts = cmd_opts;
	} else
		opts = set_opts;
	ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
	while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
		set = tobool(!(go.info & GI_PLUS));
		switch (optc) {
		case 'A':
			if (what == OF_FIRSTTIME)
				break;
			arrayset = set ? 1 : -1;
			array = go.optarg;
			break;

		case 'o':
			if (what == OF_FIRSTTIME)
				break;
			if (go.optarg == NULL) {
				/*
				 * lone -o: print options
				 *
				 * Note that on the command line, -o requires
				 * an option (ie, can't get here if what is
				 * OF_CMDLINE).
				 */
				printoptions(set);
				break;
			}
			i = option(go.optarg);
			if ((i == FPOSIX || i == FSH) && set && !fcompatseen) {
				/*
				 * If running 'set -o posix' or
				 * 'set -o sh', turn off the other;
				 * if running 'set -o posix -o sh'
				 * allow both to be set though.
				 */
				Flag(FPOSIX) = 0;
				Flag(FSH) = 0;
				fcompatseen = true;
			}
			if ((i != (size_t)-1) && (set ? 1U : 0U) == Flag(i))
				/*
				 * Don't check the context if the flag
				 * isn't changing - makes "set -o interactive"
				 * work if you're already interactive. Needed
				 * if the output of "set +o" is to be used.
				 */
				;
			else if ((i != (size_t)-1) && (OFF(i) & what))
				change_flag((enum sh_flag)i, what, set);
			else {
				bi_errorf("%s: %s", go.optarg, "bad option");
				return (-1);
			}
			break;

#ifdef KSH_CHVT_FLAG
		case 'T':
			if (what != OF_FIRSTTIME)
				break;
#ifndef KSH_CHVT_CODE
			errorf("no TIOCSCTTY ioctl");
#else
			change_flag(FTALKING, OF_CMDLINE, true);
			chvt(&go);
			break;
#endif
#endif

		case '?':
			return (-1);

		default:
			if (what == OF_FIRSTTIME)
				break;
			/* -s: sort positional params (AT&T ksh stupidity) */
			if (what == OF_SET && optc == 's') {
				sortargs = true;
				break;
			}
			for (i = 0; i < NELEM(options); i++)
				if (optc == OFC(i) &&
				    (what & OFF(i))) {
					change_flag((enum sh_flag)i, what, set);
					break;
				}
			if (i == NELEM(options))
				internal_errorf("parse_args: '%c'", optc);
		}
	}
	if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
	    (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') &&
	    argv[go.optind][1] == '\0') {
		/* lone - clears -v and -x flags */
		if (argv[go.optind][0] == '-') {
			Flag(FVERBOSE) = 0;
			change_xtrace(0, false);
		}
		/* set skips lone - or + option */
		go.optind++;
	}
	if (setargsp)
		/* -- means set $#/$* even if there are no arguments */
		*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
		    argv[go.optind]);

	if (arrayset) {
		const char *ccp = NULL;

		if (*array)
			ccp = skip_varname(array, false);
		if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
			bi_errorf("%s: %s", array, "is not an identifier");
			return (-1);
		}
	}
	if (sortargs) {
		for (i = go.optind; argv[i]; i++)
			;
		qsort(&argv[go.optind], i - go.optind, sizeof(void *),
		    xstrcmp);
	}
	if (arrayset)
		go.optind += set_array(array, tobool(arrayset > 0),
		    argv + go.optind);

	return (go.optind);
}
Ejemplo n.º 6
0
/*
 * lookup variable (according to (set&LOCAL)), set its attributes
 * (INTEGER, RDONLY, EXPORT, TRACE, LJUST, RJUST, ZEROFIL, LCASEV,
 * UCASEV_AL), and optionally set its value if an assignment.
 */
struct tbl *
typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
{
	struct tbl *vp;
	struct tbl *vpbase, *t;
	char *tvar;
	const char *val;
	size_t len;
	bool vappend = false;
	enum namerefflag new_refflag = SRF_NOP;

	if ((set & (ARRAY | ASSOC)) == ASSOC) {
		new_refflag = SRF_ENABLE;
		set &= ~(ARRAY | ASSOC);
	}
	if ((clr & (ARRAY | ASSOC)) == ASSOC) {
		new_refflag = SRF_DISABLE;
		clr &= ~(ARRAY | ASSOC);
	}

	/* check for valid variable name, search for value */
	val = skip_varname(var, false);
	if (val == var) {
		/* no variable name given */
		return (NULL);
	}
	if (*val == '[') {
		if (new_refflag != SRF_NOP)
			errorf("%s: %s", var,
			    "reference variable can't be an array");
		len = array_ref_len(val);
		if (len == 0)
			return (NULL);
		/*
		 * IMPORT is only used when the shell starts up and is
		 * setting up its environment. Allow only simple array
		 * references at this time since parameter/command
		 * substitution is performed on the [expression] which
		 * would be a major security hole.
		 */
		if (set & IMPORT) {
			size_t i;

			for (i = 1; i < len - 1; i++)
				if (!ksh_isdigit(val[i]))
					return (NULL);
		}
		val += len;
	}
	if (val[0] == '=') {
		strndupx(tvar, var, val - var, ATEMP);
		++val;
	} else if (set & IMPORT) {
		/* environment invalid variable name or no assignment */
		return (NULL);
	} else if (val[0] == '+' && val[1] == '=') {
		strndupx(tvar, var, val - var, ATEMP);
		val += 2;
		vappend = true;
	} else if (val[0] != '\0') {
		/* other invalid variable names (not from environment) */
		return (NULL);
	} else {
		/* just varname with no value part nor equals sign */
		strdupx(tvar, var, ATEMP);
		val = NULL;
		/* handle foo[*] => foo (whole array) mapping for R39b */
		len = strlen(tvar);
		if (len > 3 && tvar[len - 3] == '[' && tvar[len - 2] == '*' &&
		    tvar[len - 1] == ']')
			tvar[len - 3] = '\0';
	}

	if (new_refflag == SRF_ENABLE) {
		const char *qval, *ccp;

		/* bail out on 'nameref foo+=bar' */
		if (vappend)
			errorf("appending not allowed for nameref");
		/* find value if variable already exists */
		if ((qval = val) == NULL) {
			varsearch(e->loc, &vp, tvar, hash(tvar));
			if (vp == NULL)
				goto nameref_empty;
			qval = str_val(vp);
		}
		/* check target value for being a valid variable name */
		ccp = skip_varname(qval, false);
		if (ccp == qval) {
			if (ksh_isdigit(qval[0])) {
				int c;

				if (getn(qval, &c))
					goto nameref_rhs_checked;
			} else if (qval[1] == '\0') switch (qval[0]) {
			case '$':
			case '!':
			case '?':
			case '#':
			case '-':
				goto nameref_rhs_checked;
			}
 nameref_empty:
			errorf("%s: %s", var, "empty nameref target");
		}
		len = (*ccp == '[') ? array_ref_len(ccp) : 0;
		if (ccp[len]) {
			/*
			 * works for cases "no array", "valid array with
			 * junk after it" and "invalid array"; in the
			 * latter case, len is also 0 and points to '['
			 */
			errorf("%s: %s", qval,
			    "nameref target not a valid parameter name");
		}
 nameref_rhs_checked:
		/* prevent nameref loops */
		while (qval) {
			if (!strcmp(qval, tvar))
				errorf("%s: %s", qval,
				    "expression recurses on parameter");
			varsearch(e->loc, &vp, qval, hash(qval));
			qval = NULL;
			if (vp && ((vp->flag & (ARRAY | ASSOC)) == ASSOC))
				qval = str_val(vp);
		}
	}

	/* prevent typeset from creating a local PATH/ENV/SHELL */
	if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0 ||
	    strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0))
		errorf("%s: %s", tvar, "restricted");

	innermost_refflag = new_refflag;
	vp = (set & LOCAL) ? local(tvar, tobool(set & LOCAL_COPY)) :
	    global(tvar);
	if (new_refflag == SRF_DISABLE && (vp->flag & (ARRAY|ASSOC)) == ASSOC)
		vp->flag &= ~ASSOC;
	else if (new_refflag == SRF_ENABLE) {
		if (vp->flag & ARRAY) {
			struct tbl *a, *tmp;

			/* free up entire array */
			for (a = vp->u.array; a; ) {
				tmp = a;
				a = a->u.array;
				if (tmp->flag & ALLOC)
					afree(tmp->val.s, tmp->areap);
				afree(tmp, tmp->areap);
			}
			vp->u.array = NULL;
			vp->flag &= ~ARRAY;
		}
		vp->flag |= ASSOC;
	}

	set &= ~(LOCAL|LOCAL_COPY);

	vpbase = (vp->flag & ARRAY) ? global(arrayname(tvar)) : vp;

	/*
	 * only allow export flag to be set; AT&T ksh allows any
	 * attribute to be changed which means it can be truncated or
	 * modified (-L/-R/-Z/-i)
	 */
	if ((vpbase->flag & RDONLY) &&
	    (val || clr || (set & ~EXPORT)))
		/* XXX check calls - is error here ok by POSIX? */
		errorfx(2, "read-only: %s", tvar);
	afree(tvar, ATEMP);

	/* most calls are with set/clr == 0 */
	if (set | clr) {
		bool ok = true;

		/*
		 * XXX if x[0] isn't set, there will be problems: need
		 * to have one copy of attributes for arrays...
		 */
		for (t = vpbase; t; t = t->u.array) {
			bool fake_assign;
			char *s = NULL;
			char *free_me = NULL;

			fake_assign = (t->flag & ISSET) && (!val || t != vp) &&
			    ((set & (UCASEV_AL|LCASEV|LJUST|RJUST|ZEROFIL)) ||
			    ((t->flag & INTEGER) && (clr & INTEGER)) ||
			    (!(t->flag & INTEGER) && (set & INTEGER)));
			if (fake_assign) {
				if (t->flag & INTEGER) {
					s = str_val(t);
					free_me = NULL;
				} else {
					s = t->val.s + t->type;
					free_me = (t->flag & ALLOC) ? t->val.s :
					    NULL;
				}
				t->flag &= ~ALLOC;
			}
			if (!(t->flag & INTEGER) && (set & INTEGER)) {
				t->type = 0;
				t->flag &= ~ALLOC;
			}
			t->flag = (t->flag | set) & ~clr;
			/*
			 * Don't change base if assignment is to be
			 * done, in case assignment fails.
			 */
			if ((set & INTEGER) && base > 0 && (!val || t != vp))
				t->type = base;
			if (set & (LJUST|RJUST|ZEROFIL))
				t->u2.field = field;
			if (fake_assign) {
				if (!setstr(t, s, KSH_RETURN_ERROR)) {
					/*
					 * Somewhat arbitrary action
					 * here: zap contents of
					 * variable, but keep the flag
					 * settings.
					 */
					ok = false;
					if (t->flag & INTEGER)
						t->flag &= ~ISSET;
					else {
						if (t->flag & ALLOC)
							afree(t->val.s, t->areap);
						t->flag &= ~(ISSET|ALLOC);
						t->type = 0;
					}
				}
				if (free_me)
					afree(free_me, t->areap);
			}
		}
		if (!ok)
			errorfz();
	}

	if (val != NULL) {
		char *tval;

		if (vappend) {
			tval = shf_smprintf("%s%s", str_val(vp), val);
			val = tval;
		} else
			tval = NULL;

		if (vp->flag&INTEGER) {
			/* do not zero base before assignment */
			setstr(vp, val, KSH_UNWIND_ERROR | 0x4);
			/* done after assignment to override default */
			if (base > 0)
				vp->type = base;
		} else
			/* setstr can't fail (readonly check already done) */
			setstr(vp, val, KSH_RETURN_ERROR | 0x4);

		if (tval != NULL)
			afree(tval, ATEMP);
	}

	/* only x[0] is ever exported, so use vpbase */
	if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) &&
	    vpbase->type == 0)
		exportprep(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);

	return (vp);
}
Ejemplo n.º 7
0
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
		} else if (tp->val.f == c_trap) {
			t->u.evalflags &= ~DOTCOMEXEC;
			break;
		} else
			break;
		tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC));
	}
	if (t->u.evalflags & DOTCOMEXEC)
		flags |= XEXEC;
	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;
	if (Flag(FXTRACE))
		change_xtrace(2, false);
	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 | DOASNFIELD);
		e->loc = l_assign;
		if (Flag(FXTRACE)) {
			const char *ccp;

			ccp = skip_varname(cp, true);
			if (*ccp == '+')
				++ccp;
			if (*ccp == '=')
				++ccp;
			shf_write(cp, ccp - cp, shl_xtrace);
			print_value_quoted(shl_xtrace, ccp);
			shf_putc(' ', shl_xtrace);
		}
		/* but assign in there as usual */
		typeset(cp, type_flags, 0, 0, 0);
		if (bourne_function_call && !(type_flags & EXPORT))
			typeset(cp, LOCAL | LOCAL_COPY | EXPORT, 0, 0, 0);
	}

	if (Flag(FXTRACE)) {
		change_xtrace(2, false);
		if (ap[rv = 0]) {
 xtrace_ap_loop:
			print_value_quoted(shl_xtrace, ap[rv]);
			if (ap[++rv]) {
				shf_putc(' ', shl_xtrace);
				goto xtrace_ap_loop;
			}
		}
		change_xtrace(1, false);
	}

	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) {
				warningf(true, "%s: %s %s %s: %s", cp,
				    "can't open", "function definition file",
				    tp->u.fpath, cstrerror(errno));
				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) ? 1 : 0;
		change_xtrace((Flag(FXTRACEREC) ? old_xflag : 0) |
		    ((tp->flag & TRACE) ? 1 : 0), false);
		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;
		change_xtrace(old_xflag, false);
		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);
}
Ejemplo n.º 8
0
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 resetspec;
	int fcflags = FC_BI|FC_FUNC|FC_PATH;
	struct block *l_expand, *l_assign;
	int optc;
	const char *exec_argv0 = NULL;
	bool exec_clrenv = false;

	/* snag the last argument for $_ */
	if (Flag(FTALKING) && *(lastp = ap)) {
		/*
		 * 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()?.
		 */
		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
	 */
	resetspec = false;
	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");
			if (tp->type == CSHELL && (tp->val.f == c_cat
#ifdef MKSH_PRINTF_BUILTIN
			    || tp->val.f == c_printf
#endif
			    ))
				break;
			continue;
		} else if (tp->val.f == c_exec) {
			if (ap[1] == NULL)
				break;
			ksh_getopt_reset(&builtin_opt, GF_ERROR);
			while ((optc = ksh_getopt(ap, &builtin_opt, "a:c")) != -1)
				switch (optc) {
				case 'a':
					exec_argv0 = builtin_opt.optarg;
					break;
				case 'c':
					exec_clrenv = true;
					/* ensure we can actually do this */
					resetspec = true;
					break;
				default:
					rv = 2;
					goto Leave;
				}
			ap += builtin_opt.optind;
			flags |= XEXEC;
		} else if (tp->val.f == c_command) {
			bool saw_p = false;

			/*
			 * 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 = true;
			if (optc != -1)
				/* 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.
			 */
			resetspec = true;
			if (!ap[0]) {
				/* ensure command with no args exits with 0 */
				subst_exstat = 0;
				break;
			}
		} else if (tp->val.f == c_cat) {
			/* if we have any flags, do not use the builtin */
			if (ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' &&
			    /* argument, begins with -, is not - or -- */
			    (ap[1][1] != '-' || ap[1][2] != '\0')) {
				struct tbl *ext_cat;

				ext_cat = findcom(Tcat, FC_PATH | FC_FUNC);
				if (ext_cat && (ext_cat->type != CTALIAS ||
				    (ext_cat->flag & ISSET)))
					tp = ext_cat;
			}
			break;
#ifdef MKSH_PRINTF_BUILTIN
		} else if (tp->val.f == c_printf) {
			struct tbl *ext_printf;

			ext_printf = findcom(Tprintf, FC_PATH | FC_FUNC);
			if (ext_printf && (ext_printf->type != CTALIAS ||
			    (ext_printf->flag & ISSET)))
				tp = ext_printf;
			break;
#endif
		} else if (tp->val.f == c_trap) {
			t->u.evalflags &= ~DOTCOMEXEC;
			break;
		} else
			break;
		tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC));
	}
	if (t->u.evalflags & DOTCOMEXEC)
		flags |= XEXEC;
	l_expand = e->loc;
	if (!resetspec && (!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 (!resetspec && tp && tp->type == CFUNC &&
		    !(tp->flag & FKSH))
			type_flags = EXPORT;
		else
			type_flags = LOCAL|LOCAL_COPY|EXPORT;
	}
	l_assign = e->loc;
	if (exec_clrenv)
		l_assign->flags |= BF_STOPENV;
	if (Flag(FEXPORT))
		type_flags |= EXPORT;
	if (Flag(FXTRACE))
		change_xtrace(2, false);
	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 | DOSCALAR);
		e->loc = l_assign;
		if (Flag(FXTRACE)) {
			const char *ccp;

			ccp = skip_varname(cp, true);
			if (*ccp == '+')
				++ccp;
			if (*ccp == '=')
				++ccp;
			shf_write(cp, ccp - cp, shl_xtrace);
			print_value_quoted(shl_xtrace, ccp);
			shf_putc(' ', shl_xtrace);
		}
		/* but assign in there as usual */
		typeset(cp, type_flags, 0, 0, 0);
	}

	if (Flag(FXTRACE)) {
		change_xtrace(2, false);
		if (ap[rv = 0]) {
 xtrace_ap_loop:
			print_value_quoted(shl_xtrace, ap[rv]);
			if (ap[++rv]) {
				shf_putc(' ', shl_xtrace);
				goto xtrace_ap_loop;
			}
		}
		change_xtrace(1, false);
	}

	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:
 do_call_builtin:
		rv = call_builtin(tp, (const char **)ap, null, resetspec);
		if (resetspec && tp->val.f == c_shift) {
			l_expand->argc = l_assign->argc;
			l_expand->argv = l_assign->argv;
		}
		break;

	/* function call */
	case CFUNC: {
		volatile uint32_t old_inuse;
		const char * volatile old_kshname;
		volatile uint8_t old_flags[FNFLAGS];

		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) {
				if (!strcmp(cp, Tcat)) {
 no_cat_in_FPATH:
					tp = findcom(Tcat, FC_BI);
					goto do_call_builtin;
				}
#ifdef MKSH_PRINTF_BUILTIN
				if (!strcmp(cp, Tprintf)) {
 no_printf_in_FPATH:
					tp = findcom(Tprintf, FC_BI);
					goto do_call_builtin;
				}
#endif
				warningf(true, "%s: %s %s %s: %s", cp,
				    "can't open", "function definition file",
				    tp->u.fpath, cstrerror(errno));
				rv = 127;
				break;
			}
			if (!(ftp = findfunc(cp, hash(cp), false)) ||
			    !(ftp->flag & ISSET)) {
				if (!strcmp(cp, Tcat))
					goto no_cat_in_FPATH;
#ifdef MKSH_PRINTF_BUILTIN
				if (!strcmp(cp, Tprintf))
					goto no_printf_in_FPATH;
#endif
				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);
		}

		for (type_flags = 0; type_flags < FNFLAGS; ++type_flags)
			old_flags[type_flags] = shell_flags[type_flags];
		change_xtrace((Flag(FXTRACEREC) ? Flag(FXTRACE) : 0) |
		    ((tp->flag & TRACE) ? 1 : 0), false);
		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;
		change_xtrace(old_flags[(int)FXTRACE], false);
#ifndef MKSH_LEGACY_MODE
		if (tp->flag & FKSH) {
			/* Korn style functions restore Flags on return */
			old_flags[(int)FXTRACE] = Flag(FXTRACE);
			for (type_flags = 0; type_flags < FNFLAGS; ++type_flags)
				shell_flags[type_flags] = old_flags[type_flags];
		}
#endif
		tp->flag = (tp->flag & ~FINUSE) | old_inuse;

		/*
		 * Were we deleted while executing? If so, free the
		 * execution tree.
		 */
		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 program's full path */
		/* setstr() can't fail here */
		setstr(typeset("_", LOCAL | EXPORT, 0, INTEGER, 0),
		    tp->val.s, KSH_RETURN_ERROR);

		/* to fork, we set up a TEXEC node and call execute */
		texec.type = TEXEC;
		/* for vistree/dumptree */
		texec.left = t;
		texec.str = tp->val.s;
		texec.args = ap;

		/* in this case we do not fork, of course */
		if (flags & XEXEC) {
			if (exec_argv0)
				texec.args[0] = exec_argv0;
			j_exit();
			if (!(flags & XBGND)
#ifndef MKSH_UNEMPLOYED
			    || Flag(FMONITOR)
#endif
			    ) {
				setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG);
				setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG);
			}
		}

		rv = exchild(&texec, flags, xerrok, -1);
		break;
	}
 Leave:
	if (flags & XEXEC) {
		exstat = rv & 0xFF;
		unwind(LLEAVE);
	}
	return (rv);
}