/* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #if !defined(lint) && defined(DOSCCS) static char *sccsid = "@(#)sh.func.c 5.3 (Berkeley) 5/13/86"; #endif #include "sh.h" #include /* * C shell */ struct biltins * isbfunc(t) struct command *t; { register char *cp = t->t_dcom[0]; register struct biltins *bp, *bp1, *bp2; int dolabel(), dofg1(), dobg1(); static struct biltins label = { "", dolabel, 0, 0 }; static struct biltins foregnd = { "%job", dofg1, 0, 0 }; static struct biltins backgnd = { "%job &", dobg1, 0, 0 }; if (lastchr(cp) == ':') { label.bname = cp; return (&label); } if (*cp == '%') { if (t->t_dflg & FAND) { t->t_dflg &= ~FAND; backgnd.bname = cp; return (&backgnd); } foregnd.bname = cp; return (&foregnd); } /* * Binary search * Bp1 is the beginning of the current search range. * Bp2 is one past the end. */ for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2;) { register i; bp = bp1 + (bp2 - bp1 >> 1); if ((i = *cp - *bp->bname) == 0 && (i = strcmp(cp, bp->bname)) == 0) return bp; if (i < 0) bp2 = bp; else bp1 = bp + 1; } return (0); } func(t, bp) register struct command *t; register struct biltins *bp; { int i; xechoit(t->t_dcom); setname(bp->bname); i = blklen(t->t_dcom) - 1; if (i < bp->minargs) bferr("Too few arguments"); if (i > bp->maxargs) bferr("Too many arguments"); (*bp->bfunct)(t->t_dcom, t); } dolabel() { } doonintr(v) char **v; { register char *cp; register char *vv = v[1]; if (parintr == SIG_IGN) return; if (setintr && intty) bferr("Can't from terminal"); cp = gointr, gointr = 0, xfree(cp); if (vv == 0) { if (setintr) (void) sigblock(sigmask(SIGINT)); else (void) signal(SIGINT, SIG_DFL); gointr = 0; } else if (eq((vv = strip(vv)), "-")) { (void) signal(SIGINT, SIG_IGN); gointr = "-"; } else { gointr = savestr(vv); (void) signal(SIGINT, pintr); } } donohup() { if (intty) bferr("Can't from terminal"); if (setintr == 0) { (void) signal(SIGHUP, SIG_IGN); #ifdef CC submit(getpid()); #endif } } dozip() { ; } prvars() { plist(&shvhed); } doalias(v) register char **v; { register struct varent *vp; register char *p; v++; p = *v++; if (p == 0) plist(&aliases); else if (*v == 0) { vp = adrof1(strip(p), &aliases); if (vp) blkpr(vp->vec), printf("\n"); } else { if (eq(p, "alias") || eq(p, "unalias")) { setname(p); bferr("Too dangerous to alias that"); } set1(strip(p), saveblk(v), &aliases); } } unalias(v) char **v; { unset1(v, &aliases); } dologout() { islogin(); goodbye(); } dologin(v) char **v; { islogin(); rechist(); (void) signal(SIGTERM, parterm); execl("/bin/login", "login", v[1], 0); untty(); exit(1); } #ifdef NEWGRP donewgrp(v) char **v; { if (chkstop == 0 && setintr) panystop(0); (void) signal(SIGTERM, parterm); execl("/bin/newgrp", "newgrp", v[1], 0); execl("/usr/bin/newgrp", "newgrp", v[1], 0); untty(); exit(1); } #endif islogin() { if (chkstop == 0 && setintr) panystop(0); if (loginsh) return; error("Not login shell"); } doif(v, kp) char **v; struct command *kp; { register int i; register char **vv; v++; i = exp(&v); vv = v; if (*vv == NOSTR) bferr("Empty if"); if (eq(*vv, "then")) { if (*++vv) bferr("Improper then"); setname("then"); /* * If expression was zero, then scan to else, * otherwise just fall into following code. */ if (!i) search(ZIF, 0); return; } /* * Simple command attached to this if. * Left shift the node in this tree, munging it * so we can reexecute it. */ if (i) { lshift(kp->t_dcom, vv - kp->t_dcom); reexecute(kp); donefds(); } } /* * Reexecute a command, being careful not * to redo i/o redirection, which is already set up. */ reexecute(kp) register struct command *kp; { kp->t_dflg &= FSAVE; kp->t_dflg |= FREDO; /* * If tty is still ours to arbitrate, arbitrate it; * otherwise dont even set pgrp's as the jobs would * then have no way to get the tty (we can't give it * to them, and our parent wouldn't know their pgrp, etc. */ execute(kp, tpgrp > 0 ? tpgrp : -1); } doelse() { search(ZELSE, 0); } dogoto(v) char **v; { register struct whyle *wp; char *lp; /* * While we still can, locate any unknown ends of existing loops. * This obscure code is the WORST result of the fact that we * don't really parse. */ for (wp = whyles; wp; wp = wp->w_next) if (wp->w_end == 0) { search(ZBREAK, 0); wp->w_end = btell(); } else bseek(wp->w_end); search(ZGOTO, 0, lp = globone(v[1])); xfree(lp); /* * Eliminate loops which were exited. */ wfree(); } doswitch(v) register char **v; { register char *cp, *lp; v++; if (!*v || *(*v++) != '(') goto syntax; cp = **v == ')' ? "" : *v++; if (*(*v++) != ')') v--; if (*v) syntax: error("Syntax error"); search(ZSWITCH, 0, lp = globone(cp)); xfree(lp); } dobreak() { if (whyles) toend(); else bferr("Not in while/foreach"); } doexit(v) char **v; { if (chkstop == 0) panystop(0); /* * Don't DEMAND parentheses here either. */ v++; if (*v) { set("status", putn(exp(&v))); if (*v) bferr("Expression syntax"); } btoeof(); if (intty) (void) close(SHIN); } doforeach(v) register char **v; { register char *cp; register struct whyle *nwp; v++; cp = strip(*v); while (*cp && letter(*cp)) cp++; if (*cp || strlen(*v) >= 20) bferr("Invalid variable"); cp = *v++; if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')') bferr("Words not ()'ed"); v++; gflag = 0, tglob(v); v = glob(v); if (v == 0) bferr("No match"); nwp = (struct whyle *) calloc(1, sizeof *nwp); nwp->w_fe = nwp->w_fe0 = v; gargv = 0; nwp->w_start = btell(); nwp->w_fename = savestr(cp); nwp->w_next = whyles; whyles = nwp; /* * Pre-read the loop so as to be more * comprehensible to a terminal user. */ if (intty) preread(); doagain(); } dowhile(v) char **v; { register int status; register bool again = whyles != 0 && whyles->w_start == lineloc && whyles->w_fename == 0; v++; /* * Implement prereading here also, taking care not to * evaluate the expression before the loop has been read up * from a terminal. */ if (intty && !again) status = !exp0(&v, 1); else status = !exp(&v); if (*v) bferr("Expression syntax"); if (!again) { register struct whyle *nwp = (struct whyle *) calloc(1, sizeof (*nwp)); nwp->w_start = lineloc; nwp->w_end = 0; nwp->w_next = whyles; whyles = nwp; if (intty) { /* * The tty preread */ preread(); doagain(); return; } } if (status) /* We ain't gonna loop no more, no more! */ toend(); } preread() { whyles->w_end = -1; if (setintr) (void) sigsetmask(sigblock(0L) & ~sigmask(SIGINT)); search(ZBREAK, 0); if (setintr) (void) sigblock(sigmask(SIGINT)); whyles->w_end = btell(); } doend() { if (!whyles) bferr("Not in while/foreach"); whyles->w_end = btell(); doagain(); } docontin() { if (!whyles) bferr("Not in while/foreach"); doagain(); } doagain() { /* Repeating a while is simple */ if (whyles->w_fename == 0) { bseek(whyles->w_start); return; } /* * The foreach variable list actually has a spurious word * ")" at the end of the w_fe list. Thus we are at the * of the list if one word beyond this is 0. */ if (!whyles->w_fe[1]) { dobreak(); return; } set(whyles->w_fename, savestr(*whyles->w_fe++)); bseek(whyles->w_start); } dorepeat(v, kp) char **v; struct command *kp; { register int i; register long omask; i = getn(v[1]); if (setintr) omask = sigblock(sigmask(SIGINT)) & ~sigmask(SIGINT); lshift(v, 2); while (i > 0) { if (setintr) (void) sigsetmask(omask); reexecute(kp); --i; } donefds(); if (setintr) (void) sigsetmask(omask); } doswbrk() { search(ZBRKSW, 0); } srchx(cp) register char *cp; { register struct srch *sp, *sp1, *sp2; register i; /* * Binary search * Sp1 is the beginning of the current search range. * Sp2 is one past the end. */ for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2;) { sp = sp1 + (sp2 - sp1 >> 1); if ((i = *cp - *sp->s_name) == 0 && (i = strcmp(cp, sp->s_name)) == 0) return sp->s_value; if (i < 0) sp2 = sp; else sp1 = sp + 1; } return (-1); } char Stype; char *Sgoal; /*VARARGS2*/ search(type, level, goal) int type; register int level; char *goal; { char wordbuf[BUFSIZ]; register char *aword = wordbuf; register char *cp; Stype = type; Sgoal = goal; if (type == ZGOTO) bseek((off_t)0); do { if (intty && fseekp == feobp) printf("? "), flush(); aword[0] = 0; (void) getword(aword); switch (srchx(aword)) { case ZELSE: if (level == 0 && type == ZIF) return; break; case ZIF: while (getword(aword)) continue; if ((type == ZIF || type == ZELSE) && eq(aword, "then")) level++; break; case ZENDIF: if (type == ZIF || type == ZELSE) level--; break; case ZFOREACH: case ZWHILE: if (type == ZBREAK) level++; break; case ZEND: if (type == ZBREAK) level--; break; case ZSWITCH: if (type == ZSWITCH || type == ZBRKSW) level++; break; case ZENDSW: if (type == ZSWITCH || type == ZBRKSW) level--; break; case ZLABEL: if (type == ZGOTO && getword(aword) && eq(aword, goal)) level = -1; break; default: if (type != ZGOTO && (type != ZSWITCH || level != 0)) break; if (lastchr(aword) != ':') break; aword[strlen(aword) - 1] = 0; if (type == ZGOTO && eq(aword, goal) || type == ZSWITCH && eq(aword, "default")) level = -1; break; case ZCASE: if (type != ZSWITCH || level != 0) break; (void) getword(aword); if (lastchr(aword) == ':') aword[strlen(aword) - 1] = 0; cp = strip(Dfix1(aword)); if (Gmatch(goal, cp)) level = -1; xfree(cp); break; case ZDEFAULT: if (type == ZSWITCH && level == 0) level = -1; break; } (void) getword(NOSTR); } while (level >= 0); } getword(wp) register char *wp; { register int found = 0; register int c, d; c = readc(1); d = 0; do { while (c == ' ' || c == '\t') c = readc(1); if (c == '#') do c = readc(1); while (c >= 0 && c != '\n'); if (c < 0) goto past; if (c == '\n') { if (wp) break; return (0); } unreadc(c); found = 1; do { c = readc(1); if (c == '\\' && (c = readc(1)) == '\n') c = ' '; if (c == '\'' || c == '"') if (d == 0) d = c; else if (d == c) d = 0; if (c < 0) goto past; if (wp) *wp++ = c; } while ((d || c != ' ' && c != '\t') && c != '\n'); } while (wp == 0); unreadc(c); if (found) *--wp = 0; return (found); past: switch (Stype) { case ZIF: bferr("then/endif not found"); case ZELSE: bferr("endif not found"); case ZBRKSW: case ZSWITCH: bferr("endsw not found"); case ZBREAK: bferr("end not found"); case ZGOTO: setname(Sgoal); bferr("label not found"); } /*NOTREACHED*/ } toend() { if (whyles->w_end == 0) { search(ZBREAK, 0); whyles->w_end = btell() - 1; } else bseek(whyles->w_end); wfree(); } wfree() { long o = btell(); while (whyles) { register struct whyle *wp = whyles; register struct whyle *nwp = wp->w_next; if (o >= wp->w_start && (wp->w_end == 0 || o < wp->w_end)) break; if (wp->w_fe0) blkfree(wp->w_fe0); if (wp->w_fename) xfree(wp->w_fename); xfree((char *)wp); whyles = nwp; } } doecho(v) char **v; { echo(' ', v); } doglob(v) char **v; { echo(0, v); flush(); } echo(sep, v) char sep; register char **v; { register char *cp; int nonl = 0; if (setintr) (void) sigsetmask(sigblock(0L) & ~sigmask(SIGINT)); v++; if (*v == 0) return; gflag = 0, tglob(v); if (gflag) { v = glob(v); if (v == 0) bferr("No match"); } else trim(v); if (sep == ' ' && *v && !strcmp(*v, "-n")) nonl++, v++; while (cp = *v++) { register int c; while (c = *cp++) putchar(c | QUOTE); if (*v) putchar(sep | QUOTE); } if (sep && nonl == 0) putchar('\n'); else flush(); if (setintr) (void) sigblock(sigmask(SIGINT)); if (gargv) blkfree(gargv), gargv = 0; } char **environ; dosetenv(v) register char **v; { char *vp, *lp; v++; if ((vp = *v++) == 0) { register char **ep; if (setintr) (void) sigsetmask(sigblock(0L) & ~ sigmask(SIGINT)); for (ep = environ; *ep; ep++) printf("%s\n", *ep); return; } if ((lp = *v++) == 0) lp = ""; setenv(vp, lp = globone(lp)); if (eq(vp, "PATH")) { importpath(lp); dohash(); } xfree(lp); } dounsetenv(v) register char **v; { v++; do unsetenv(*v++); while (*v); } setenv(name, val) char *name, *val; { register char **ep = environ; register char *cp, *dp; char *blk[2], **oep = ep; for (; *ep; ep++) { for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++) continue; if (*cp != 0 || *dp != '=') continue; cp = strspl("=", val); xfree(*ep); *ep = strspl(name, cp); xfree(cp); trim(ep); return; } blk[0] = strspl(name, "="); blk[1] = 0; environ = blkspl(environ, blk); xfree((char *)oep); setenv(name, val); } unsetenv(name) char *name; { register char **ep = environ; register char *cp, *dp; char **oep = ep; for (; *ep; ep++) { for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++) continue; if (*cp != 0 || *dp != '=') continue; cp = *ep; *ep = 0; environ = blkspl(environ, ep+1); *ep = cp; xfree(cp); xfree((char *)oep); return; } } doumask(v) register char **v; { register char *cp = v[1]; register int i; if (cp == 0) { i = umask(0); (void) umask(i); printf("%o\n", i); return; } i = 0; while (digit(*cp) && *cp != '8' && *cp != '9') i = i * 8 + *cp++ - '0'; if (*cp || i < 0 || i > 0777) bferr("Improper mask"); (void) umask(i); } #ifndef NOLIMITS struct limits { int limconst; char *limname; int limdiv; char *limscale; } limits[] = { RLIMIT_CPU, "cputime", 1, "seconds", RLIMIT_FSIZE, "filesize", 1024, "kbytes", RLIMIT_DATA, "datasize", 1024, "kbytes", RLIMIT_STACK, "stacksize", 1024, "kbytes", RLIMIT_CORE, "coredumpsize", 1024, "kbytes", RLIMIT_RSS, "memoryuse", 1024, "kbytes", -1, 0, }; struct limits * findlim(cp) char *cp; { register struct limits *lp, *res; res = 0; for (lp = limits; lp->limconst >= 0; lp++) if (prefix(cp, lp->limname)) { if (res) bferr("Ambiguous"); res = lp; } if (res) return (res); bferr("No such limit"); /*NOTREACHED*/ } dolimit(v) register char **v; { register struct limits *lp; register long limit; long getval(); char hard = 0; v++; if (*v && eq(*v, "-h")) { hard = 1; v++; } if (*v == 0) { for (lp = limits; lp->limconst >= 0; lp++) plim(lp, hard); return; } lp = findlim(v[0]); if (v[1] == 0) { plim(lp, hard); return; } limit = getval(lp, v+1); if (setlim(lp, hard, limit) < 0) error(NOSTR); } long getval(lp, v) register struct limits *lp; char **v; { register float f; double atof(); char *cp = *v++; f = atof(cp); while (digit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E') cp++; if (*cp == 0) { if (*v == 0) return ((long)(f+0.5) * lp->limdiv); cp = *v; } switch (*cp) { case ':': if (lp->limconst != RLIMIT_CPU) goto badscal; return ((long)(f * 60.0 + atof(cp+1))); case 'h': if (lp->limconst != RLIMIT_CPU) goto badscal; limtail(cp, "hours"); f *= 3600.; break; case 'm': if (lp->limconst == RLIMIT_CPU) { limtail(cp, "minutes"); f *= 60.; break; } case 'M': if (lp->limconst == RLIMIT_CPU) goto badscal; *cp = 'm'; limtail(cp, "megabytes"); f *= 1024.*1024.; break; case 's': if (lp->limconst != RLIMIT_CPU) goto badscal; limtail(cp, "seconds"); break; case 'k': if (lp->limconst == RLIMIT_CPU) goto badscal; limtail(cp, "kbytes"); f *= 1024; break; case 'u': limtail(cp, "unlimited"); return (RLIM_INFINITY); default: badscal: bferr("Improper or unknown scale factor"); } return ((long)(f+0.5)); } limtail(cp, str0) char *cp, *str0; { register char *str = str0; while (*cp && *cp == *str) cp++, str++; if (*cp) error("Bad scaling; did you mean ``%s''?", str0); } plim(lp, hard) register struct limits *lp; char hard; { struct rlimit rlim; long limit; printf("%s \t", lp->limname); (void) getrlimit(lp->limconst, &rlim); limit = hard ? rlim.rlim_max : rlim.rlim_cur; if (limit == RLIM_INFINITY) printf("unlimited"); else if (lp->limconst == RLIMIT_CPU) psecs((long)limit); else printf("%ld %s", limit / lp->limdiv, lp->limscale); printf("\n"); } dounlimit(v) register char **v; { register struct limits *lp; int err = 0; char hard = 0; v++; if (*v && eq(*v, "-h")) { hard = 1; v++; } if (*v == 0) { for (lp = limits; lp->limconst >= 0; lp++) if (setlim(lp, hard, (long)RLIM_INFINITY) < 0) err++; if (err) error(NOSTR); return; } while (*v) { lp = findlim(*v++); if (setlim(lp, hard, (long)RLIM_INFINITY) < 0) error(NOSTR); } } setlim(lp, hard, limit) register struct limits *lp; char hard; long limit; { struct rlimit rlim; (void) getrlimit(lp->limconst, &rlim); if (hard) rlim.rlim_max = limit; else if (limit == RLIM_INFINITY && geteuid() != 0) rlim.rlim_cur = rlim.rlim_max; else rlim.rlim_cur = limit; if (setrlimit(lp->limconst, &rlim) < 0) { printf("%s: %s: Can't %s%s limit\n", bname, lp->limname, limit == RLIM_INFINITY ? "remove" : "set", hard ? " hard" : ""); return (-1); } return (0); } #endif !NOLIMITS dosuspend() { int ldisc, ctpgrp; int (*old)(); if (loginsh) error("Can't suspend a login shell (yet)"); untty(); old = signal(SIGTSTP, SIG_DFL); (void) kill(0, SIGTSTP); /* the shell stops here */ (void) signal(SIGTSTP, old); if (tpgrp != -1) { retry: (void) ioctl(FSHTTY, TIOCGPGRP, (char *)&ctpgrp); if (ctpgrp != opgrp) { old = signal(SIGTTIN, SIG_DFL); (void) kill(0, SIGTTIN); (void) signal(SIGTTIN, old); goto retry; } (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&shpgrp); (void) setpgrp(0, shpgrp); } (void) ioctl(FSHTTY, TIOCGETD, (char *)&oldisc); if (oldisc != NTTYDISC) { printf("Switching to new tty driver...\n"); ldisc = NTTYDISC; (void) ioctl(FSHTTY, TIOCSETD, (char *)&ldisc); } } doeval(v) char **v; { char **oevalvec = evalvec; char *oevalp = evalp; jmp_buf osetexit; int reenter; char **gv = 0; v++; if (*v == 0) return; gflag = 0, tglob(v); if (gflag) { gv = v = glob(v); gargv = 0; if (v == 0) error("No match"); v = copyblk(v); } else trim(v); getexit(osetexit); reenter = 0; setexit(); reenter++; if (reenter == 1) { evalvec = v; evalp = 0; process(0); } evalvec = oevalvec; evalp = oevalp; doneinp = 0; if (gv) blkfree(gv); resexit(osetexit); if (reenter >= 2) error(NOSTR); }