Пример #1
0
/*
 * execute command tree
 */
int
execute(struct op *volatile t,
    volatile int flags, volatile int *xerrok)		/* if XEXEC don't fork */
{
	int i, dummy = 0;
	volatile int rv = 0;
	int pv[2];
	char ** volatile ap;
	char *s, *cp;
	struct ioword **iowp;
	struct tbl *tp = NULL;

	if (t == NULL)
		return 0;

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

	/* Is this the end of a pipeline?  If so, we want to evaluate the
	 * command arguments
	bool eval_done = false;
	if ((flags&XFORK) && !(flags&XEXEC) && (flags&XPCLOSE)) {
		eval_done = true;
		tp = eval_execute_args(t, &ap);
	}
	 */
	if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE)
		return exchild(t, flags & ~XTIME, xerrok, -1); /* run in sub-process */

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

	if (t->type == TCOM) {
		/* Clear subst_exstat before argument expansion.  Used by
		 * null commands (see comexec() and c_eval()) and by c_set().
		 */
		subst_exstat = 0;

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

		/* POSIX says expand command words first, then redirections,
		 * and assignments last..
		 */
		ap = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE);
		if (flags & XTIME)
			/* Allow option parsing (bizarre, but POSIX) */
			timex_hook(t, &ap);
		if (Flag(FXTRACE) && ap[0]) {
			shf_fprintf(shl_out, "%s",
				PS4_SUBSTITUTE(str_val(global("PS4"))));
			for (i = 0; ap[i]; i++)
				shf_fprintf(shl_out, "%s%s", ap[i],
				    ap[i + 1] ? space : newline);
			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 = (short *) alloc(sizeofN(short, NUFILE), ATEMP);
		/* initialize to not redirected */
		memset(e->savefd, 0, sizeofN(short, NUFILE));
	}
Пример #2
0
/*
 * run the commands from the input source, returning status.
 */
int
shell(Source * volatile s, volatile int level)
{
	struct op *t;
	volatile bool wastty = tobool(s->flags & SF_TTY);
	volatile uint8_t attempts = 13;
	volatile bool interactive = (level == 0) && Flag(FTALKING);
	volatile bool sfirst = true;
	Source *volatile old_source = source;
	int i;

	newenv(level == 2 ? E_EVAL : E_PARSE);
	if (interactive)
		really_exit = false;
	switch ((i = kshsetjmp(e->jbuf))) {
	case 0:
		break;
	case LBREAK:
	case LCONTIN:
		if (level != 2) {
			source = old_source;
			quitenv(NULL);
			internal_errorf(Tf_cant_s, Tshell,
			    i == LBREAK ? Tbreak : Tcontinue);
			/* NOTREACHED */
		}
		/* assert: interactive == false */
		/* FALLTHROUGH */
	case LINTR:
		/* we get here if SIGINT not caught or ignored */
	case LERROR:
	case LSHELL:
		if (interactive) {
			if (i == LINTR)
				shellf("\n");
			/*
			 * Reset any eof that was read as part of a
			 * multiline command.
			 */
			if (Flag(FIGNOREEOF) && s->type == SEOF && wastty)
				s->type = SSTDIN;
			/*
			 * Used by exit command to get back to
			 * top level shell. Kind of strange since
			 * interactive is set if we are reading from
			 * a tty, but to have stopped jobs, one only
			 * needs FMONITOR set (not FTALKING/SF_TTY)...
			 */
			/* toss any input we have so far */
			yyrecursive_pop(true);
			s->start = s->str = null;
			retrace_info = NULL;
			herep = heres;
			break;
		}
		/* FALLTHROUGH */
	case LEXIT:
	case LLEAVE:
	case LRETURN:
		source = old_source;
		quitenv(NULL);
		/* keep on going */
		unwind(i);
		/* NOTREACHED */
	default:
		source = old_source;
		quitenv(NULL);
		internal_errorf(Tunexpected_type, Tunwind, Tshell, i);
		/* NOTREACHED */
	}
	while (/* CONSTCOND */ 1) {
		if (trap)
			runtraps(0);

		if (s->next == NULL) {
			if (Flag(FVERBOSE))
				s->flags |= SF_ECHO;
			else
				s->flags &= ~SF_ECHO;
		}
		if (interactive) {
			j_notify();
			set_prompt(PS1, s);
		}
		t = compile(s, sfirst, true);
		if (interactive)
			histsave(&s->line, NULL, HIST_FLUSH, true);
		sfirst = false;
		if (!t)
			goto source_no_tree;
		if (t->type == TEOF) {
			if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
				shellf("Use 'exit' to leave mksh\n");
				s->type = SSTDIN;
			} else if (wastty && !really_exit &&
			    j_stopped_running()) {
				really_exit = true;
				s->type = SSTDIN;
			} else {
				/*
				 * this for POSIX which says EXIT traps
				 * shall be taken in the environment
				 * immediately after the last command
				 * executed.
				 */
				if (level == 0)
					unwind(LEXIT);
				break;
			}
		} else if ((s->flags & SF_MAYEXEC) && t->type == TCOM)
			t->u.evalflags |= DOTCOMEXEC;
		if (!Flag(FNOEXEC) || (s->flags & SF_TTY))
			exstat = execute(t, 0, NULL) & 0xFF;

		if (t->type != TEOF && interactive && really_exit)
			really_exit = false;

 source_no_tree:
		reclaim();
	}
	quitenv(NULL);
	source = old_source;
	return (exstat & 0xFF);
}
Пример #3
0
cell proc_eval(const cell &x)
{
    bool listvars = false;
    if (listvars)
        proc_listvars(cell());
    if (x.type == v_string || x.type == v_number || x.type == v_function)
        return x;
    else if (x.type == v_symbol)
    {
        //std::cout << "fetching var " << x.str << ": " << toString(env->get(x.str)) << "\n";
        return env->get(x.str);
    }
    else if (x.type == v_list)
    {
        if (!x.car)
            return nil;
        cell head = proc_eval(*x.car);
        if (head.type == v_proc)
            return head.proc(x.cdr? *x.cdr : nil);
        if (head.type == v_function)
        {
            std::shared_ptr<environment> oldenv = env;
            std::shared_ptr<environment> newenv(new environment(head.env));
            const cell *name_iter = head.car;
            const cell *arg_iter = x.cdr;
            while (arg_iter && arg_iter->car && name_iter && name_iter->car)
            {
                if (name_iter->car->str == "&REST")
                {
                    if (!(name_iter->cdr && name_iter->cdr->car && name_iter->cdr->car->type == v_symbol))
                        throw(exception("Error: expected name for &rest parameter"));
                    cell head(v_list);
                    cell *tail = &head;
                    while (arg_iter && arg_iter->car)
                    {
                        tail->car = new cell();
                        *tail->car = proc_eval(*arg_iter->car);
                        tail->cdr = new cell(v_list);
                        tail = tail->cdr;
                        arg_iter = arg_iter->cdr;
                    }
                    newenv->vars[name_iter->cdr->car->str] = head;
                    name_iter = name_iter->cdr->cdr;
                    break;                              //skip the outer loop so we don't dereference the null car pointer.
                }
                newenv->vars[name_iter->car->str] = proc_eval(*arg_iter->car);
                arg_iter = arg_iter->cdr;
                name_iter = name_iter->cdr;
            }
            if (arg_iter && arg_iter->car)
                throw(exception("Error: too many arguments to function"));
            if (name_iter && name_iter->car)
                throw(exception("Error: too few arguments to function"));
            env = newenv;
            cell result;
            cell *body_iter = head.cdr;
            while (body_iter && body_iter->car)
            {
                result = proc_eval(*body_iter->car);
                body_iter = body_iter->cdr;
            }
            env = oldenv;
            return result;
        }
        if (head.type == v_macro)
            return proc_eval(expand_macro(head, x.cdr? *x.cdr : cell(v_list)));

        throw(exception("Error: attempt to call non-proc"));

    }
    else
    {
        throw(exception("Unrecognised cell type! (eval)"));
    }
}
Пример #4
0
int
include(const char *name, int argc, const char **argv, bool intr_ok)
{
	Source *volatile s = NULL;
	struct shf *shf;
	const char **volatile old_argv;
	volatile int old_argc;
	int i;

	shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC);
	if (shf == NULL)
		return (-1);

	if (argv) {
		old_argv = e->loc->argv;
		old_argc = e->loc->argc;
	} else {
		old_argv = NULL;
		old_argc = 0;
	}
	newenv(E_INCL);
	if ((i = kshsetjmp(e->jbuf))) {
		quitenv(s ? s->u.shf : NULL);
		if (old_argv) {
			e->loc->argv = old_argv;
			e->loc->argc = old_argc;
		}
		switch (i) {
		case LRETURN:
		case LERROR:
			/* see below */
			return (exstat & 0xFF);
		case LINTR:
			/*
			 * intr_ok is set if we are including .profile or $ENV.
			 * If user ^Cs out, we don't want to kill the shell...
			 */
			if (intr_ok && ((exstat & 0xFF) - 128) != SIGTERM)
				return (1);
			/* FALLTHROUGH */
		case LEXIT:
		case LLEAVE:
		case LSHELL:
			unwind(i);
			/* NOTREACHED */
		default:
			internal_errorf(Tunexpected_type, Tunwind, Tsource, i);
			/* NOTREACHED */
		}
	}
	if (argv) {
		e->loc->argv = argv;
		e->loc->argc = argc;
	}
	s = pushs(SFILE, ATEMP);
	s->u.shf = shf;
	strdupx(s->file, name, ATEMP);
	i = shell(s, 1);
	quitenv(s->u.shf);
	if (old_argv) {
		e->loc->argv = old_argv;
		e->loc->argc = old_argc;
	}
	/* & 0xff to ensure value not -1 */
	return (i & 0xFF);
}
Пример #5
0
/*
 * 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, (size_t)-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 (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, flags & XERROK, xerrok);
		else {
			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 | INT_L;
		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);
}