/*ARGSUSED*/ void dowhich(Char **v, struct command *c) { struct wordent lexw[3]; struct varent *vp; lexw[0].next = &lexw[1]; lexw[1].next = &lexw[2]; lexw[2].next = &lexw[0]; lexw[0].prev = &lexw[2]; lexw[1].prev = &lexw[0]; lexw[2].prev = &lexw[1]; lexw[0].word = STRNULL; lexw[2].word = STRret; while (*++v) { if ((vp = adrof1(*v, &aliases)) != NULL) { (void)fprintf(cshout, "%s: \t aliased to ", vis_str(*v)); blkpr(cshout, vp->vec); (void)fputc('\n', cshout); set(STRstatus, Strsave(STR0)); } else { lexw[1].word = *v; set(STRstatus, Strsave(tellmewhat(lexw, NULL) ? STR0 : STR1)); } } }
/* * dfollow - change to arg directory; fall back on cdpath if not valid */ static Char * dfollow(Char *cp) { Char *dp; struct varent *c; char ebuf[PATH_MAX]; int serrno; cp = globone(cp, G_ERROR); /* * if we are ignoring symlinks, try to fix relatives now. */ dp = dnormalize(cp); if (chdir(short2str(dp)) >= 0) { free(cp); return dgoto(dp); } else { free(dp); if (chdir(short2str(cp)) >= 0) return dgoto(cp); serrno = errno; } if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) && (c = adrof(STRcdpath))) { Char **cdp; Char *p; Char buf[PATH_MAX]; for (cdp = c->vec; *cdp; cdp++) { for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) continue; dp[-1] = '/'; for (p = cp; (*dp++ = *p++) != '\0';) continue; if (chdir(short2str(buf)) >= 0) { printd = 1; free(cp); cp = Strsave(buf); return dgoto(cp); } } } dp = value(cp); if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { free(cp); cp = Strsave(dp); printd = 1; return dgoto(cp); } (void) strlcpy(ebuf, short2str(cp), sizeof ebuf); free(cp); stderror(ERR_SYSTEM, ebuf, strerror(serrno)); return (NULL); }
Char * globone(Char *str, int action) { Char *v[2], **vl, **vo; int gflg, noglob; noglob = adrof(STRnoglob) != 0; v[0] = str; v[1] = 0; gflg = tglob(v); if (gflg == G_NONE) return (strip(Strsave(str))); if (gflg & G_CSH) { /* * Expand back-quote, tilde and brace */ vo = globexpand(v, noglob); if (noglob || (gflg & G_GLOB) == 0) { vl = vo; goto result; } cleanup_push(vo, blk_cleanup); } else if (noglob || (gflg & G_GLOB) == 0) return (strip(Strsave(str))); else vo = v; vl = libglob(vo); if (gflg & G_CSH) { if (vl != vo) cleanup_until(vo); else cleanup_ignore(vo); } if (vl == NULL) { setname(short2str(str)); stderror(ERR_NAME | ERR_NOMATCH); } result: if (vl && vl[0] == NULL) { xfree(vl); return (Strsave(STRNULL)); } if (vl && vl[1]) return (handleone(str, vl, action)); else { str = strip(*vl); xfree(vl); return (str); } }
void setq(Char *name, Char **vec, struct varent *p) { struct varent *c; int f; f = 0; /* tree hangs off the header's left link */ while ((c = p->v_link[f]) != NULL) { if ((f = *name - *c->v_name) == 0 && (f = Strcmp(name, c->v_name)) == 0) { blkfree(c->vec); goto found; } p = c; f = f > 0; } p->v_link[f] = c = (struct varent *) xmalloc((size_t) sizeof(struct varent)); c->v_name = Strsave(name); c->v_bal = 0; c->v_left = c->v_right = 0; c->v_parent = p; balance(p, f, 0); found: trim(c->vec = vec); }
static Char * globtilde(Char *s) { Char *name, *u, *home, *res; u = s; for (s++; *s && *s != '/' && *s != ':'; s++) continue; name = Strnsave(u + 1, s - (u + 1)); cleanup_push(name, xfree); home = gethdir(name); if (home == NULL) { if (adrof(STRnonomatch)) { cleanup_until(name); return u; } if (*name) stderror(ERR_UNKUSER, short2str(name)); else stderror(ERR_NOHOME); } cleanup_until(name); if (home[0] == '/' && home[1] == '\0' && s[0] == '/') res = Strsave(s); else res = Strspl(home, s); xfree(home); xfree(u); return res; }
void setq(const Char *name, Char **vec, struct varent *p, int flags) { struct varent *c; int f; f = 0; /* tree hangs off the header's left link */ while ((c = p->v_link[f]) != 0) { if ((f = *name - *c->v_name) == 0 && (f = Strcmp(name, c->v_name)) == 0) { if (c->v_flags & VAR_READONLY) stderror(ERR_READONLY|ERR_NAME, c->v_name); blkfree(c->vec); c->v_flags = flags; trim(c->vec = vec); return; } p = c; f = f > 0; } p->v_link[f] = c = xmalloc(sizeof(struct varent)); c->v_name = Strsave(name); c->v_flags = flags; c->v_bal = 0; c->v_left = c->v_right = 0; c->v_parent = p; balance(p, f, 0); trim(c->vec = vec); }
static Char * handleone(Char *str, Char **vl, int action) { size_t chars; Char **t, *p, *strp; switch (action) { case G_ERROR: setname(short2str(str)); blkfree(vl); stderror(ERR_NAME | ERR_AMBIG); break; case G_APPEND: chars = 0; for (t = vl; (p = *t++) != NULL; chars++) chars += Strlen(p); str = xmalloc(chars * sizeof(Char)); for (t = vl, strp = str; (p = *t++) != '\0'; chars++) { while (*p) *strp++ = *p++ & TRIM; *strp++ = ' '; } *--strp = '\0'; blkfree(vl); break; case G_IGNORE: str = Strsave(strip(*vl)); blkfree(vl); break; default: break; } return (str); }
void importpath(Char *cp) { Char *dp, **pv; int c, i; i = 0; for (dp = cp; *dp; dp++) if (*dp == ':') i++; /* * i+2 where i is the number of colons in the path. There are i+1 * directories in the path plus we need room for a zero terminator. */ pv = (Char **)xcalloc((size_t) (i + 2), sizeof(Char **)); dp = cp; i = 0; if (*dp) for (;;) { if ((c = *dp) == ':' || c == 0) { *dp = 0; pv[i++] = Strsave(*cp ? cp : STRdot); if (c) { cp = dp + 1; *dp = ':'; } else break; } dp++; } pv[i] = 0; setq(STRpath, pv, &shvhed); }
void dsetstack(void) { Char **cp; struct varent *vp; struct directory *dn, *dp; if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL) return; /* Free the whole stack */ while ((dn = dhead.di_prev) != &dhead) { dn->di_next->di_prev = dn->di_prev; dn->di_prev->di_next = dn->di_next; if (dn != dcwd) dfree(dn); } /* thread the current working directory */ dhead.di_prev = dhead.di_next = dcwd; dcwd->di_next = dcwd->di_prev = &dhead; /* put back the stack */ for (cp = vp->vec; cp && *cp && **cp; cp++) { dp = xcalloc(sizeof(struct directory), 1); dp->di_name = Strsave(*cp); dp->di_count = 0; dp->di_prev = dcwd; dp->di_next = dcwd->di_next; dcwd->di_next = dp; dp->di_next->di_prev = dp; } dgetstack(); /* Make $dirstack reflect the current state */ }
void setcopy(const Char *var, const Char *val, int flags) { Char *copy; copy = Strsave(val); cleanup_push(copy, xfree); setv(var, copy, flags); cleanup_ignore(copy); cleanup_until(copy); }
static Char * xset(Char *cp, Char ***vp) { Char *dp; if (*cp) { dp = Strsave(cp); --(*vp); xfree(** vp); **vp = dp; } return (putn(expr(vp))); }
Char * putn(int n) { Char nbuf[(CHAR_BIT * sizeof (n) + 2) / 3 + 2]; /* Enough even for octal */ putp = nbuf; if (n < 0) { n = -n; *putp++ = '-'; } putn1(n); *putp = 0; return (Strsave(nbuf)); }
/*ARGSUSED*/ void dochngd(Char **v, struct command *c) { Char *cp; struct directory *dp; int dflag = skipargs(&v, "plvn", "[-|<dir>]"); USE(c); printd = 0; cp = (dflag & DIR_OLD) ? varval(STRowd) : *v; if (cp == NULL) { if (!cdtohome) stderror(ERR_NAME | ERR_TOOFEW); else if ((cp = varval(STRhome)) == STRNULL || *cp == 0) stderror(ERR_NAME | ERR_NOHOMEDIR); if (chdir(short2str(cp)) < 0) stderror(ERR_NAME | ERR_CANTCHANGE); cp = Strsave(cp); } else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) { stderror(ERR_NAME | ERR_TOOMANY); /* NOTREACHED */ return; } else if ((dp = dfind(cp)) != 0) { char *tmp; printd = 1; if (chdir(tmp = short2str(dp->di_name)) < 0) stderror(ERR_SYSTEM, tmp, strerror(errno)); dcwd->di_prev->di_next = dcwd->di_next; dcwd->di_next->di_prev = dcwd->di_prev; dfree(dcwd); dnewcwd(dp, dflag); return; } else if ((cp = dfollow(cp, dflag & DIR_OLD)) == NULL) return; dp = xcalloc(sizeof(struct directory), 1); dp->di_name = cp; dp->di_count = 0; dp->di_next = dcwd->di_next; dp->di_prev = dcwd->di_prev; dp->di_prev->di_next = dp; dp->di_next->di_prev = dp; dfree(dcwd); dnewcwd(dp, dflag); }
static void dset(Char *dp) { /* * Don't call set() directly cause if the directory contains ` or * other junk characters glob will fail. */ Char **vec = xreallocarray(NULL, 2, sizeof(*vec)); vec[0] = Strsave(dp); vec[1] = 0; setq(STRcwd, vec, &shvhed); Setenv(STRPWD, dp); }
static void dset(Char *dp) { /* * Don't call set() directly cause if the directory contains ` or * other junk characters glob will fail. */ Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **))); vec[0] = Strsave(dp); vec[1] = 0; setq(STRcwd, vec, &shvhed); Setenv(STRPWD, dp); }
Char * putn(tcsh_number_t n) { Char nbuf[1024]; /* Enough even for octal */ putp = nbuf; if (n < 0) { n = -n; *putp++ = '-'; } putn1(n); *putp = 0; return (Strsave(nbuf)); }
/* * dochngd - implement chdir command. */ void /*ARGSUSED*/ dochngd(Char **v, struct command *t) { Char *cp; struct directory *dp; skipargs(&v, " [<dir>]"); printd = 0; if (*v == NULL) { if ((cp = value(STRhome)) == NULL || *cp == 0) stderror(ERR_NAME | ERR_NOHOMEDIR); if (chdir(short2str(cp)) < 0) stderror(ERR_NAME | ERR_CANTCHANGE); cp = Strsave(cp); } else if (v[1] != NULL) { stderror(ERR_NAME | ERR_TOOMANY); /* NOTREACHED */ return; } else if ((dp = dfind(*v)) != 0) { char *tmp; printd = 1; if (chdir(tmp = short2str(dp->di_name)) < 0) stderror(ERR_SYSTEM, tmp, strerror(errno)); dcwd->di_prev->di_next = dcwd->di_next; dcwd->di_next->di_prev = dcwd->di_prev; dfree(dcwd); dnewcwd(dp); return; } else cp = dfollow(*v); dp = xcalloc(1, sizeof(struct directory)); dp->di_name = cp; dp->di_count = 0; dp->di_next = dcwd->di_next; dp->di_prev = dcwd->di_prev; dp->di_prev->di_next = dp; dp->di_next->di_prev = dp; dfree(dcwd); dnewcwd(dp); }
/* * $ substitute one word, for i/o redirection */ Char * Dfix1(Char *cp) { Char *Dv[2]; if (noexec) return (0); Dv[0] = cp; Dv[1] = NULL; Dfix2(Dv); if (gargc != 1) { setname(vis_str(cp)); stderror(ERR_NAME | ERR_AMBIG); } cp = Strsave(gargv[0]); blkfree(gargv), gargv = 0; return (cp); }
/* * $ substitute one word, for i/o redirection */ Char * Dfix1(Char *cp) { Char *Dv[2], **expanded; if (noexec) return (0); Dv[0] = cp; Dv[1] = NULL; expanded = Dfix2(Dv); if (expanded[0] == NULL || expanded[1] != NULL) { blkfree(expanded); setname(short2str(cp)); stderror(ERR_NAME | ERR_AMBIG); } cp = Strsave(expanded[0]); blkfree(expanded); return (cp); }
int Gnmatch(const Char *string, const Char *pattern, const Char **endstr) { Char ***fblk, **p; const Char *tstring = string; int gpol = 1, gres = 0; if (*pattern == '^') { gpol = 0; pattern++; } fblk = xmalloc(sizeof(Char ***)); *fblk = xmalloc(GLOBSPACE * sizeof(Char *)); (*fblk)[0] = Strsave(pattern); (*fblk)[1] = NULL; cleanup_push(fblk, blk_indirect_cleanup); expbrace(fblk, NULL, GLOBSPACE); if (endstr == NULL) /* Exact matches only */ for (p = *fblk; *p; p++) gres |= t_pmatch(string, *p, &tstring, 1) == 2 ? 1 : 0; else { const Char *end; /* partial matches */ end = Strend(string); for (p = *fblk; *p; p++) if (t_pmatch(string, *p, &tstring, 1) != 0) { gres |= 1; if (end > tstring) end = tstring; } *endstr = end; } cleanup_until(fblk); return(gres == gpol); }
static void dgetstack(void) { int i = 0; Char **dblk, **dbp; struct directory *dn; if (adrof(STRdirstack) == NULL) return; for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++) continue; dbp = dblk = xmalloc((i + 1) * sizeof(Char *)); for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++) *dbp = Strsave(dn->di_name); *dbp = NULL; cleanup_push(dblk, blk_cleanup); setq(STRdirstack, dblk, &shvhed, VAR_READWRITE); cleanup_ignore(dblk); cleanup_until(dblk); }
/*ARGSUSED*/ void dolist(Char **v, struct command *c) { Char **globbed; int i, k, ret = 0; struct stat st; USE(c); if (*++v == NULL) { struct Strbuf word = Strbuf_INIT; Strbuf_terminate(&word); cleanup_push(&word, Strbuf_cleanup); (void) t_search(&word, LIST, TW_ZERO, 0, STRNULL, 0); cleanup_until(&word); return; } v = glob_all_or_error(v); globbed = v; cleanup_push(globbed, blk_cleanup); for (k = 0; v[k] != NULL && v[k][0] != '-'; k++) continue; if (v[k]) { /* * We cannot process a flag therefore we let ls do it right. */ Char *lspath; struct command *t; struct wordent cmd, *nextword, *lastword; Char *cp; struct varent *vp; if (setintr) { pintr_disabled++; cleanup_push(&pintr_disabled, disabled_cleanup); } if (seterr) { xfree(seterr); seterr = NULL; } lspath = STRls; STRmCF[1] = 'C'; STRmCF[3] = '\0'; /* Look at listflags, to add -A to the flags, to get a path of ls if necessary */ if ((vp = adrof(STRlistflags)) != NULL && vp->vec != NULL && vp->vec[0] != STRNULL) { if (vp->vec[1] != NULL && vp->vec[1][0] != '\0') lspath = vp->vec[1]; for (cp = vp->vec[0]; *cp; cp++) switch (*cp) { case 'x': STRmCF[1] = 'x'; break; case 'a': STRmCF[3] = 'a'; break; case 'A': STRmCF[3] = 'A'; break; default: break; } } cmd.word = STRNULL; lastword = &cmd; nextword = xcalloc(1, sizeof cmd); nextword->word = Strsave(lspath); lastword->next = nextword; nextword->prev = lastword; lastword = nextword; nextword = xcalloc(1, sizeof cmd); nextword->word = Strsave(STRmCF); lastword->next = nextword; nextword->prev = lastword; #if defined(KANJI) && defined(SHORT_STRINGS) && defined(DSPMBYTE) if (dspmbyte_ls) { lastword = nextword; nextword = xcalloc(1, sizeof cmd); nextword->word = Strsave(STRmmliteral); lastword->next = nextword; nextword->prev = lastword; } #endif #ifdef COLOR_LS_F if (color_context_ls) { lastword = nextword; nextword = xcalloc(1, sizeof cmd); nextword->word = Strsave(STRmmcolormauto); lastword->next = nextword; nextword->prev = lastword; } #endif /* COLOR_LS_F */ lastword = nextword; for (cp = *v; cp; cp = *++v) { nextword = xcalloc(1, sizeof cmd); nextword->word = quote(Strsave(cp)); lastword->next = nextword; nextword->prev = lastword; lastword = nextword; } lastword->next = &cmd; cmd.prev = lastword; cleanup_push(&cmd, lex_cleanup); /* build a syntax tree for the command. */ t = syntax(cmd.next, &cmd, 0); cleanup_push(t, syntax_cleanup); if (seterr) stderror(ERR_OLD); /* expand aliases like process() does */ /* alias(&cmd); */ /* execute the parse tree. */ execute(t, tpgrp > 0 ? tpgrp : -1, NULL, NULL, FALSE); /* done. free the lex list and parse tree. */ cleanup_until(&cmd); if (setintr) cleanup_until(&pintr_disabled); } else { Char *dp, *tmp; struct Strbuf buf = Strbuf_INIT; cleanup_push(&buf, Strbuf_cleanup); for (k = 0, i = 0; v[k] != NULL; k++) { tmp = dnormalize(v[k], symlinks == SYM_IGNORE); cleanup_push(tmp, xfree); dp = Strend(tmp) - 1; if (*dp == '/' && dp != tmp) #ifdef apollo if (dp != &tmp[1]) #endif /* apollo */ *dp = '\0'; if (stat(short2str(tmp), &st) == -1) { int err; err = errno; if (k != i) { if (i != 0) xputchar('\n'); print_by_column(STRNULL, &v[i], k - i, FALSE); } haderr = 1; xprintf("%S: %s.\n", tmp, strerror(err)); haderr = 0; i = k + 1; ret = 1; } else if (S_ISDIR(st.st_mode)) { Char *cp; if (k != i) { if (i != 0) xputchar('\n'); print_by_column(STRNULL, &v[i], k - i, FALSE); } if (k != 0 && v[1] != NULL) xputchar('\n'); xprintf("%S:\n", tmp); buf.len = 0; for (cp = tmp; *cp; cp++) Strbuf_append1(&buf, (*cp | QUOTE)); Strbuf_terminate(&buf); dp = &buf.s[buf.len - 1]; if ( #ifdef WINNT_NATIVE (*dp != (Char) (':' | QUOTE)) && #endif /* WINNT_NATIVE */ (*dp != (Char) ('/' | QUOTE))) { Strbuf_append1(&buf, '/'); Strbuf_terminate(&buf); } else *dp &= TRIM; (void) t_search(&buf, LIST, TW_ZERO, 0, STRNULL, 0); i = k + 1; } cleanup_until(tmp); } cleanup_until(&buf); if (k != i) { if (i != 0) xputchar('\n'); print_by_column(STRNULL, &v[i], k - i, FALSE); } if (ret) stderror(ERR_SILENT); } cleanup_until(globbed); }
/* * Execute command f, arg list t. * Record error message if not found. * Also do shell scripts here. */ static void texec(Char *sf, Char **st) { struct varent *v; Char *lastsh[2], **vp, *st0, **ost; char *f, **t; int fd; unsigned char c = '\0'; /* The order for the conversions is significant */ t = short2blk(st); f = short2str(sf); Vt = t; errno = 0; /* don't use a previous error */ (void)execve(f, t, environ); Vt = 0; blkfree((Char **)t); switch (errno) { case ENOEXEC: /* * From: [email protected] (Casper H.S. Dik) If we could not execute * it, don't feed it to the shell if it looks like a binary! */ if ((fd = open(f, O_RDONLY)) != -1) { if (read(fd, (char *)&c, 1) == 1) { if (!Isprint(c) && (c != '\n' && c != '\t')) { (void)close(fd); /* * We *know* what ENOEXEC means. */ stderror(ERR_ARCH, f, strerror(errno)); } } #ifdef _PATH_BSHELL else c = '#'; #endif (void)close(fd); } /* * If there is an alias for shell, then put the words of the alias in * front of the argument list replacing the command name. Note no * interpretation of the words at this point. */ v = adrof1(STRshell, &aliases); if (v == 0) { vp = lastsh; vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH; vp[1] = NULL; #ifdef _PATH_BSHELL if (fd != -1 && c != '#') vp[0] = STR_BSHELL; #endif } else vp = v->vec; st0 = st[0]; st[0] = sf; ost = st; st = blkspl(vp, st); /* Splice up the new arglst */ ost[0] = st0; sf = *st; /* The order for the conversions is significant */ t = short2blk(st); f = short2str(sf); free(st); Vt = t; (void)execve(f, t, environ); Vt = 0; blkfree((Char **)t); /* FALLTHROUGH */ case ENOMEM: stderror(ERR_SYSTEM, f, strerror(errno)); /* NOTREACHED */ case ENOENT: break; default: if (exerr == 0) { exerr = strerror(errno); if (expath) free(expath); expath = Strsave(sf); Vexpath = expath; } } }
void /*ARGSUSED*/ doexec(Char **v, struct command *t) { struct varent *pathv; Char *blk[2], **av, *dp, **pv, *sav; int i, hashval, hashval1; sigset_t nsigset; int slash; hashval = 0; /* * Glob the command name. We will search $path even if this does something, * as in sh but not in csh. One special case: if there is no PATH, then we * execute only commands which start with '/'. */ blk[0] = t->t_dcom[0]; blk[1] = 0; gflag = 0, tglob(blk); if (gflag) { pv = globall(blk); if (pv == 0) { setname(vis_str(blk[0])); stderror(ERR_NAME | ERR_NOMATCH); } gargv = 0; } else pv = saveblk(blk); trim(pv); exerr = 0; expath = Strsave(pv[0]); Vexpath = expath; pathv = adrof(STRpath); if (pathv == 0 && expath[0] != '/') { blkfree(pv); pexerr(); } slash = any(short2str(expath), '/'); /* * Glob the argument list, if necessary. Otherwise trim off the quote bits. */ gflag = 0; av = &t->t_dcom[1]; tglob(av); if (gflag) { av = globall(av); if (av == 0) { blkfree(pv); setname(vis_str(expath)); stderror(ERR_NAME | ERR_NOMATCH); } gargv = 0; } else av = saveblk(av); blkfree(t->t_dcom); t->t_dcom = blkspl(pv, av); free(pv); free(av); av = t->t_dcom; trim(av); if (*av == NULL || **av == '\0') pexerr(); xechoit(av); /* Echo command if -x */ /* * Since all internal file descriptors are set to close on exec, we don't * need to close them explicitly here. Just reorient ourselves for error * messages. */ SHIN = 0; SHOUT = 1; SHERR = 2; OLDSTD = 0; /* * We must do this AFTER any possible forking (like `foo` in glob) so that * this shell can still do subprocesses. */ sigemptyset(&nsigset); (void)sigprocmask(SIG_SETMASK, &nsigset, NULL); /* * If no path, no words in path, or a / in the filename then restrict the * command search. */ if (pathv == 0 || pathv->vec[0] == 0 || slash) pv = justabs; else pv = pathv->vec; sav = Strspl(STRslash, *av); /* / command name for postpending */ Vsav = sav; if (havhash) hashval = hashname(*av); i = 0; hits++; do { /* * Try to save time by looking at the hash table for where this command * could be. If we are doing delayed hashing, then we put the names in * one at a time, as the user enters them. This is kinda like Korn * Shell's "tracked aliases". */ if (!slash && pv[0][0] == '/' && havhash) { hashval1 = hash(hashval, i); if (!bit(xhash, hashval1)) goto cont; } if (pv[0][0] == 0 || eq(pv[0], STRdot)) /* don't make ./xxx */ texec(*av, av); else { dp = Strspl(*pv, sav); Vdp = dp; texec(dp, av); Vdp = 0; free(dp); } misses++; cont: pv++; i++; } while (*pv); hits--; Vsav = 0; free(sav); pexerr(); /* NOTREACHED */ }
static void update_vars(Char *vp) { if (eq(vp, STRpath)) { struct varent *p = adrof(STRpath); if (p == NULL) stderror(ERR_NAME | ERR_UNDVAR); else { exportpath(p->vec); dohash(NULL, NULL); } } else if (eq(vp, STRnoclobber)) { struct varent *p = adrof(STRnoclobber); if (p == NULL) stderror(ERR_NAME | ERR_UNDVAR); else no_clobber = set_noclobber(p->vec); } else if (eq(vp, STRhistchars)) { Char *pn = varval(vp); HIST = *pn++; if (HIST) HISTSUB = *pn; else HISTSUB = HIST; } else if (eq(vp, STRpromptchars)) { Char *pn = varval(vp); PRCH = *pn++; if (PRCH) PRCHROOT = *pn; else PRCHROOT = PRCH; } else if (eq(vp, STRhistlit)) { HistLit = 1; } else if (eq(vp, STRuser)) { tsetenv(STRKUSER, varval(vp)); tsetenv(STRLOGNAME, varval(vp)); } else if (eq(vp, STRgroup)) { tsetenv(STRKGROUP, varval(vp)); } else if (eq(vp, STRwordchars)) { word_chars = varval(vp); } else if (eq(vp, STRloginsh)) { loginsh = 1; } else if (eq(vp, STRanyerror)) { anyerror = 1; } else if (eq(vp, STRsymlinks)) { Char *pn = varval(vp); if (eq(pn, STRignore)) symlinks = SYM_IGNORE; else if (eq(pn, STRexpand)) symlinks = SYM_EXPAND; else if (eq(pn, STRchase)) symlinks = SYM_CHASE; else symlinks = 0; } else if (eq(vp, STRterm)) { Char *cp = varval(vp); tsetenv(STRKTERM, cp); #ifdef DOESNT_WORK_RIGHT cp = getenv("TERMCAP"); if (cp && (*cp != '/')) /* if TERMCAP and not a path */ Unsetenv(STRTERMCAP); #endif /* DOESNT_WORK_RIGHT */ GotTermCaps = 0; if (noediting && Strcmp(cp, STRnetwork) != 0 && Strcmp(cp, STRunknown) != 0 && Strcmp(cp, STRdumb) != 0) { editing = 1; noediting = 0; setNS(STRedit); } ed_Init(); /* reset the editor */ } else if (eq(vp, STRhome)) { Char *cp, *canon; cp = Strsave(varval(vp)); /* get the old value back */ cleanup_push(cp, xfree); /* * convert to cononical pathname (possibly resolving symlinks) */ canon = dcanon(cp, cp); cleanup_ignore(cp); cleanup_until(cp); cleanup_push(canon, xfree); setcopy(vp, canon, VAR_READWRITE); /* have to save the new val */ /* and now mirror home with HOME */ tsetenv(STRKHOME, canon); /* fix directory stack for new tilde home */ dtilde(); cleanup_until(canon); } else if (eq(vp, STRedit)) { editing = 1; noediting = 0; /* PWP: add more stuff in here later */ } else if (eq(vp, STRvimode)) { VImode = 1; update_wordchars(); } else if (eq(vp, STRshlvl)) { tsetenv(STRKSHLVL, varval(vp)); } else if (eq(vp, STRignoreeof)) { Char *cp; numeof = 0; for ((cp = varval(STRignoreeof)); cp && *cp; cp++) { if (!Isdigit(*cp)) { numeof = 0; break; } numeof = numeof * 10 + *cp - '0'; } if (numeof <= 0) numeof = 26; /* Sanity check */ } else if (eq(vp, STRbackslash_quote)) { bslash_quote = 1; } else if (eq(vp, STRcompat_expr)) { compat_expr = 1; } else if (eq(vp, STRdirstack)) { dsetstack(); } else if (eq(vp, STRrecognize_only_executables)) { tw_cmd_free(); } else if (eq(vp, STRkillring)) { SetKillRing((int)getn(varval(vp))); } else if (eq(vp, STRhistory)) { sethistory((int)getn(varval(vp))); } #ifndef HAVENOUTMP else if (eq(vp, STRwatch)) { resetwatch(); } #endif /* HAVENOUTMP */ else if (eq(vp, STRimplicitcd)) { implicit_cd = ((eq(varval(vp), STRverbose)) ? 2 : 1); } else if (eq(vp, STRcdtohome)) { cdtohome = 1; } #ifdef COLOR_LS_F else if (eq(vp, STRcolor)) { set_color_context(); } #endif /* COLOR_LS_F */ #if defined(KANJI) && defined(SHORT_STRINGS) && defined(DSPMBYTE) else if(eq(vp, CHECK_MBYTEVAR) || eq(vp, STRnokanji)) { update_dspmbyte_vars(); } #endif #ifdef NLS_CATALOGS else if (eq(vp, STRcatalog)) { nlsclose(); nlsinit(); } #if defined(FILEC) && defined(TIOCSTI) else if (eq(vp, STRfilec)) filec = 1; #endif #endif /* NLS_CATALOGS */ }
/*ARGSUSED*/ void dolet(Char **v, struct command *dummy) { Char *p; Char *vp, c, op; int hadsub; int subscr; USE(dummy); v++; p = *v++; if (p == 0) { prvars(); return; } do { hadsub = 0; vp = p; if (letter(*p)) for (; alnum(*p); p++) continue; if (vp == p || !letter(*vp)) stderror(ERR_NAME | ERR_VARBEGIN); if (*p == '[') { hadsub++; p = getinx(p, &subscr); } if (*p == 0 && *v) p = *v++; if ((op = *p) != 0) *p++ = 0; else stderror(ERR_NAME | ERR_ASSIGN); /* * if there is no expression after the '=' then print a "Syntax Error" * message - strike */ if (*p == '\0' && *v == NULL) stderror(ERR_NAME | ERR_ASSIGN); vp = Strsave(vp); cleanup_push(vp, xfree); if (op == '=') { c = '='; p = xset(p, &v); } else { c = *p++; if (any("+-", c)) { if (c != op || *p) stderror(ERR_NAME | ERR_UNKNOWNOP); p = Strsave(STR1); } else { if (any("<>", op)) { if (c != op) stderror(ERR_NAME | ERR_UNKNOWNOP); stderror(ERR_NAME | ERR_SYNTAX); } if (c != '=') stderror(ERR_NAME | ERR_UNKNOWNOP); p = xset(p, &v); } } cleanup_push(p, xfree); if (op == '=') { if (hadsub) asx(vp, subscr, p); else setv(vp, p, VAR_READWRITE); cleanup_ignore(p); } else if (hadsub) { struct varent *gv = getvx(vp, subscr); Char *val; val = operate(op, gv->vec[subscr - 1], p); cleanup_push(val, xfree); asx(vp, subscr, val); cleanup_ignore(val); cleanup_until(val); } else { Char *val; val = operate(op, varval(vp), p); cleanup_push(val, xfree); setv(vp, val, VAR_READWRITE); cleanup_ignore(val); cleanup_until(val); } update_vars(vp); cleanup_until(vp); } while ((p = *v++) != NULL); }
/*ARGSUSED*/ void doset(Char **v, struct command *c) { Char *p; Char *vp; Char **vecp; int hadsub; int subscr; int flags = VAR_READWRITE; int first_match = 0; int last_match = 0; int changed = 0; USE(c); v++; do { changed = 0; /* * Readonly addition From: Tim P. Starrin <*****@*****.**> */ if (*v && eq(*v, STRmr)) { flags = VAR_READONLY; v++; changed = 1; } if (*v && eq(*v, STRmf) && !last_match) { first_match = 1; v++; changed = 1; } if (*v && eq(*v, STRml) && !first_match) { last_match = 1; v++; changed = 1; } } while(changed); p = *v++; if (p == 0) { plist(&shvhed, flags); return; } do { hadsub = 0; vp = p; if (!letter(*p)) stderror(ERR_NAME | ERR_VARBEGIN); do { p++; } while (alnum(*p)); if (*p == '[') { hadsub++; p = getinx(p, &subscr); } if (*p != '\0' && *p != '=') stderror(ERR_NAME | ERR_VARALNUM); if (*p == '=') { *p++ = '\0'; if (*p == '\0' && *v != NULL && **v == '(') p = *v++; } else if (*v && eq(*v, STRequal)) { if (*++v != NULL) p = *v++; } if (eq(p, STRLparen)) { Char **e = v; if (hadsub) stderror(ERR_NAME | ERR_SYNTAX); for (;;) { if (!*e) stderror(ERR_NAME | ERR_MISSING, ')'); if (**e == ')') break; e++; } p = *e; *e = 0; vecp = saveblk(v); if (first_match) flags |= VAR_FIRST; else if (last_match) flags |= VAR_LAST; set1(vp, vecp, &shvhed, flags); *e = p; v = e + 1; } else if (hadsub) { Char *copy; copy = Strsave(p); cleanup_push(copy, xfree); asx(vp, subscr, copy); cleanup_ignore(copy); cleanup_until(copy); } else setv(vp, Strsave(p), flags); update_vars(vp); } while ((p = *v++) != NULL); }
/* * Print the error with the given id. * * Special ids: * ERR_SILENT: Print nothing. * ERR_OLD: Print the previously set error if one was there. * otherwise return. * ERR_NAME: If this bit is set, print the name of the function * in bname * * This routine always resets or exits. The flag haderr * is set so the routine who catches the unwind can propogate * it if they want. * * Note that any open files at the point of error will eventually * be closed in the routine process in sh.c which is the only * place error unwinds are ever caught. */ void stderror(int id, ...) { va_list va; Char **v; int flags; flags = id & ERR_FLAGS; id &= ~ERR_FLAGS; if ((flags & ERR_OLD) && seterr == NULL) abort(); if (id < 0 || id > (int)(sizeof(errorlist) / sizeof(errorlist[0]))) id = ERR_INVALID; (void)fflush(cshout); (void)fflush(csherr); haderr = 1; /* Now to diagnostic output */ timflg = 0; /* This isn't otherwise reset */ if (!(flags & ERR_SILENT)) { if (flags & ERR_NAME) (void)fprintf(csherr, "%s: ", bname); if ((flags & ERR_OLD)) /* Old error. */ (void)fprintf(csherr, "%s.\n", seterr); else { va_start(va, id); (void)vfprintf(csherr, errorlist[id], va); va_end(va); (void)fprintf(csherr, ".\n"); } } if (seterr) { xfree((ptr_t) seterr); seterr = NULL; } if ((v = pargv) != NULL) pargv = 0, blkfree(v); if ((v = gargv) != NULL) gargv = 0, blkfree(v); (void)fflush(cshout); (void)fflush(csherr); didfds = 0; /* Forget about 0,1,2 */ /* * Go away if -e or we are a child shell */ if (exiterr || child) xexit(1); /* * Reset the state of the input. This buffered seek to end of file will * also clear the while/foreach stack. */ btoeof(); set(STRstatus, Strsave(STR1)); if (tpgrp > 0) (void)tcsetpgrp(FSHTTY, tpgrp); reset(); /* Unwind */ }
/* * Karl Kleinpaste, 21oct1983. * Set up a one-word alias command, for use for special things. * This code is based on the mainline of process(). */ void aliasrun(int cnt, Char *s1, Char *s2) { struct wordent w, *new1, *new2; /* for holding alias name */ struct command *t = NULL; jmp_buf_t osetexit; int status; size_t omark; getexit(osetexit); if (seterr) { xfree(seterr); seterr = NULL; /* don't repeatedly print err msg. */ } w.word = STRNULL; new1 = xcalloc(1, sizeof w); new1->word = Strsave(s1); if (cnt == 1) { /* build a lex list with one word. */ w.next = w.prev = new1; new1->next = new1->prev = &w; } else { /* build a lex list with two words. */ new2 = xcalloc(1, sizeof w); new2->word = Strsave(s2); w.next = new2->prev = new1; new1->next = w.prev = new2; new1->prev = new2->next = &w; } cleanup_push(&w, lex_cleanup); /* Save the old status */ status = getn(varval(STRstatus)); /* expand aliases like process() does. */ alias(&w); /* build a syntax tree for the command. */ t = syntax(w.next, &w, 0); cleanup_push(t, syntax_cleanup); if (seterr) stderror(ERR_OLD); psavejob(); cleanup_push(&cnt, psavejob_cleanup); /* cnt is used only as a marker */ /* catch any errors here */ omark = cleanup_push_mark(); if (setexit() == 0) /* execute the parse tree. */ /* * From: Michael Schroeder <*****@*****.**> * was execute(t, tpgrp); */ execute(t, tpgrp > 0 ? tpgrp : -1, NULL, NULL, TRUE); /* reset the error catcher to the old place */ cleanup_pop_mark(omark); resexit(osetexit); if (haderr) { haderr = 0; /* * Either precmd, or cwdcmd, or periodic had an error. Call it again so * that it is removed */ if (precmd_active) precmd(); if (postcmd_active) postcmd(); #ifdef notdef /* * XXX: On the other hand, just interrupting them causes an error too. * So if we hit ^C in the middle of cwdcmd or periodic the alias gets * removed. We don't want that. Note that we want to remove precmd * though, cause that could lead into an infinite loop. This should be * fixed correctly, but then haderr should give us the whole exit * status not just true or false. */ else if (cwdcmd_active) cwd_cmd(); else if (beepcmd_active) beep_cmd(); else if (periodic_active) period_cmd(); #endif /* notdef */ } cleanup_until(&w); pendjob(); /* Restore status */ setv(STRstatus, putn((tcsh_number_t)status), VAR_READWRITE); }
void execute(struct command *t, int wtty, int *pipein, int *pipeout) { static sigset_t csigset, ocsigset; static int nosigchld = 0, onosigchld = 0; volatile int wanttty = wtty; struct biltins * volatile bifunc; int pv[2], pid; sigset_t nsigset; int forked; UNREGISTER(forked); UNREGISTER(bifunc); UNREGISTER(wanttty); forked = 0; pid = 0; if (t == 0) return; if (t->t_dflg & F_AMPERSAND) wanttty = 0; switch (t->t_dtyp) { case NODE_COMMAND: if ((t->t_dcom[0][0] & (QUOTE | TRIM)) == QUOTE) (void)Strcpy(t->t_dcom[0], t->t_dcom[0] + 1); if ((t->t_dflg & F_REPEAT) == 0) Dfix(t); /* $ " ' \ */ if (t->t_dcom[0] == 0) return; /* FALLTHROUGH */ case NODE_PAREN: if (t->t_dflg & F_PIPEOUT) mypipe(pipeout); /* * Must do << early so parent will know where input pointer should be. * If noexec then this is all we do. */ if (t->t_dflg & F_READ) { (void)close(0); heredoc(t->t_dlef); if (noexec) (void)close(0); } set(STRstatus, Strsave(STR0)); /* * This mess is the necessary kludge to handle the prefix builtins: * nice, nohup, time. These commands can also be used by themselves, * and this is not handled here. This will also work when loops are * parsed. */ while (t->t_dtyp == NODE_COMMAND) if (eq(t->t_dcom[0], STRnice)) { if (t->t_dcom[1]) { if (strchr("+-", t->t_dcom[1][0])) { if (t->t_dcom[2]) { setname("nice"); t->t_nice = getn(t->t_dcom[1]); lshift(t->t_dcom, 2); t->t_dflg |= F_NICE; } else break; } else { t->t_nice = 4; lshift(t->t_dcom, 1); t->t_dflg |= F_NICE; } } else break; } else if (eq(t->t_dcom[0], STRnohup)) { if (t->t_dcom[1]) { t->t_dflg |= F_NOHUP; lshift(t->t_dcom, 1); } else break; } else if (eq(t->t_dcom[0], STRtime)) { if (t->t_dcom[1]) { t->t_dflg |= F_TIME; lshift(t->t_dcom, 1); } else break; } else break; /* is it a command */ if (t->t_dtyp == NODE_COMMAND) { /* * Check if we have a builtin function and remember which one. */ bifunc = isbfunc(t); if (noexec && bifunc != NULL) { /* * Continue for builtins that are part of the scripting language */ if (bifunc->bfunct != dobreak && bifunc->bfunct != docontin && bifunc->bfunct != doelse && bifunc->bfunct != doend && bifunc->bfunct != doforeach && bifunc->bfunct != dogoto && bifunc->bfunct != doif && bifunc->bfunct != dorepeat && bifunc->bfunct != doswbrk && bifunc->bfunct != doswitch && bifunc->bfunct != dowhile && bifunc->bfunct != dozip) break; } } else { /* not a command */ bifunc = NULL; if (noexec) break; } /* * We fork only if we are timed, or are not the end of a parenthesized * list and not a simple builtin function. Simple meaning one that is * not pipedout, niced, nohupped, or &'d. It would be nice(?) to not * fork in some of these cases. */ /* * Prevent forking cd, pushd, popd, chdir cause this will cause the * shell not to change dir! */ if (bifunc && (bifunc->bfunct == dochngd || bifunc->bfunct == dopushd || bifunc->bfunct == dopopd)) t->t_dflg &= ~(F_NICE); if (((t->t_dflg & F_TIME) || ((t->t_dflg & F_NOFORK) == 0 && (!bifunc || t->t_dflg & (F_PIPEOUT | F_AMPERSAND | F_NICE | F_NOHUP)))) || /* * We have to fork for eval too. */ (bifunc && (t->t_dflg & (F_PIPEIN | F_PIPEOUT)) != 0 && bifunc->bfunct == doeval)) { if (t->t_dtyp == NODE_PAREN || t->t_dflg & (F_REPEAT | F_AMPERSAND) || bifunc) { forked++; /* * We need to block SIGCHLD here, so that if the process does * not die before we can set the process group */ if (wanttty >= 0 && !nosigchld) { sigemptyset(&nsigset); (void)sigaddset(&nsigset, SIGCHLD); (void)sigprocmask(SIG_BLOCK, &nsigset, &csigset); nosigchld = 1; } pid = pfork(t, wanttty); if (pid == 0 && nosigchld) { (void)sigprocmask(SIG_SETMASK, &csigset, NULL); nosigchld = 0; } else if (pid != 0 && (t->t_dflg & F_AMPERSAND)) backpid = pid; } else { int ochild, osetintr, ohaderr, odidfds; int oSHIN, oSHOUT, oSHERR, oOLDSTD, otpgrp; sigset_t osigset; /* * Prepare for the vfork by saving everything that the child * corrupts before it exec's. Note that in some signal * implementations which keep the signal info in user space * (e.g. Sun's) it will also be necessary to save and restore * the current sigaction's for the signals the child touches * before it exec's. */ if (wanttty >= 0 && !nosigchld && !noexec) { sigemptyset(&nsigset); (void)sigaddset(&nsigset, SIGCHLD); (void)sigprocmask(SIG_BLOCK, &nsigset, &csigset); nosigchld = 1; } sigemptyset(&nsigset); (void)sigaddset(&nsigset, SIGCHLD); (void)sigaddset(&nsigset, SIGINT); (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset); ochild = child; osetintr = setintr; ohaderr = haderr; odidfds = didfds; oSHIN = SHIN; oSHOUT = SHOUT; oSHERR = SHERR; oOLDSTD = OLDSTD; otpgrp = tpgrp; ocsigset = csigset; onosigchld = nosigchld; Vsav = Vdp = 0; Vexpath = 0; Vt = 0; pid = vfork(); if (pid < 0) { (void)sigprocmask(SIG_SETMASK, &osigset, NULL); stderror(ERR_NOPROC); } forked++; if (pid) { /* parent */ child = ochild; setintr = osetintr; haderr = ohaderr; didfds = odidfds; SHIN = oSHIN; SHOUT = oSHOUT; SHERR = oSHERR; OLDSTD = oOLDSTD; tpgrp = otpgrp; csigset = ocsigset; nosigchld = onosigchld; xfree((ptr_t) Vsav); Vsav = 0; xfree((ptr_t) Vdp); Vdp = 0; xfree((ptr_t) Vexpath); Vexpath = 0; blkfree((Char **) Vt); Vt = 0; /* this is from pfork() */ palloc(pid, t); (void)sigprocmask(SIG_SETMASK, &osigset, NULL); } else { /* child */ /* this is from pfork() */ int pgrp; int ignint = 0; if (nosigchld) { (void)sigprocmask(SIG_SETMASK, &csigset, NULL); nosigchld = 0; } if (setintr) ignint = (tpgrp == -1 && (t->t_dflg & F_NOINTERRUPT)) || (gointr && eq(gointr, STRminus)); pgrp = pcurrjob ? pcurrjob->p_jobid : getpid(); child++; if (setintr) { setintr = 0; if (ignint) { (void)signal(SIGINT, SIG_IGN); (void)signal(SIGQUIT, SIG_IGN); } else { (void)signal(SIGINT, vffree); (void)signal(SIGQUIT, SIG_DFL); } if (wanttty >= 0) { (void)signal(SIGTSTP, SIG_DFL); (void)signal(SIGTTIN, SIG_DFL); (void)signal(SIGTTOU, SIG_DFL); } (void)signal(SIGTERM, parterm); } else if (tpgrp == -1 && (t->t_dflg & F_NOINTERRUPT)) { (void)signal(SIGINT, SIG_IGN); (void)signal(SIGQUIT, SIG_IGN); } pgetty(wanttty, pgrp); if (t->t_dflg & F_NOHUP) (void)signal(SIGHUP, SIG_IGN); if (t->t_dflg & F_NICE) (void)setpriority(PRIO_PROCESS, 0, t->t_nice); } } } if (pid != 0) { /* * It would be better if we could wait for the whole job when we * knew the last process had been started. Pwait, in fact, does * wait for the whole job anyway, but this test doesn't really * express our intentions. */ if (didfds == 0 && t->t_dflg & F_PIPEIN) { (void)close(pipein[0]); (void)close(pipein[1]); } if ((t->t_dflg & F_PIPEOUT) == 0) { if (nosigchld) { (void)sigprocmask(SIG_SETMASK, &csigset, NULL); nosigchld = 0; } if ((t->t_dflg & F_AMPERSAND) == 0) pwait(); } break; } doio(t, pipein, pipeout); if (t->t_dflg & F_PIPEOUT) { (void)close(pipeout[0]); (void)close(pipeout[1]); } /* * Perform a builtin function. If we are not forked, arrange for * possible stopping */ if (bifunc) { func(t, bifunc); if (forked) exitstat(); break; } if (t->t_dtyp != NODE_PAREN) doexec(NULL, t); /* * For () commands must put new 0,1,2 in FSH* and recurse */ (void) ioctl(OLDSTD = dcopy(0, FOLDSTD), FIOCLEX, NULL); (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, NULL); (void) ioctl(SHERR = dcopy(2, FSHERR), FIOCLEX, NULL); (void) close(SHIN); SHIN = -1; didfds = 0; wanttty = -1; t->t_dspr->t_dflg |= t->t_dflg & F_NOINTERRUPT; execute(t->t_dspr, wanttty, NULL, NULL); exitstat(); /* NOTREACHED */ case NODE_PIPE: t->t_dcar->t_dflg |= F_PIPEOUT | (t->t_dflg & (F_PIPEIN | F_AMPERSAND | F_STDERR | F_NOINTERRUPT)); execute(t->t_dcar, wanttty, pipein, pv); t->t_dcdr->t_dflg |= F_PIPEIN | (t->t_dflg & (F_PIPEOUT | F_AMPERSAND | F_NOFORK | F_NOINTERRUPT)); if (wanttty > 0) wanttty = 0; /* got tty already */ execute(t->t_dcdr, wanttty, pv, pipeout); break; case NODE_LIST: if (t->t_dcar) { t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT; execute(t->t_dcar, wanttty, NULL, NULL); /* * In strange case of A&B make a new job after A */ if (t->t_dcar->t_dflg & F_AMPERSAND && t->t_dcdr && (t->t_dcdr->t_dflg & F_AMPERSAND) == 0) pendjob(); } if (t->t_dcdr) { t->t_dcdr->t_dflg |= t->t_dflg & (F_NOFORK | F_NOINTERRUPT); execute(t->t_dcdr, wanttty, NULL, NULL); } break; case NODE_OR: case NODE_AND: if (t->t_dcar) { t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT; execute(t->t_dcar, wanttty, NULL, NULL); if ((getn(value(STRstatus)) == 0) != (t->t_dtyp == NODE_AND)) return; } if (t->t_dcdr) { t->t_dcdr->t_dflg |= t->t_dflg & (F_NOFORK | F_NOINTERRUPT); execute(t->t_dcdr, wanttty, NULL, NULL); } break; } /* * Fall through for all breaks from switch * * If there will be no more executions of this command, flush all file * descriptors. Places that turn on the F_REPEAT bit are responsible for * doing donefds after the last re-execution */ if (didfds && !(t->t_dflg & F_REPEAT)) donefds(); }