extern void *erealloc(void *p, size_t n) { extern void *realloc(void *, size_t); if ((p = realloc(p, n)) == NULL) { uerror("realloc"); rc_exit(1); } return p; }
extern void *ealloc(size_t n) { extern void *malloc(size_t); void *p = malloc(n); if (p == NULL) { uerror("malloc"); rc_exit(1); } return p; }
extern char *which(char *name, bool verbose) { static char *test = NULL; static size_t testlen = 0; List *path; int len; if (name == NULL) /* no filename? can happen with "> foo" as a command */ return NULL; if (!initialized) { initialized = TRUE; uid = geteuid(); gid = getegid(); #if HAVE_GETGROUPS #if HAVE_POSIX_GETGROUPS ngroups = getgroups(0, (GETGROUPS_T *)0); if (ngroups < 0) { uerror("getgroups"); rc_exit(1); } #else ngroups = NGROUPS; #endif if (ngroups) { gidset = ealloc(ngroups * sizeof(GETGROUPS_T)); getgroups(ngroups, gidset); } #endif } if (isabsolute(name)) /* absolute pathname? */ return rc_access(name, verbose) ? name : NULL; len = strlen(name); for (path = varlookup("path"); path != NULL; path = path->n) { size_t need = strlen(path->w) + len + 2; /* one for null terminator, one for the '/' */ if (testlen < need) { efree(test); test = ealloc(testlen = need); } if (*path->w == '\0') { strcpy(test, name); } else { strcpy(test, path->w); if (!streq(test, "/")) /* "//" is special to POSIX */ strcat(test, "/"); strcat(test, name); } if (rc_access(test, FALSE)) return test; } if (verbose) { char *n = protect(name); fprint(2, RC "cannot find `%s'\n", n); efree(n); } return NULL; }
extern void statprint(int pid, int i) { if (i & 0xff) { char *msg = ((i & 0x7f) < NUMOFSIGNALS ? signals[i & 0x7f].msg : ""); if (pid != -1) fprint(2, "%d: ", pid); if (i & 0x80) { if (*msg == '\0') fprint(2, "core dumped\n"); else fprint(2, "%s--core dumped\n", msg); } else if (*msg != '\0') fprint(2, "%s\n", msg); } if (i != 0 && dashee && !cond) rc_exit(getstatus()); }
extern void rc_raise(ecodes e) { if (e == eError && rc_pid != getpid()) exit(1); /* child processes exit on an error/signal */ for (; estack != NULL; estack = estack->prev) if (estack->e != e) { if (e == eBreak && estack->e != eArena && estack->e != eVarstack) rc_error("break outside of loop"); else if (e == eReturn && estack->e == eError) /* can return from loops inside functions */ rc_error("return outside of function"); switch (estack->e) { default: break; case eVarstack: varrm(estack->data.name, TRUE); break; case eArena: restoreblock(estack->data.b); break; case eFifo: unlink(estack->data.name); break; case eFd: close(estack->data.fd); break; } } else { if (e == eError && !estack->interactive) { popinput(); } else { Jbwrap *j = estack->data.jb; interactive = estack->interactive; estack = estack->prev; siglongjmp(j->j, 1); } } rc_exit(1); /* top of exception stack */ }
extern int main(int argc, char *argv[], char *envp[]) { char *dashsee[2], *dollarzero, *null[1]; int c; initprint(); dashsee[0] = dashsee[1] = NULL; dollarzero = argv[0]; rc_pid = getpid(); dashell = (*argv[0] == '-'); /* Unix tradition */ while ((c = rc_getopt(argc, argv, "c:deiIlnopsvx")) != -1) switch (c) { case 'c': dashsee[0] = rc_optarg; goto quitopts; case 'd': dashdee = TRUE; break; case 'e': dashee = TRUE; break; case 'I': dashEYE = TRUE; interactive = FALSE; break; case 'i': dasheye = interactive = TRUE; break; case 'l': dashell = TRUE; break; case 'n': dashen = TRUE; break; case 'o': dashoh = TRUE; break; case 'p': dashpee = TRUE; break; case 's': dashess = TRUE; break; case 'v': dashvee = TRUE; break; case 'x': dashex = TRUE; break; case '?': exit(1); } quitopts: argv += rc_optind; /* use isatty() iff neither -i nor -I is set, and iff the input is not from a script or -c flags */ if (!dasheye && !dashEYE && dashsee[0] == NULL && (dashess || *argv == NULL)) interactive = isatty(0); if (!dashoh) { checkfd(0, rFrom); checkfd(1, rCreate); checkfd(2, rCreate); } initsignal(); inithash(); initparse(); assigndefault("ifs", " ", "\t", "\n", (void *)0); #ifdef DEFAULTPATH assigndefault("path", DEFAULTPATH, (void *)0); #endif assigndefault("pid", nprint("%d", rc_pid), (void *)0); assigndefault("prompt", "; ", "", (void *)0); assigndefault("version", VERSION, "$Release: @(#)" PACKAGE " " VERSION " " RELDATE " $", (void *)0); initenv(envp); initinput(); null[0] = NULL; starassign(dollarzero, null, FALSE); /* assign $0 to $* */ inithandler(); if (dashell) { char *rcrc; int fd; rcrc = concat(varlookup("home"), word("/.rcrc", NULL))->w; fd = rc_open(rcrc, rFrom); if (fd == -1) { if (errno != ENOENT) uerror(rcrc); } else { bool push_interactive; pushfd(fd); push_interactive = interactive; interactive = FALSE; doit(TRUE); interactive = push_interactive; close(fd); } } if (dashsee[0] != NULL || dashess) { /* input from -c or -s? */ if (*argv != NULL) starassign(dollarzero, argv, FALSE); if (dashess) pushfd(0); else pushstring(dashsee, TRUE); } else if (*argv != NULL) { /* else from a file? */ b_dot(--argv); rc_exit(getstatus()); } else { /* else stdin */ pushfd(0); } dasheye = FALSE; doit(TRUE); rc_exit(getstatus()); return 0; /* Never really reached. */ }
extern void exec(List *s, bool parent) { char **av, **ev = NULL; int pid, stat; builtin_t *b; char *path = NULL; bool didfork, returning, saw_exec, saw_builtin; av = list2array(s, dashex); saw_builtin = saw_exec = FALSE; do { if (*av == NULL || isabsolute(*av)) b = NULL; else if (!saw_builtin && fnlookup(*av) != NULL) b = funcall; else b = isbuiltin(*av); /* a builtin applies only to the immmediately following command, e.g., builtin exec echo hi */ saw_builtin = FALSE; if (b == b_exec) { av++; saw_exec = TRUE; parent = FALSE; } else if (b == b_builtin) { av++; saw_builtin = TRUE; } } while (b == b_exec || b == b_builtin); if (*av == NULL && saw_exec) { /* do redirs and return on a null exec */ doredirs(); return; } /* force an exit on exec with any rc_error, but not for null commands as above */ if (saw_exec) rc_pid = -1; if (b == NULL) { path = which(*av, TRUE); if (path == NULL && *av != NULL) { /* perform null commands for redirections */ set(FALSE); redirq = NULL; if (parent) return; rc_exit(1); } ev = makeenv(); /* environment only needs to be built for execve() */ } /* If parent & the redirq is nonnull, builtin or not it has to fork. If the fifoq is nonnull, then it must be emptied at the end so we must fork no matter what. */ if ((parent && (b == NULL || redirq != NULL)) || outstanding_cmdarg()) { pid = rc_fork(); didfork = TRUE; } else { pid = 0; didfork = FALSE; } returning = (!didfork && parent); switch (pid) { case -1: uerror("fork"); rc_error(NULL); /* NOTREACHED */ case 0: if (!returning) setsigdefaults(FALSE); pop_cmdarg(FALSE); doredirs(); /* null commands performed for redirections */ if (*av == NULL || b != NULL) { if (b != NULL) (*b)(av); if (returning) return; rc_exit(getstatus()); } execve(path, (char * const *) av, (char * const *) ev); uerror(*av); rc_exit(1); /* NOTREACHED */ default: redirq = NULL; rc_wait4(pid, &stat, TRUE); setstatus(-1, stat); if ((stat & 0xff) == 0) nl_on_intr = FALSE; SIGCHK; nl_on_intr = TRUE; pop_cmdarg(TRUE); } }
extern bool walk(Node *n, bool parent) { top: sigchk(); if (n == NULL) { if (!parent) exit(0); set(TRUE); return TRUE; } switch (n->type) { case nArgs: case nBackq: case nConcat: case nCount: case nFlat: case nLappend: case nRedir: case nVar: case nVarsub: case nWord: exec(glob(glom(n)), parent); /* simple command */ break; case nBody: walk(n->u[0].p, TRUE); WALK(n->u[1].p, parent); /* WALK doesn't fall through */ case nNowait: { int pid; if ((pid = rc_fork()) == 0) { #if defined(RC_JOB) && defined(SIGTTOU) && defined(SIGTTIN) && defined(SIGTSTP) setsigdefaults(FALSE); rc_signal(SIGTTOU, SIG_IGN); /* Berkeleyized version: put it in a new pgroup. */ rc_signal(SIGTTIN, SIG_IGN); rc_signal(SIGTSTP, SIG_IGN); setpgid(0, getpid()); #else setsigdefaults(TRUE); /* ignore SIGINT, SIGQUIT, SIGTERM */ #endif mvfd(rc_open("/dev/null", rFrom), 0); walk(n->u[0].p, FALSE); exit(getstatus()); } if (interactive) fprint(2, "%d\n", pid); varassign("apid", word(nprint("%d", pid), NULL), FALSE); redirq = NULL; /* kill pre-redir queue */ break; } case nAndalso: { bool oldcond = cond; cond = TRUE; if (walk(n->u[0].p, TRUE)) { cond = oldcond; WALK(n->u[1].p, parent); } else cond = oldcond; break; } case nOrelse: { bool oldcond = cond; cond = TRUE; if (!walk(n->u[0].p, TRUE)) { cond = oldcond; WALK(n->u[1].p, parent); } else cond = oldcond; break; } case nBang: set(!walk(n->u[0].p, TRUE)); break; case nIf: { bool oldcond = cond; Node *true_cmd = n->u[1].p, *false_cmd = NULL; if (true_cmd != NULL && true_cmd->type == nElse) { false_cmd = true_cmd->u[1].p; true_cmd = true_cmd->u[0].p; } cond = TRUE; if (!walk(n->u[0].p, TRUE)) true_cmd = false_cmd; /* run the else clause */ cond = oldcond; WALK(true_cmd, parent); } case nWhile: { Jbwrap j; Edata jbreak; Estack e1, e2; bool testtrue, oldcond = cond; cond = TRUE; if (!walk(n->u[0].p, TRUE)) { /* prevent spurious breaks inside test */ cond = oldcond; break; } if (sigsetjmp(j.j, 1)) break; jbreak.jb = &j; except(eBreak, jbreak, &e1); do { Edata block; block.b = newblock(); cond = oldcond; except(eArena, block, &e2); walk(n->u[1].p, TRUE); testtrue = walk(n->u[0].p, TRUE); unexcept(); /* eArena */ cond = TRUE; } while (testtrue); cond = oldcond; unexcept(); /* eBreak */ break; } case nForin: { List *l, *var = glom(n->u[0].p); Jbwrap j; Estack e1, e2; Edata jbreak; if (sigsetjmp(j.j, 1)) break; jbreak.jb = &j; except(eBreak, jbreak, &e1); for (l = listcpy(glob(glom(n->u[1].p)), nalloc); l != NULL; l = l->n) { Edata block; assign(var, word(l->w, NULL), FALSE); block.b = newblock(); except(eArena, block, &e2); walk(n->u[2].p, TRUE); unexcept(); /* eArena */ } unexcept(); /* eBreak */ break; } case nSubshell: if (dofork(TRUE)) { setsigdefaults(FALSE); walk(n->u[0].p, FALSE); rc_exit(getstatus()); } break; case nAssign: if (n->u[0].p == NULL) rc_error("null variable name"); assign(glom(n->u[0].p), glob(glom(n->u[1].p)), FALSE); set(TRUE); break; case nPipe: dopipe(n); break; case nNewfn: { List *l = glom(n->u[0].p); if (l == NULL) rc_error("null function name"); while (l != NULL) { if (dashex) prettyprint_fn(2, l->w, n->u[1].p); fnassign(l->w, n->u[1].p); l = l->n; } set(TRUE); break; } case nRmfn: { List *l = glom(n->u[0].p); while (l != NULL) { if (dashex) fprint(2, "fn %S\n", l->w); fnrm(l->w); l = l->n; } set(TRUE); break; } case nDup: redirq = NULL; break; /* Null command */ case nMatch: { List *a = glob(glom(n->u[0].p)), *b = glom(n->u[1].p); if (dashex) fprint(2, (a != NULL && a->n != NULL) ? "~ (%L) %L\n" : "~ %L %L\n", a, " ", b, " "); set(lmatch(a, b)); break; } case nSwitch: { List *v = glom(n->u[0].p); while (1) { do { n = n->u[1].p; if (n == NULL) return istrue(); } while (n->u[0].p == NULL || n->u[0].p->type != nCase); if (lmatch(v, glom(n->u[0].p->u[0].p))) { for (n = n->u[1].p; n != NULL && (n->u[0].p == NULL || n->u[0].p->type != nCase); n = n->u[1].p) walk(n->u[0].p, TRUE); break; } } break; } case nPre: { List *v; if (n->u[0].p->type == nRedir || n->u[0].p->type == nDup) { if (redirq == NULL && !dofork(parent)) /* subshell on first preredir */ break; setsigdefaults(FALSE); qredir(n->u[0].p); if (!haspreredir(n->u[1].p)) doredirs(); /* no more preredirs, empty queue */ walk(n->u[1].p, FALSE); rc_exit(getstatus()); /* NOTREACHED */ } else if (n->u[0].p->type == nAssign) { if (isallpre(n->u[1].p)) { walk(n->u[0].p, TRUE); WALK(n->u[1].p, parent); } else { Estack e; Edata var; v = glom(n->u[0].p->u[0].p); assign(v, glob(glom(n->u[0].p->u[1].p)), TRUE); var.name = v->w; except(eVarstack, var, &e); walk(n->u[1].p, parent); varrm(v->w, TRUE); unexcept(); /* eVarstack */ } } else panic("unexpected node in preredir section of walk"); break; } case nBrace: if (n->u[1].p == NULL) { WALK(n->u[0].p, parent); } else if (dofork(parent)) { setsigdefaults(FALSE); walk(n->u[1].p, TRUE); /* Do redirections */ redirq = NULL; /* Reset redirection queue */ walk(n->u[0].p, FALSE); /* Do commands */ rc_exit(getstatus()); /* NOTREACHED */ } break; case nEpilog: qredir(n->u[0].p); if (n->u[1].p != NULL) { WALK(n->u[1].p, parent); /* Do more redirections. */ } else { doredirs(); /* Okay, we hit the bottom. */ } break; case nNmpipe: rc_error("named pipes cannot be executed as commands"); /* NOTREACHED */ default: panic("unknown node in walk"); /* NOTREACHED */ } return istrue(); }
static void b_exit(char **av) { if (*++av != NULL) ssetstatus(av); rc_exit(getstatus()); }