📄 eval.c
字号:
/* $NetBSD: eval.c,v 1.81.2.1 2005/06/13 22:03:51 tron Exp $ *//*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#include <sys/cdefs.h>#ifndef lint#if 0static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95";#else__RCSID("$NetBSD: eval.c,v 1.81.2.1 2005/06/13 22:03:51 tron Exp $");#endif#endif /* not lint */#include <stdlib.h>#include <signal.h>#include <stdio.h>#include <unistd.h>#ifdef __linux__#include <fcntl.h>#else#include <sys/fcntl.h>#endif#include <sys/times.h>#include <sys/param.h>#include <sys/types.h>#include <sys/wait.h>/* * Evaluate a command. */#include "shell.h"#include "nodes.h"#include "syntax.h"#include "expand.h"#include "parser.h"#include "jobs.h"#include "eval.h"#include "builtins.h"#include "options.h"#include "exec.h"#include "redir.h"#include "input.h"#include "output.h"#include "trap.h"#include "var.h"#include "memalloc.h"#include "error.h"#include "show.h"#include "mystring.h"#include "main.h"#ifndef SMALL#include "myhistedit.h"#endif/* flags in argument to evaltree */#define EV_EXIT 01 /* exit after evaluating tree */#define EV_TESTED 02 /* exit status is checked; ignore -e flag */#define EV_BACKCMD 04 /* command executing within back quotes */int evalskip; /* set if we are skipping commands */STATIC int skipcount; /* number of levels to skip */MKINIT int loopnest; /* current loop nesting level */int funcnest; /* depth of function calls */char *commandname;struct strlist *cmdenviron;int exitstatus; /* exit status of last command */int back_exitstatus; /* exit status of backquoted command */STATIC void evalloop(union node *, int);STATIC void evalfor(union node *, int);STATIC void evalcase(union node *, int);STATIC void evalsubshell(union node *, int);STATIC void expredir(union node *);STATIC void evalpipe(union node *);STATIC void evalcommand(union node *, int, struct backcmd *);STATIC void prehash(union node *);/* * Called to reset things after an exception. */#ifdef mkinitINCLUDE "eval.h"RESET { evalskip = 0; loopnest = 0; funcnest = 0;}SHELLPROC { exitstatus = 0;}#endifstatic intsh_pipe(int fds[2]){ int nfd; if (pipe(fds)) return -1; if (fds[0] < 3) { nfd = fcntl(fds[0], F_DUPFD, 3); if (nfd != -1) { close(fds[0]); fds[0] = nfd; } } if (fds[1] < 3) { nfd = fcntl(fds[1], F_DUPFD, 3); if (nfd != -1) { close(fds[1]); fds[1] = nfd; } } return 0;}/* * The eval commmand. */intevalcmd(int argc, char **argv){ char *p; char *concat; char **ap; if (argc > 1) { p = argv[1]; if (argc > 2) { STARTSTACKSTR(concat); ap = argv + 2; for (;;) { while (*p) STPUTC(*p++, concat); if ((p = *ap++) == NULL) break; STPUTC(' ', concat); } STPUTC('\0', concat); p = grabstackstr(concat); } evalstring(p, EV_TESTED); } return exitstatus;}/* * Execute a command or commands contained in a string. */voidevalstring(char *s, int flag){ union node *n; struct stackmark smark; setstackmark(&smark); setinputstring(s, 1); while ((n = parsecmd(0)) != NEOF) { evaltree(n, flag); popstackmark(&smark); } popfile(); popstackmark(&smark);}/* * Evaluate a parse tree. The value is left in the global variable * exitstatus. */voidevaltree(union node *n, int flags){ if (n == NULL) { TRACE(("evaltree(NULL) called\n")); exitstatus = 0; goto out; }#ifdef WITH_HISTORY displayhist = 1; /* show history substitutions done with fc */#endif TRACE(("pid %d, evaltree(%p: %d, %d) called\n", getpid(), n, n->type, flags)); switch (n->type) { case NSEMI: evaltree(n->nbinary.ch1, flags & EV_TESTED); if (evalskip) goto out; evaltree(n->nbinary.ch2, flags); break; case NAND: evaltree(n->nbinary.ch1, EV_TESTED); if (evalskip || exitstatus != 0) goto out; evaltree(n->nbinary.ch2, flags); break; case NOR: evaltree(n->nbinary.ch1, EV_TESTED); if (evalskip || exitstatus == 0) goto out; evaltree(n->nbinary.ch2, flags); break; case NREDIR: expredir(n->nredir.redirect); redirect(n->nredir.redirect, REDIR_PUSH); evaltree(n->nredir.n, flags); popredir(); break; case NSUBSHELL: evalsubshell(n, flags); break; case NBACKGND: evalsubshell(n, flags); break; case NIF: { evaltree(n->nif.test, EV_TESTED); if (evalskip) goto out; if (exitstatus == 0) evaltree(n->nif.ifpart, flags); else if (n->nif.elsepart) evaltree(n->nif.elsepart, flags); else exitstatus = 0; break; } case NWHILE: case NUNTIL: evalloop(n, flags); break; case NFOR: evalfor(n, flags); break; case NCASE: evalcase(n, flags); break; case NDEFUN: defun(n->narg.text, n->narg.next); exitstatus = 0; break; case NNOT: evaltree(n->nnot.com, EV_TESTED); exitstatus = !exitstatus; break; case NPIPE: evalpipe(n); break; case NCMD: evalcommand(n, flags, (struct backcmd *)NULL); break; default: out1fmt("Node type = %d\n", n->type); flushout(&output); break; }out: if (pendingsigs) dotrap(); if ((flags & EV_EXIT) != 0) exitshell(exitstatus);}STATIC voidevalloop(union node *n, int flags){ int status; loopnest++; status = 0; for (;;) { evaltree(n->nbinary.ch1, EV_TESTED); if (evalskip) {skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { evalskip = 0; continue; } if (evalskip == SKIPBREAK && --skipcount <= 0) evalskip = 0; break; } if (n->type == NWHILE) { if (exitstatus != 0) break; } else { if (exitstatus == 0) break; } evaltree(n->nbinary.ch2, flags & EV_TESTED); status = exitstatus; if (evalskip) goto skipping; } loopnest--; exitstatus = status;}STATIC voidevalfor(union node *n, int flags){ struct arglist arglist; union node *argp; struct strlist *sp; struct stackmark smark; int status = 0; setstackmark(&smark); arglist.lastp = &arglist.list; for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); if (evalskip) goto out; } *arglist.lastp = NULL; loopnest++; for (sp = arglist.list ; sp ; sp = sp->next) { setvar(n->nfor.var, sp->text, 0); evaltree(n->nfor.body, flags & EV_TESTED); status = exitstatus; if (evalskip) { if (evalskip == SKIPCONT && --skipcount <= 0) { evalskip = 0; continue; } if (evalskip == SKIPBREAK && --skipcount <= 0) evalskip = 0; break; } } loopnest--; exitstatus = status;out: popstackmark(&smark);}STATIC voidevalcase(union node *n, int flags){ union node *cp; union node *patp; struct arglist arglist; struct stackmark smark; int status = 0; setstackmark(&smark); arglist.lastp = &arglist.list; expandarg(n->ncase.expr, &arglist, EXP_TILDE); for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { if (casematch(patp, arglist.list->text)) { if (evalskip == 0) { evaltree(cp->nclist.body, flags); status = exitstatus; } goto out; } } }out: exitstatus = status; popstackmark(&smark);}/* * Kick off a subshell to evaluate a tree. */STATIC voidevalsubshell(union node *n, int flags){ struct job *jp; int backgnd = (n->type == NBACKGND); expredir(n->nredir.redirect); INTOFF; jp = makejob(n, 1); if (forkshell(jp, n, backgnd) == 0) { INTON; if (backgnd) flags &=~ EV_TESTED; redirect(n->nredir.redirect, 0); /* never returns */ evaltree(n->nredir.n, flags | EV_EXIT); } if (! backgnd) exitstatus = waitforjob(jp); INTON;}/* * Compute the names of the files in a redirection list. */STATIC voidexpredir(union node *n){ union node *redir; for (redir = n ; redir ; redir = redir->nfile.next) { struct arglist fn; fn.lastp = &fn.list; switch (redir->type) { case NFROMTO: case NFROM: case NTO: case NCLOBBER: case NAPPEND: expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); redir->nfile.expfname = fn.list->text; break; case NFROMFD: case NTOFD: if (redir->ndup.vname) { expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); fixredir(redir, fn.list->text, 1); } break; } }}/* * Evaluate a pipeline. All the processes in the pipeline are children * of the process creating the pipeline. (This differs from some versions * of the shell, which make the last process in a pipeline the parent * of all the rest.) */STATIC voidevalpipe(union node *n){ struct job *jp; struct nodelist *lp; int pipelen; int prevfd; int pip[2]; TRACE(("evalpipe(0x%lx) called\n", (long)n)); pipelen = 0; for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) pipelen++; INTOFF; jp = makejob(n, pipelen); prevfd = -1; for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { prehash(lp->n); pip[1] = -1; if (lp->next) { if (sh_pipe(pip) < 0) { close(prevfd); error("Pipe call failed"); } } if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { INTON; if (prevfd > 0) { close(0); copyfd(prevfd, 0); close(prevfd); } if (pip[1] >= 0) { close(pip[0]); if (pip[1] != 1) { close(1); copyfd(pip[1], 1); close(pip[1]); } } evaltree(lp->n, EV_EXIT); } if (prevfd >= 0) close(prevfd); prevfd = pip[0]; close(pip[1]); } if (n->npipe.backgnd == 0) { exitstatus = waitforjob(jp); TRACE(("evalpipe: job done exit status %d\n", exitstatus)); } INTON;}/* * Execute a command inside back quotes. If it's a builtin command, we * want to save its output in a block obtained from malloc. Otherwise * we fork off a subprocess and get the output of the command via a pipe. * Should be called with interrupts off. */voidevalbackcmd(union node *n, struct backcmd *result){ int pip[2]; struct job *jp; struct stackmark smark; /* unnecessary */ setstackmark(&smark); result->fd = -1; result->buf = NULL; result->nleft = 0; result->jp = NULL; if (n == NULL) { goto out; }#ifdef notyet /* * For now we disable executing builtins in the same * context as the shell, because we are not keeping * enough state to recover from changes that are * supposed only to affect subshells. eg. echo "`cd /`" */ if (n->type == NCMD) { exitstatus = oexitstatus; evalcommand(n, EV_BACKCMD, result); } else#endif { INTOFF; if (sh_pipe(pip) < 0) error("Pipe call failed"); jp = makejob(n, 1); if (forkshell(jp, n, FORK_NOJOB) == 0) { FORCEINTON; close(pip[0]); if (pip[1] != 1) { close(1); copyfd(pip[1], 1); close(pip[1]); } eflag = 0; evaltree(n, EV_EXIT); /* NOTREACHED */ } close(pip[1]); result->fd = pip[0]; result->jp = jp; INTON; }out: popstackmark(&smark); TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", result->fd, result->buf, result->nleft, result->jp));}static const char *syspath(void){ static char *sys_path = NULL;#ifndef __linux__ static int mib[] = {CTL_USER, USER_CS_PATH};#endif static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin"; if (sys_path == NULL) {#ifndef __linux__ size_t len; if (sysctl(mib, 2, 0, &len, 0, 0) != -1 && (sys_path = ckmalloc(len + 5)) != NULL && sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) { memcpy(sys_path, "PATH=", 5); } else#endif { ckfree(sys_path); /* something to keep things happy */ sys_path = def_path; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -