diff --git a/bin/sh/histedit.c b/bin/sh/histedit.c index 56f4d02dc558..6d5e2808bbd7 100644 --- a/bin/sh/histedit.c +++ b/bin/sh/histedit.c @@ -1,480 +1,480 @@ /*- * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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. * - * $Id: histedit.c,v 1.4 1995/05/30 00:07:14 rgrimes Exp $ + * $Id: histedit.c,v 1.5 1996/09/01 10:20:12 peter Exp $ */ #ifndef lint static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; #endif /* not lint */ #include #include #include #include #include /* * Editline and history functions (and glue). */ #include "shell.h" #include "parser.h" #include "var.h" #include "options.h" #include "main.h" #include "output.h" #include "mystring.h" #ifndef NO_HISTORY #include "myhistedit.h" #endif #include "error.h" #include "eval.h" #include "memalloc.h" #define MAXHISTLOOPS 4 /* max recursions through fc */ #define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ History *hist; /* history cookie */ EditLine *el; /* editline cookie */ int displayhist; static FILE *el_in, *el_out; STATIC char *fc_replace __P((const char *, char *, char *)); /* * Set history and editing status. Called whenever the status may * have changed (figures out what to do). */ void histedit() { #define editing (Eflag || Vflag) if (iflag) { if (!hist) { /* * turn history on */ INTOFF; hist = history_init(); INTON; if (hist != NULL) sethistsize(); else out2str("sh: can't initialize history\n"); } if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ /* * turn editing on */ INTOFF; if (el_in == NULL) el_in = fdopen(0, "r"); if (el_out == NULL) el_out = fdopen(2, "w"); if (el_in == NULL || el_out == NULL) goto bad; el = el_init(arg0, el_in, el_out); if (el != NULL) { if (hist) el_set(el, EL_HIST, history, hist); el_set(el, EL_PROMPT, getprompt); } else { bad: out2str("sh: can't initialize editing\n"); } INTON; } else if (!editing && el) { INTOFF; el_end(el); el = NULL; INTON; } if (el) { if (Vflag) el_set(el, EL_EDITOR, "vi"); else if (Eflag) el_set(el, EL_EDITOR, "emacs"); } } else { INTOFF; if (el) { /* no editing if not interactive */ el_end(el); el = NULL; } if (hist) { history_end(hist); hist = NULL; } INTON; } } void sethistsize() { char *cp; int histsize; if (hist != NULL) { cp = lookupvar("HISTSIZE"); if (cp == NULL || *cp == '\0' || (histsize = atoi(cp)) < 0) histsize = 100; history(hist, H_EVENT, histsize); } } /* * This command is provided since POSIX decided to standardize * the Korn shell fc command. Oh well... */ int histcmd(argc, argv) int argc; char **argv; { extern char *optarg; extern int optind, optopt, optreset; int ch; char *editor = NULL; const HistEvent *he; int lflg = 0, nflg = 0, rflg = 0, sflg = 0; int i; char *firststr, *laststr; int first, last, direction; char *pat = NULL, *repl; /* ksh "fc old=new" crap */ static int active = 0; struct jmploc jmploc; struct jmploc *volatile savehandler; char editfile[MAXPATHLEN + 1]; FILE *efp; #ifdef __GNUC__ /* Avoid longjmp clobbering */ (void) &editor; (void) &lflg; (void) &nflg; (void) &rflg; (void) &sflg; (void) &firststr; (void) &laststr; (void) &pat; (void) &repl; (void) &efp; (void) &argc; (void) &argv; #endif if (hist == NULL) error("history not active"); if (argc == 1) error("missing history argument"); optreset = 1; optind = 1; /* initialize getopt */ while (not_fcnumber(argv[optind]) && (ch = getopt(argc, argv, ":e:lnrs")) != EOF) switch ((char)ch) { case 'e': editor = optarg; break; case 'l': lflg = 1; break; case 'n': nflg = 1; break; case 'r': rflg = 1; break; case 's': sflg = 1; break; case ':': error("option -%c expects argument", optopt); case '?': default: error("unknown option: -%c", optopt); } argc -= optind, argv += optind; /* * If executing... */ if (lflg == 0 || editor || sflg) { lflg = 0; /* ignore */ editfile[0] = '\0'; /* * Catch interrupts to reset active counter and * cleanup temp files. */ if (setjmp(jmploc.loc)) { active = 0; if (*editfile) unlink(editfile); handler = savehandler; longjmp(handler->loc, 1); } savehandler = handler; handler = &jmploc; if (++active > MAXHISTLOOPS) { active = 0; displayhist = 0; error("called recursively too many times"); } /* * Set editor. */ if (sflg == 0) { if (editor == NULL && (editor = bltinlookup("FCEDIT", 1)) == NULL && (editor = bltinlookup("EDITOR", 1)) == NULL) editor = DEFEDITOR; if (editor[0] == '-' && editor[1] == '\0') { sflg = 1; /* no edit */ editor = NULL; } } } /* * If executing, parse [old=new] now */ if (lflg == 0 && argc > 0 && ((repl = strchr(argv[0], '=')) != NULL)) { pat = argv[0]; *repl++ = '\0'; argc--, argv++; } /* * determine [first] and [last] */ switch (argc) { case 0: firststr = lflg ? "-16" : "-1"; laststr = "-1"; break; case 1: firststr = argv[0]; laststr = lflg ? "-1" : argv[0]; break; case 2: firststr = argv[0]; laststr = argv[1]; break; default: error("too many args"); } /* * Turn into event numbers. */ first = str_to_event(firststr, 0); last = str_to_event(laststr, 1); if (rflg) { i = last; last = first; first = i; } /* * XXX - this should not depend on the event numbers * always increasing. Add sequence numbers or offset * to the history element in next (diskbased) release. */ direction = first < last ? H_PREV : H_NEXT; /* * If editing, grab a temp file. */ if (editor) { int fd; INTOFF; /* easier */ sprintf(editfile, "%s/_shXXXXXX", _PATH_TMP); if ((fd = mkstemp(editfile)) < 0) error("can't create temporary file %s", editfile); if ((efp = fdopen(fd, "w")) == NULL) { close(fd); - error("can't allocate stdio buffer for temp\n"); + error("can't allocate stdio buffer for temp"); } } /* * Loop through selected history events. If listing or executing, * do it now. Otherwise, put into temp file and call the editor * after. * * The history interface needs rethinking, as the following * convolutions will demonstrate. */ history(hist, H_FIRST); he = history(hist, H_NEXT_EVENT, first); for (;he != NULL; he = history(hist, direction)) { if (lflg) { if (!nflg) out1fmt("%5d ", he->num); out1str(he->str); } else { char *s = pat ? fc_replace(he->str, pat, repl) : (char *)he->str; if (sflg) { if (displayhist) { out2str(s); } evalstring(s); if (displayhist && hist) { /* * XXX what about recursive and * relative histnums. */ history(hist, H_ENTER, s); } } else fputs(s, efp); } /* * At end? (if we were to loose last, we'd sure be * messed up). */ if (he->num == last) break; } if (editor) { char *editcmd; fclose(efp); editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); sprintf(editcmd, "%s %s", editor, editfile); evalstring(editcmd); /* XXX - should use no JC command */ INTON; readcmdfile(editfile); /* XXX - should read back - quick tst */ unlink(editfile); } if (lflg == 0 && active > 0) --active; if (displayhist) displayhist = 0; return 0; } STATIC char * fc_replace(s, p, r) const char *s; char *p, *r; { char *dest; int plen = strlen(p); STARTSTACKSTR(dest); while (*s) { if (*s == *p && strncmp(s, p, plen) == 0) { while (*r) STPUTC(*r++, dest); s += plen; *p = '\0'; /* so no more matches */ } else STPUTC(*s++, dest); } STACKSTRNUL(dest); dest = grabstackstr(dest); return (dest); } int not_fcnumber(s) char *s; { if (s == NULL) { /* NULL is not a fc_number */ return (1); } if (*s == '-') s++; return (!is_number(s)); } int str_to_event(str, last) char *str; int last; { const HistEvent *he; char *s = str; int relative = 0; int i; he = history(hist, H_FIRST); switch (*s) { case '-': relative = 1; /*FALLTHROUGH*/ case '+': s++; } if (is_number(s)) { i = atoi(s); if (relative) { while (he != NULL && i--) { he = history(hist, H_NEXT); } if (he == NULL) he = history(hist, H_LAST); } else { he = history(hist, H_NEXT_EVENT, i); if (he == NULL) { /* * the notion of first and last is * backwards to that of the history package */ he = history(hist, last ? H_FIRST : H_LAST); } } if (he == NULL) error("history number %s not found (internal error)", str); } else { /* * pattern */ he = history(hist, H_PREV_STR, str); if (he == NULL) error("history pattern not found: %s", str); } return (he->num); } diff --git a/bin/sh/jobs.c b/bin/sh/jobs.c index 65dea1d033d6..005e2fd2049c 100644 --- a/bin/sh/jobs.c +++ b/bin/sh/jobs.c @@ -1,1086 +1,1086 @@ /*- * Copyright (c) 1991, 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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. * - * $Id: jobs.c,v 1.4 1995/09/21 13:24:20 bde Exp $ + * $Id: jobs.c,v 1.5 1996/09/01 10:20:24 peter Exp $ */ #ifndef lint static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95"; #endif /* not lint */ #include #include #include #include #include #include #include #ifdef BSD #include #include #include #endif #include "shell.h" #if JOBS #ifdef OLD_TTY_DRIVER #include "sgtty.h" #else #include #endif #undef CEOF /* syntax.h redefines this */ #endif #include "redir.h" #include "show.h" #include "main.h" #include "parser.h" #include "nodes.h" #include "jobs.h" #include "options.h" #include "trap.h" #include "syntax.h" #include "input.h" #include "output.h" #include "memalloc.h" #include "error.h" #include "mystring.h" struct job *jobtab; /* array of jobs */ int njobs; /* size of array */ MKINIT short backgndpid = -1; /* pid of last background process */ #if JOBS int initialpgrp; /* pgrp of shell on invocation */ short curjob; /* current job */ #endif STATIC void restartjob __P((struct job *)); STATIC void freejob __P((struct job *)); STATIC struct job *getjob __P((char *)); STATIC int dowait __P((int, struct job *)); STATIC int onsigchild __P((void)); STATIC int waitproc __P((int, int *)); STATIC void cmdtxt __P((union node *)); STATIC void cmdputs __P((char *)); /* * Turn job control on and off. * * Note: This code assumes that the third arg to ioctl is a character * pointer, which is true on Berkeley systems but not System V. Since * System V doesn't have job control yet, this isn't a problem now. */ MKINIT int jobctl; void setjobctl(on) int on; { #ifdef OLD_TTY_DRIVER int ldisc; #endif if (on == jobctl || rootshell == 0) return; if (on) { do { /* while we are in the background */ if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) { out2str("sh: can't access tty; job control turned off\n"); mflag = 0; return; } if (initialpgrp == -1) initialpgrp = getpgrp(); else if (initialpgrp != getpgrp()) { killpg(initialpgrp, SIGTTIN); continue; } } while (0); #ifdef OLD_TTY_DRIVER if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) { out2str("sh: need new tty driver to run job control; job control turned off\n"); mflag = 0; return; } #endif setsignal(SIGTSTP); setsignal(SIGTTOU); setsignal(SIGTTIN); setpgid(0, rootpid); ioctl(2, TIOCSPGRP, (char *)&rootpid); } else { /* turning job control off */ setpgid(0, initialpgrp); ioctl(2, TIOCSPGRP, (char *)&initialpgrp); setsignal(SIGTSTP); setsignal(SIGTTOU); setsignal(SIGTTIN); } jobctl = on; } #ifdef mkinit INCLUDE SHELLPROC { backgndpid = -1; #if JOBS jobctl = 0; #endif } #endif #if JOBS int fgcmd(argc, argv) int argc; char **argv; { struct job *jp; int pgrp; int status; jp = getjob(argv[1]); if (jp->jobctl == 0) error("job not created under job control"); pgrp = jp->ps[0].pid; ioctl(2, TIOCSPGRP, (char *)&pgrp); restartjob(jp); INTOFF; status = waitforjob(jp); INTON; return status; } int bgcmd(argc, argv) int argc; char **argv; { struct job *jp; do { jp = getjob(*++argv); if (jp->jobctl == 0) error("job not created under job control"); restartjob(jp); } while (--argc > 1); return 0; } STATIC void restartjob(jp) struct job *jp; { struct procstat *ps; int i; if (jp->state == JOBDONE) return; INTOFF; killpg(jp->ps[0].pid, SIGCONT); for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { if ((ps->status & 0377) == 0177) { ps->status = -1; jp->state = 0; } } INTON; } #endif int jobscmd(argc, argv) int argc; char **argv; { showjobs(0); return 0; } /* * Print a list of jobs. If "change" is nonzero, only print jobs whose * statuses have changed since the last call to showjobs. * * If the shell is interrupted in the process of creating a job, the * result may be a job structure containing zero processes. Such structures * will be freed here. */ void showjobs(change) int change; { int jobno; int procno; int i; struct job *jp; struct procstat *ps; int col; char s[64]; TRACE(("showjobs(%d) called\n", change)); while (dowait(0, (struct job *)NULL) > 0); for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { if (! jp->used) continue; if (jp->nprocs == 0) { freejob(jp); continue; } if (change && ! jp->changed) continue; procno = jp->nprocs; for (ps = jp->ps ; ; ps++) { /* for each process */ if (ps == jp->ps) fmtstr(s, 64, "[%d] %d ", jobno, ps->pid); else fmtstr(s, 64, " %d ", ps->pid); out1str(s); col = strlen(s); s[0] = '\0'; if (ps->status == -1) { /* don't print anything */ } else if ((ps->status & 0xFF) == 0) { fmtstr(s, 64, "Exit %d", ps->status >> 8); } else { i = ps->status; #if JOBS if ((i & 0xFF) == 0177) i >>= 8; #endif if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F]) scopy(sys_siglist[i & 0x7F], s); else fmtstr(s, 64, "Signal %d", i & 0x7F); if (i & 0x80) strcat(s, " (core dumped)"); } out1str(s); col += strlen(s); do { out1c(' '); col++; } while (col < 30); out1str(ps->cmd); out1c('\n'); if (--procno <= 0) break; } jp->changed = 0; if (jp->state == JOBDONE) { freejob(jp); } } } /* * Mark a job structure as unused. */ STATIC void freejob(jp) struct job *jp; { struct procstat *ps; int i; INTOFF; for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) { if (ps->cmd != nullstr) ckfree(ps->cmd); } if (jp->ps != &jp->ps0) ckfree(jp->ps); jp->used = 0; #if JOBS if (curjob == jp - jobtab + 1) curjob = 0; #endif INTON; } int waitcmd(argc, argv) int argc; char **argv; { struct job *job; int status; struct job *jp; if (argc > 1) { job = getjob(argv[1]); } else { job = NULL; } for (;;) { /* loop until process terminated or stopped */ if (job != NULL) { if (job->state) { status = job->ps[job->nprocs - 1].status; if ((status & 0xFF) == 0) status = status >> 8 & 0xFF; #if JOBS else if ((status & 0xFF) == 0177) status = (status >> 8 & 0x7F) + 128; #endif else status = (status & 0x7F) + 128; if (! iflag) freejob(job); return status; } } else { for (jp = jobtab ; ; jp++) { if (jp >= jobtab + njobs) { /* no running procs */ return 0; } if (jp->used && jp->state == 0) break; } } dowait(1, (struct job *)NULL); } } int jobidcmd(argc, argv) int argc; char **argv; { struct job *jp; int i; jp = getjob(argv[1]); for (i = 0 ; i < jp->nprocs ; ) { out1fmt("%d", jp->ps[i].pid); out1c(++i < jp->nprocs? ' ' : '\n'); } return 0; } /* * Convert a job name to a job structure. */ STATIC struct job * getjob(name) char *name; { int jobno; register struct job *jp; int pid; int i; if (name == NULL) { #if JOBS currentjob: if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0) error("No current job"); return &jobtab[jobno - 1]; #else error("No current job"); #endif } else if (name[0] == '%') { if (is_digit(name[1])) { jobno = number(name + 1); if (jobno > 0 && jobno <= njobs && jobtab[jobno - 1].used != 0) return &jobtab[jobno - 1]; #if JOBS } else if (name[1] == '%' && name[2] == '\0') { goto currentjob; #endif } else { register struct job *found = NULL; for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { if (jp->used && jp->nprocs > 0 && prefix(name + 1, jp->ps[0].cmd)) { if (found) error("%s: ambiguous", name); found = jp; } } if (found) return found; } } else if (is_number(name)) { pid = number(name); for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { if (jp->used && jp->nprocs > 0 && jp->ps[jp->nprocs - 1].pid == pid) return jp; } } error("No such job: %s", name); /*NOTREACHED*/ return NULL; } /* * Return a new job structure, */ struct job * makejob(node, nprocs) union node *node; int nprocs; { int i; struct job *jp; for (i = njobs, jp = jobtab ; ; jp++) { if (--i < 0) { INTOFF; if (njobs == 0) { jobtab = ckmalloc(4 * sizeof jobtab[0]); } else { struct job *ojp; jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); for (i = njobs, ojp = jobtab; --i >= 0; jp++, ojp++) if (ojp->ps == &ojp->ps0) ojp->ps = &jp->ps0; jp -= njobs; memcpy(jp, jobtab, njobs * sizeof jp[0]); ckfree(jobtab); jobtab = jp; } jp = jobtab + njobs; for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0); INTON; break; } if (jp->used == 0) break; } INTOFF; jp->state = 0; jp->used = 1; jp->changed = 0; jp->nprocs = 0; #if JOBS jp->jobctl = jobctl; #endif if (nprocs > 1) { jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); } else { jp->ps = &jp->ps0; } INTON; TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, jp - jobtab + 1)); return jp; } /* * Fork of a subshell. If we are doing job control, give the subshell its * own process group. Jp is a job structure that the job is to be added to. * N is the command that will be evaluated by the child. Both jp and n may * be NULL. The mode parameter can be one of the following: * FORK_FG - Fork off a foreground process. * FORK_BG - Fork off a background process. * FORK_NOJOB - Like FORK_FG, but don't give the process its own * process group even if job control is on. * * When job control is turned off, background processes have their standard * input redirected to /dev/null (except for the second and later processes * in a pipeline). */ int forkshell(jp, n, mode) union node *n; struct job *jp; int mode; { int pid; int pgrp; TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n, mode)); INTOFF; pid = fork(); if (pid == -1) { TRACE(("Fork failed, errno=%d\n", errno)); INTON; error("Cannot fork"); } if (pid == 0) { struct job *p; int wasroot; int i; TRACE(("Child shell %d\n", getpid())); wasroot = rootshell; rootshell = 0; for (i = njobs, p = jobtab ; --i >= 0 ; p++) if (p->used) freejob(p); closescript(); INTON; clear_traps(); #if JOBS jobctl = 0; /* do job control only in root shell */ if (wasroot && mode != FORK_NOJOB && mflag) { if (jp == NULL || jp->nprocs == 0) pgrp = getpid(); else pgrp = jp->ps[0].pid; setpgid(0, pgrp); if (mode == FORK_FG) { /*** this causes superfluous TIOCSPGRPS ***/ if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0) - error("TIOCSPGRP failed, errno=%d\n", errno); + error("TIOCSPGRP failed, errno=%d", errno); } setsignal(SIGTSTP); setsignal(SIGTTOU); } else if (mode == FORK_BG) { ignoresig(SIGINT); ignoresig(SIGQUIT); if ((jp == NULL || jp->nprocs == 0) && ! fd0_redirected_p ()) { close(0); if (open("/dev/null", O_RDONLY) != 0) error("Can't open /dev/null"); } } #else if (mode == FORK_BG) { ignoresig(SIGINT); ignoresig(SIGQUIT); if ((jp == NULL || jp->nprocs == 0) && ! fd0_redirected_p ()) { close(0); if (open("/dev/null", O_RDONLY) != 0) error("Can't open /dev/null"); } } #endif if (wasroot && iflag) { setsignal(SIGINT); setsignal(SIGQUIT); setsignal(SIGTERM); } return pid; } if (rootshell && mode != FORK_NOJOB && mflag) { if (jp == NULL || jp->nprocs == 0) pgrp = pid; else pgrp = jp->ps[0].pid; setpgid(pid, pgrp); } if (mode == FORK_BG) backgndpid = pid; /* set $! */ if (jp) { struct procstat *ps = &jp->ps[jp->nprocs++]; ps->pid = pid; ps->status = -1; ps->cmd = nullstr; if (iflag && rootshell && n) ps->cmd = commandtext(n); } INTON; TRACE(("In parent shell: child = %d\n", pid)); return pid; } /* * Wait for job to finish. * * Under job control we have the problem that while a child process is * running interrupts generated by the user are sent to the child but not * to the shell. This means that an infinite loop started by an inter- * active user may be hard to kill. With job control turned off, an * interactive user may place an interactive program inside a loop. If * the interactive program catches interrupts, the user doesn't want * these interrupts to also abort the loop. The approach we take here * is to have the shell ignore interrupt signals while waiting for a * forground process to terminate, and then send itself an interrupt * signal if the child process was terminated by an interrupt signal. * Unfortunately, some programs want to do a bit of cleanup and then * exit on interrupt; unless these processes terminate themselves by * sending a signal to themselves (instead of calling exit) they will * confuse this approach. */ int waitforjob(jp) register struct job *jp; { #if JOBS int mypgrp = getpgrp(); #endif int status; int st; INTOFF; TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); while (jp->state == 0) { dowait(1, jp); } #if JOBS if (jp->jobctl) { if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0) - error("TIOCSPGRP failed, errno=%d\n", errno); + error("TIOCSPGRP failed, errno=%d", errno); } if (jp->state == JOBSTOPPED) curjob = jp - jobtab + 1; #endif status = jp->ps[jp->nprocs - 1].status; /* convert to 8 bits */ if ((status & 0xFF) == 0) st = status >> 8 & 0xFF; #if JOBS else if ((status & 0xFF) == 0177) st = (status >> 8 & 0x7F) + 128; #endif else st = (status & 0x7F) + 128; if (! JOBS || jp->state == JOBDONE) freejob(jp); CLEAR_PENDING_INT; if ((status & 0x7F) == SIGINT) kill(getpid(), SIGINT); INTON; return st; } /* * Wait for a process to terminate. */ STATIC int dowait(block, job) int block; struct job *job; { int pid; int status; struct procstat *sp; struct job *jp; struct job *thisjob; int done; int stopped; int core; TRACE(("dowait(%d) called\n", block)); do { pid = waitproc(block, &status); TRACE(("wait returns %d, status=%d\n", pid, status)); } while (pid == -1 && errno == EINTR); if (pid <= 0) return pid; INTOFF; thisjob = NULL; for (jp = jobtab ; jp < jobtab + njobs ; jp++) { if (jp->used) { done = 1; stopped = 1; for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { if (sp->pid == -1) continue; if (sp->pid == pid) { TRACE(("Changin status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status)); sp->status = status; thisjob = jp; } if (sp->status == -1) stopped = 0; else if ((sp->status & 0377) == 0177) done = 0; } if (stopped) { /* stopped or done */ int state = done? JOBDONE : JOBSTOPPED; if (jp->state != state) { TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); jp->state = state; #if JOBS if (done && curjob == jp - jobtab + 1) curjob = 0; /* no current job */ #endif } } } } INTON; if (! rootshell || ! iflag || (job && thisjob == job)) { #if JOBS if ((status & 0xFF) == 0177) status >>= 8; #endif core = status & 0x80; status &= 0x7F; if (status != 0 && status != SIGINT && status != SIGPIPE) { if (thisjob != job) outfmt(out2, "%d: ", pid); #if JOBS if (status == SIGTSTP && rootshell && iflag) outfmt(out2, "%%%d ", job - jobtab + 1); #endif if (status < NSIG && sys_siglist[status]) out2str(sys_siglist[status]); else outfmt(out2, "Signal %d", status); if (core) out2str(" - core dumped"); out2c('\n'); flushout(&errout); } else { TRACE(("Not printing status: status=%d\n", status)); } } else { TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job)); if (thisjob) thisjob->changed = 1; } return pid; } /* * Do a wait system call. If job control is compiled in, we accept * stopped processes. If block is zero, we return a value of zero * rather than blocking. * * System V doesn't have a non-blocking wait system call. It does * have a SIGCLD signal that is sent to a process when one of it's * children dies. The obvious way to use SIGCLD would be to install * a handler for SIGCLD which simply bumped a counter when a SIGCLD * was received, and have waitproc bump another counter when it got * the status of a process. Waitproc would then know that a wait * system call would not block if the two counters were different. * This approach doesn't work because if a process has children that * have not been waited for, System V will send it a SIGCLD when it * installs a signal handler for SIGCLD. What this means is that when * a child exits, the shell will be sent SIGCLD signals continuously * until is runs out of stack space, unless it does a wait call before * restoring the signal handler. The code below takes advantage of * this (mis)feature by installing a signal handler for SIGCLD and * then checking to see whether it was called. If there are any * children to be waited for, it will be. * * If neither SYSV nor BSD is defined, we don't implement nonblocking * waits at all. In this case, the user will not be informed when * a background process until the next time she runs a real program * (as opposed to running a builtin command or just typing return), * and the jobs command may give out of date information. */ #ifdef SYSV STATIC int gotsigchild; STATIC int onsigchild() { gotsigchild = 1; } #endif STATIC int waitproc(block, status) int block; int *status; { #ifdef BSD int flags; #if JOBS flags = WUNTRACED; #else flags = 0; #endif if (block == 0) flags |= WNOHANG; return wait3(status, flags, (struct rusage *)NULL); #else #ifdef SYSV int (*save)(); if (block == 0) { gotsigchild = 0; save = signal(SIGCLD, onsigchild); signal(SIGCLD, save); if (gotsigchild == 0) return 0; } return wait(status); #else if (block == 0) return 0; return wait(status); #endif #endif } /* * return 1 if there are stopped jobs, otherwise 0 */ int job_warning = 0; int stoppedjobs() { register int jobno; register struct job *jp; if (job_warning) return (0); for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) { if (jp->used == 0) continue; if (jp->state == JOBSTOPPED) { out2str("You have stopped jobs.\n"); job_warning = 2; return (1); } } return (0); } /* * Return a string identifying a command (to be printed by the * jobs command. */ STATIC char *cmdnextc; STATIC int cmdnleft; STATIC void cmdtxt(), cmdputs(); #define MAXCMDTEXT 200 char * commandtext(n) union node *n; { char *name; cmdnextc = name = ckmalloc(MAXCMDTEXT); cmdnleft = MAXCMDTEXT - 4; cmdtxt(n); *cmdnextc = '\0'; return name; } STATIC void cmdtxt(n) union node *n; { union node *np; struct nodelist *lp; char *p; int i; char s[2]; if (n == NULL) return; switch (n->type) { case NSEMI: cmdtxt(n->nbinary.ch1); cmdputs("; "); cmdtxt(n->nbinary.ch2); break; case NAND: cmdtxt(n->nbinary.ch1); cmdputs(" && "); cmdtxt(n->nbinary.ch2); break; case NOR: cmdtxt(n->nbinary.ch1); cmdputs(" || "); cmdtxt(n->nbinary.ch2); break; case NPIPE: for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { cmdtxt(lp->n); if (lp->next) cmdputs(" | "); } break; case NSUBSHELL: cmdputs("("); cmdtxt(n->nredir.n); cmdputs(")"); break; case NREDIR: case NBACKGND: cmdtxt(n->nredir.n); break; case NIF: cmdputs("if "); cmdtxt(n->nif.test); cmdputs("; then "); cmdtxt(n->nif.ifpart); cmdputs("..."); break; case NWHILE: cmdputs("while "); goto until; case NUNTIL: cmdputs("until "); until: cmdtxt(n->nbinary.ch1); cmdputs("; do "); cmdtxt(n->nbinary.ch2); cmdputs("; done"); break; case NFOR: cmdputs("for "); cmdputs(n->nfor.var); cmdputs(" in ..."); break; case NCASE: cmdputs("case "); cmdputs(n->ncase.expr->narg.text); cmdputs(" in ..."); break; case NDEFUN: cmdputs(n->narg.text); cmdputs("() ..."); break; case NCMD: for (np = n->ncmd.args ; np ; np = np->narg.next) { cmdtxt(np); if (np->narg.next) cmdputs(" "); } for (np = n->ncmd.redirect ; np ; np = np->nfile.next) { cmdputs(" "); cmdtxt(np); } break; case NARG: cmdputs(n->narg.text); break; case NTO: p = ">"; i = 1; goto redir; case NAPPEND: p = ">>"; i = 1; goto redir; case NTOFD: p = ">&"; i = 1; goto redir; case NFROM: p = "<"; i = 0; goto redir; case NFROMFD: p = "<&"; i = 0; goto redir; redir: if (n->nfile.fd != i) { s[0] = n->nfile.fd + '0'; s[1] = '\0'; cmdputs(s); } cmdputs(p); if (n->type == NTOFD || n->type == NFROMFD) { s[0] = n->ndup.dupfd + '0'; s[1] = '\0'; cmdputs(s); } else { cmdtxt(n->nfile.fname); } break; case NHERE: case NXHERE: cmdputs("<<..."); break; default: cmdputs("???"); break; } } STATIC void cmdputs(s) char *s; { register char *p, *q; register char c; int subtype = 0; if (cmdnleft <= 0) return; p = s; q = cmdnextc; while ((c = *p++) != '\0') { if (c == CTLESC) *q++ = *p++; else if (c == CTLVAR) { *q++ = '$'; if (--cmdnleft > 0) *q++ = '{'; subtype = *p++; } else if (c == '=' && subtype != 0) { *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL]; subtype = 0; } else if (c == CTLENDVAR) { *q++ = '}'; } else if (c == CTLBACKQ | c == CTLBACKQ+CTLQUOTE) cmdnleft++; /* ignore it */ else *q++ = c; if (--cmdnleft <= 0) { *q++ = '.'; *q++ = '.'; *q++ = '.'; break; } } cmdnextc = q; } diff --git a/bin/sh/miscbltin.c b/bin/sh/miscbltin.c index ca37fc1721e0..97b28853dad4 100644 --- a/bin/sh/miscbltin.c +++ b/bin/sh/miscbltin.c @@ -1,389 +1,400 @@ /*- * Copyright (c) 1991, 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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. * - * $Id: miscbltin.c,v 1.4 1995/10/21 00:47:30 joerg Exp $ + * $Id: miscbltin.c,v 1.5 1996/09/01 10:20:46 peter Exp $ */ #ifndef lint static char sccsid[] = "@(#)miscbltin.c 8.4 (Berkeley) 5/4/95"; #endif /* not lint */ /* * Miscelaneous builtins. */ #include #include #include #include #include #include +#include #include "shell.h" #include "options.h" #include "var.h" #include "output.h" #include "memalloc.h" #include "error.h" #include "mystring.h" #undef eflag extern char **argptr; /* argument list for builtin command */ /* * The read builtin. The -e option causes backslashes to escape the * following character. * * This uses unbuffered input, which may be avoidable in some cases. */ int readcmd(argc, argv) int argc; char **argv; { char **ap; int backslash; char c; int eflag; char *prompt; char *ifs; char *p; int startword; int status; int i; eflag = 0; prompt = NULL; while ((i = nextopt("ep:")) != '\0') { if (i == 'p') prompt = optarg; else eflag = 1; } if (prompt && isatty(0)) { out2str(prompt); flushall(); } if (*(ap = argptr) == NULL) error("arg count"); if ((ifs = bltinlookup("IFS", 1)) == NULL) ifs = nullstr; status = 0; startword = 1; backslash = 0; STARTSTACKSTR(p); for (;;) { if (read(0, &c, 1) != 1) { status = 1; break; } if (c == '\0') continue; if (backslash) { backslash = 0; if (c != '\n') STPUTC(c, p); continue; } if (eflag && c == '\\') { backslash++; continue; } if (c == '\n') break; if (startword && *ifs == ' ' && strchr(ifs, c)) { continue; } startword = 0; if (backslash && c == '\\') { if (read(0, &c, 1) != 1) { status = 1; break; } STPUTC(c, p); } else if (ap[1] != NULL && strchr(ifs, c) != NULL) { STACKSTRNUL(p); setvar(*ap, stackblock(), 0); ap++; startword = 1; STARTSTACKSTR(p); } else { STPUTC(c, p); } } STACKSTRNUL(p); setvar(*ap, stackblock(), 0); while (*++ap != NULL) setvar(*ap, nullstr, 0); return status; } int umaskcmd(argc, argv) int argc; char **argv; { char *ap; int mask; int i; int symbolic_mode = 0; while ((i = nextopt("S")) != '\0') { symbolic_mode = 1; } INTOFF; mask = umask(0); umask(mask); INTON; if ((ap = *argptr) == NULL) { if (symbolic_mode) { char u[4], g[4], o[4]; i = 0; if ((mask & S_IRUSR) == 0) u[i++] = 'r'; if ((mask & S_IWUSR) == 0) u[i++] = 'w'; if ((mask & S_IXUSR) == 0) u[i++] = 'x'; u[i] = '\0'; i = 0; if ((mask & S_IRGRP) == 0) g[i++] = 'r'; if ((mask & S_IWGRP) == 0) g[i++] = 'w'; if ((mask & S_IXGRP) == 0) g[i++] = 'x'; g[i] = '\0'; i = 0; if ((mask & S_IROTH) == 0) o[i++] = 'r'; if ((mask & S_IWOTH) == 0) o[i++] = 'w'; if ((mask & S_IXOTH) == 0) o[i++] = 'x'; o[i] = '\0'; out1fmt("u=%s,g=%s,o=%s\n", u, g, o); } else { out1fmt("%.4o\n", mask); } } else { if (isdigit(*ap)) { mask = 0; do { if (*ap >= '8' || *ap < '0') error("Illegal number: %s", argv[1]); mask = (mask << 3) + (*ap - '0'); } while (*++ap != '\0'); umask(mask); } else { void *set; if ((set = setmode (ap)) == 0) error("Illegal number: %s", ap); mask = getmode (set, ~mask & 0777); umask(~mask & 0777); } } return 0; } /* * ulimit builtin * * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with * ash by J.T. Conklin. * * Public domain. */ struct limits { const char *name; + const char *units; int cmd; int factor; /* multiply by to get rlim_{cur,max} values */ char option; }; static const struct limits limits[] = { #ifdef RLIMIT_CPU - { "time(seconds)", RLIMIT_CPU, 1, 't' }, + { "cpu time", "seconds", RLIMIT_CPU, 1, 't' }, #endif #ifdef RLIMIT_FSIZE - { "file(512-blocks)", RLIMIT_FSIZE, 512, 'f' }, + { "file size", "512-blocks", RLIMIT_FSIZE, 512, 'f' }, #endif #ifdef RLIMIT_DATA - { "data(kbytes)", RLIMIT_DATA, 1024, 'd' }, + { "data seg size", "kbytes", RLIMIT_DATA, 1024, 'd' }, #endif #ifdef RLIMIT_STACK - { "stack(kbytes)", RLIMIT_STACK, 1024, 's' }, + { "stack size", "kbytes", RLIMIT_STACK, 1024, 's' }, #endif #ifdef RLIMIT_CORE - { "coredump(512-blocks)", RLIMIT_CORE, 512, 'c' }, + { "core file size", "512-blocks", RLIMIT_CORE, 512, 'c' }, #endif #ifdef RLIMIT_RSS - { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' }, + { "max memory size", "kbytes", RLIMIT_RSS, 1024, 'm' }, #endif #ifdef RLIMIT_MEMLOCK - { "lockedmem(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' }, + { "locked memory", "kbytes", RLIMIT_MEMLOCK, 1024, 'l' }, #endif #ifdef RLIMIT_NPROC - { "process(processes)", RLIMIT_NPROC, 1, 'u' }, + { "max user processes", (char *)0, RLIMIT_NPROC, 1, 'u' }, #endif #ifdef RLIMIT_NOFILE - { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' }, + { "open files", (char *)0, RLIMIT_NOFILE, 1, 'n' }, #endif #ifdef RLIMIT_VMEM - { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' }, + { "virtual mem size", "kbytes", RLIMIT_VMEM, 1024, 'v' }, #endif #ifdef RLIMIT_SWAP - { "swap(kbytes)", RLIMIT_SWAP, 1024, 'w' }, + { "swap limit", "kbytes", RLIMIT_SWAP, 1024, 'w' }, #endif - { (char *) 0, 0, 0, '\0' } + { (char *) 0, (char *)0, 0, 0, '\0' } }; int ulimitcmd(argc, argv) int argc; char **argv; { register int c; quad_t val; enum { SOFT = 0x1, HARD = 0x2 } how = SOFT | HARD; const struct limits *l; int set, all = 0; int optc, what; struct rlimit limit; what = 'f'; - while ((optc = nextopt("HSatfdsmcnpl")) != '\0') + while ((optc = nextopt("HSatfdsmcnul")) != '\0') switch (optc) { case 'H': how = HARD; break; case 'S': how = SOFT; break; case 'a': all = 1; break; default: what = optc; } for (l = limits; l->name && l->option != what; l++) ; if (!l->name) - error("ulimit: internal error (%c)\n", what); + error("internal error (%c)", what); set = *argptr ? 1 : 0; if (set) { char *p = *argptr; if (all || argptr[1]) - error("ulimit: too many arguments\n"); + error("too many arguments"); if (strcmp(p, "unlimited") == 0) val = RLIM_INFINITY; else { val = (quad_t) 0; while ((c = *p++) >= '0' && c <= '9') { val = (val * 10) + (long)(c - '0'); if (val < (quad_t) 0) break; } if (c) - error("ulimit: bad number\n"); + error("bad number"); val *= l->factor; } } if (all) { - for (l = limits; l->name; l++) { - getrlimit(l->cmd, &limit); + for (l = limits; l->name; l++) { + char optbuf[40]; + if (getrlimit(l->cmd, &limit) < 0) + error("can't get limit: %s", strerror(errno)); if (how & SOFT) val = limit.rlim_cur; else if (how & HARD) val = limit.rlim_max; - out1fmt("%-20s ", l->name); + if (l->units) + snprintf(optbuf, sizeof(optbuf), + "%s (%s, -%c) ", l->name, l->units, l->option); + else + snprintf(optbuf, sizeof(optbuf), + "%s (-%c) ", l->name, l->option); + out1fmt("%32s ", optbuf); if (val == RLIM_INFINITY) out1fmt("unlimited\n"); else { val /= l->factor; out1fmt("%ld\n", (long) val); } } return 0; } - getrlimit(l->cmd, &limit); + if (getrlimit(l->cmd, &limit) < 0) + error("can't get limit: %s", strerror(errno)); if (set) { if (how & SOFT) limit.rlim_cur = val; if (how & HARD) limit.rlim_max = val; if (setrlimit(l->cmd, &limit) < 0) - error("ulimit: bad limit\n"); + error("bad limit: %s", strerror(errno)); } else { if (how & SOFT) val = limit.rlim_cur; else if (how & HARD) val = limit.rlim_max; } if (!set) { if (val == RLIM_INFINITY) out1fmt("unlimited\n"); else { val /= l->factor; out1fmt("%ld\n", (long) val); } } return 0; } diff --git a/bin/sh/mknodes.c b/bin/sh/mknodes.c index 4ed88d0819c2..f2fcf364aab7 100644 --- a/bin/sh/mknodes.c +++ b/bin/sh/mknodes.c @@ -1,477 +1,477 @@ /*- * Copyright (c) 1991, 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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. * - * $Id: mknodes.c,v 1.2 1994/09/24 02:57:55 davidg Exp $ + * $Id: mknodes.c,v 1.3 1996/09/01 10:20:51 peter Exp $ */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1991, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)mknodes.c 8.2 (Berkeley) 5/4/95"; #endif /* not lint */ /* * This program reads the nodetypes file and nodes.c.pat file. It generates * the files nodes.h and nodes.c. */ #include #include #include #if __STDC__ #include #else #include #endif #define MAXTYPES 50 /* max number of node types */ #define MAXFIELDS 20 /* max fields in a structure */ #define BUFLEN 100 /* size of character buffers */ /* field types */ #define T_NODE 1 /* union node *field */ #define T_NODELIST 2 /* struct nodelist *field */ #define T_STRING 3 #define T_INT 4 /* int field */ #define T_OTHER 5 /* other */ #define T_TEMP 6 /* don't copy this field */ struct field { /* a structure field */ char *name; /* name of field */ int type; /* type of field */ char *decl; /* declaration of field */ }; struct str { /* struct representing a node structure */ char *tag; /* structure tag */ int nfields; /* number of fields in the structure */ struct field field[MAXFIELDS]; /* the fields of the structure */ int done; /* set if fully parsed */ }; static int ntypes; /* number of node types */ static char *nodename[MAXTYPES]; /* names of the nodes */ static struct str *nodestr[MAXTYPES]; /* type of structure used by the node */ static int nstr; /* number of structures */ static struct str str[MAXTYPES]; /* the structures */ static struct str *curstr; /* current structure */ static FILE *infp = stdin; static char line[1024]; static int linno; static char *linep; static void parsenode __P((void)); static void parsefield __P((void)); static void output __P((char *)); static void outsizes __P((FILE *)); static void outfunc __P((FILE *, int)); static void indent __P((int, FILE *)); static int nextfield __P((char *)); static void skipbl __P((void)); static int readline __P((void)); static void error __P((const char *, ...)); static char *savestr __P((const char *)); int main(argc, argv) int argc; char **argv; { if (argc != 3) - error("usage: mknodes file\n"); + error("usage: mknodes file"); if ((infp = fopen(argv[1], "r")) == NULL) error("Can't open %s", argv[1]); while (readline()) { if (line[0] == ' ' || line[0] == '\t') parsefield(); else if (line[0] != '\0') parsenode(); } output(argv[2]); exit(0); } static void parsenode() { char name[BUFLEN]; char tag[BUFLEN]; struct str *sp; if (curstr && curstr->nfields > 0) curstr->done = 1; nextfield(name); if (! nextfield(tag)) error("Tag expected"); if (*linep != '\0') error("Garbage at end of line"); nodename[ntypes] = savestr(name); for (sp = str ; sp < str + nstr ; sp++) { if (strcmp(sp->tag, tag) == 0) break; } if (sp >= str + nstr) { sp->tag = savestr(tag); sp->nfields = 0; curstr = sp; nstr++; } nodestr[ntypes] = sp; ntypes++; } static void parsefield() { char name[BUFLEN]; char type[BUFLEN]; char decl[2 * BUFLEN]; struct field *fp; if (curstr == NULL || curstr->done) error("No current structure to add field to"); if (! nextfield(name)) error("No field name"); if (! nextfield(type)) error("No field type"); fp = &curstr->field[curstr->nfields]; fp->name = savestr(name); if (strcmp(type, "nodeptr") == 0) { fp->type = T_NODE; sprintf(decl, "union node *%s", name); } else if (strcmp(type, "nodelist") == 0) { fp->type = T_NODELIST; sprintf(decl, "struct nodelist *%s", name); } else if (strcmp(type, "string") == 0) { fp->type = T_STRING; sprintf(decl, "char *%s", name); } else if (strcmp(type, "int") == 0) { fp->type = T_INT; sprintf(decl, "int %s", name); } else if (strcmp(type, "other") == 0) { fp->type = T_OTHER; } else if (strcmp(type, "temp") == 0) { fp->type = T_TEMP; } else { error("Unknown type %s", type); } if (fp->type == T_OTHER || fp->type == T_TEMP) { skipbl(); fp->decl = savestr(linep); } else { if (*linep) error("Garbage at end of line"); fp->decl = savestr(decl); } curstr->nfields++; } char writer[] = "\ /*\n\ * This file was generated by the mknodes program.\n\ */\n\ \n"; static void output(file) char *file; { FILE *hfile; FILE *cfile; FILE *patfile; int i; struct str *sp; struct field *fp; char *p; if ((patfile = fopen(file, "r")) == NULL) error("Can't open %s", file); if ((hfile = fopen("nodes.h", "w")) == NULL) error("Can't create nodes.h"); if ((cfile = fopen("nodes.c", "w")) == NULL) error("Can't create nodes.c"); fputs(writer, hfile); for (i = 0 ; i < ntypes ; i++) fprintf(hfile, "#define %s %d\n", nodename[i], i); fputs("\n\n\n", hfile); for (sp = str ; sp < &str[nstr] ; sp++) { fprintf(hfile, "struct %s {\n", sp->tag); for (i = sp->nfields, fp = sp->field ; --i >= 0 ; fp++) { fprintf(hfile, " %s;\n", fp->decl); } fputs("};\n\n\n", hfile); } fputs("union node {\n", hfile); fprintf(hfile, " int type;\n"); for (sp = str ; sp < &str[nstr] ; sp++) { fprintf(hfile, " struct %s %s;\n", sp->tag, sp->tag); } fputs("};\n\n\n", hfile); fputs("struct nodelist {\n", hfile); fputs("\tstruct nodelist *next;\n", hfile); fputs("\tunion node *n;\n", hfile); fputs("};\n\n\n", hfile); fputs("#ifdef __STDC__\n", hfile); fputs("union node *copyfunc(union node *);\n", hfile); fputs("void freefunc(union node *);\n", hfile); fputs("#else\n", hfile); fputs("union node *copyfunc();\n", hfile); fputs("void freefunc();\n", hfile); fputs("#endif\n", hfile); fputs(writer, cfile); while (fgets(line, sizeof line, patfile) != NULL) { for (p = line ; *p == ' ' || *p == '\t' ; p++); if (strcmp(p, "%SIZES\n") == 0) outsizes(cfile); else if (strcmp(p, "%CALCSIZE\n") == 0) outfunc(cfile, 1); else if (strcmp(p, "%COPY\n") == 0) outfunc(cfile, 0); else fputs(line, cfile); } } static void outsizes(cfile) FILE *cfile; { int i; fprintf(cfile, "static const short nodesize[%d] = {\n", ntypes); for (i = 0 ; i < ntypes ; i++) { fprintf(cfile, " ALIGN(sizeof (struct %s)),\n", nodestr[i]->tag); } fprintf(cfile, "};\n"); } static void outfunc(cfile, calcsize) FILE *cfile; int calcsize; { struct str *sp; struct field *fp; int i; fputs(" if (n == NULL)\n", cfile); if (calcsize) fputs(" return;\n", cfile); else fputs(" return NULL;\n", cfile); if (calcsize) fputs(" funcblocksize += nodesize[n->type];\n", cfile); else { fputs(" new = funcblock;\n", cfile); fputs(" funcblock += nodesize[n->type];\n", cfile); } fputs(" switch (n->type) {\n", cfile); for (sp = str ; sp < &str[nstr] ; sp++) { for (i = 0 ; i < ntypes ; i++) { if (nodestr[i] == sp) fprintf(cfile, " case %s:\n", nodename[i]); } for (i = sp->nfields ; --i >= 1 ; ) { fp = &sp->field[i]; switch (fp->type) { case T_NODE: if (calcsize) { indent(12, cfile); fprintf(cfile, "calcsize(n->%s.%s);\n", sp->tag, fp->name); } else { indent(12, cfile); fprintf(cfile, "new->%s.%s = copynode(n->%s.%s);\n", sp->tag, fp->name, sp->tag, fp->name); } break; case T_NODELIST: if (calcsize) { indent(12, cfile); fprintf(cfile, "sizenodelist(n->%s.%s);\n", sp->tag, fp->name); } else { indent(12, cfile); fprintf(cfile, "new->%s.%s = copynodelist(n->%s.%s);\n", sp->tag, fp->name, sp->tag, fp->name); } break; case T_STRING: if (calcsize) { indent(12, cfile); fprintf(cfile, "funcstringsize += strlen(n->%s.%s) + 1;\n", sp->tag, fp->name); } else { indent(12, cfile); fprintf(cfile, "new->%s.%s = nodesavestr(n->%s.%s);\n", sp->tag, fp->name, sp->tag, fp->name); } break; case T_INT: case T_OTHER: if (! calcsize) { indent(12, cfile); fprintf(cfile, "new->%s.%s = n->%s.%s;\n", sp->tag, fp->name, sp->tag, fp->name); } break; } } indent(12, cfile); fputs("break;\n", cfile); } fputs(" };\n", cfile); if (! calcsize) fputs(" new->type = n->type;\n", cfile); } static void indent(amount, fp) int amount; FILE *fp; { while (amount >= 8) { putc('\t', fp); amount -= 8; } while (--amount >= 0) { putc(' ', fp); } } static int nextfield(buf) char *buf; { register char *p, *q; p = linep; while (*p == ' ' || *p == '\t') p++; q = buf; while (*p != ' ' && *p != '\t' && *p != '\0') *q++ = *p++; *q = '\0'; linep = p; return (q > buf); } static void skipbl() { while (*linep == ' ' || *linep == '\t') linep++; } static int readline() { register char *p; if (fgets(line, 1024, infp) == NULL) return 0; for (p = line ; *p != '#' && *p != '\n' && *p != '\0' ; p++); while (p > line && (p[-1] == ' ' || p[-1] == '\t')) p--; *p = '\0'; linep = line; linno++; if (p - line > BUFLEN) error("Line too long"); return 1; } static void #if __STDC__ error(const char *msg, ...) #else error(va_alist) va_dcl #endif { va_list va; #if __STDC__ va_start(va, msg); #else char *msg; va_start(va); msg = va_arg(va, char *); #endif (void) fprintf(stderr, "line %d: ", linno); (void) vfprintf(stderr, msg, va); (void) fputc('\n', stderr); va_end(va); exit(2); } static char * savestr(s) const char *s; { register char *p; if ((p = malloc(strlen(s) + 1)) == NULL) error("Out of space"); (void) strcpy(p, s); return p; }