diff options
Diffstat (limited to 'bin/sh/redir.c')
-rw-r--r-- | bin/sh/redir.c | 982 |
1 files changed, 982 insertions, 0 deletions
diff --git a/bin/sh/redir.c b/bin/sh/redir.c new file mode 100644 index 0000000..751cce7 --- /dev/null +++ b/bin/sh/redir.c @@ -0,0 +1,982 @@ +/* $NetBSD: redir.c,v 1.62 2018/11/26 20:03:39 kamil Exp $ */ + +/*- + * 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)redir.c 8.2 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: redir.c,v 1.62 2018/11/26 20:03:39 kamil Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/param.h> /* PIPE_BUF */ +#include <sys/stat.h> +#include <signal.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + +/* + * Code for dealing with input/output redirection. + */ + +#include "main.h" +#include "builtins.h" +#include "shell.h" +#include "nodes.h" +#include "jobs.h" +#include "options.h" +#include "expand.h" +#include "redir.h" +#include "output.h" +#include "memalloc.h" +#include "mystring.h" +#include "error.h" +#include "show.h" + + +#define EMPTY -2 /* marks an unused slot in redirtab */ +#define CLOSED -1 /* fd was not open before redir */ +#ifndef PIPE_BUF +# define PIPESIZE 4096 /* amount of buffering in a pipe */ +#else +# define PIPESIZE PIPE_BUF +#endif + + +MKINIT +struct renamelist { + struct renamelist *next; + int orig; + int into; +}; + +MKINIT +struct redirtab { + struct redirtab *next; + struct renamelist *renamed; +}; + + +MKINIT struct redirtab *redirlist; + +/* + * We keep track of whether or not fd0 has been redirected. This is for + * background commands, where we want to redirect fd0 to /dev/null only + * if it hasn't already been redirected. + */ +STATIC int fd0_redirected = 0; + +/* + * And also where to put internal use fds that should be out of the + * way of user defined fds (normally) + */ +STATIC int big_sh_fd = 0; + +STATIC const struct renamelist *is_renamed(const struct renamelist *, int); +STATIC void fd_rename(struct redirtab *, int, int); +STATIC void free_rl(struct redirtab *, int); +STATIC void openredirect(union node *, char[10], int); +STATIC int openhere(const union node *); +STATIC int copyfd(int, int, int); +STATIC void find_big_fd(void); + + +struct shell_fds { /* keep track of internal shell fds */ + struct shell_fds *nxt; + void (*cb)(int, int); + int fd; +}; + +STATIC struct shell_fds *sh_fd_list; + +STATIC void renumber_sh_fd(struct shell_fds *); +STATIC struct shell_fds *sh_fd(int); + +STATIC const struct renamelist * +is_renamed(const struct renamelist *rl, int fd) +{ + while (rl != NULL) { + if (rl->orig == fd) + return rl; + rl = rl->next; + } + return NULL; +} + +STATIC void +free_rl(struct redirtab *rt, int reset) +{ + struct renamelist *rl, *rn = rt->renamed; + + while ((rl = rn) != NULL) { + rn = rl->next; + if (rl->orig == 0) + fd0_redirected--; + VTRACE(DBG_REDIR, ("popredir %d%s: %s", + rl->orig, rl->orig==0 ? " (STDIN)" : "", + reset ? "" : "no reset\n")); + if (reset) { + if (rl->into < 0) { + VTRACE(DBG_REDIR, ("closed\n")); + close(rl->orig); + } else { + VTRACE(DBG_REDIR, ("from %d\n", rl->into)); + movefd(rl->into, rl->orig); + } + } + ckfree(rl); + } + rt->renamed = NULL; +} + +STATIC void +fd_rename(struct redirtab *rt, int from, int to) +{ + /* XXX someday keep a short list (8..10) of freed renamelists XXX */ + struct renamelist *rl = ckmalloc(sizeof(struct renamelist)); + + rl->next = rt->renamed; + rt->renamed = rl; + + rl->orig = from; + rl->into = to; +} + +/* + * Process a list of redirection commands. If the REDIR_PUSH flag is set, + * old file descriptors are stashed away so that the redirection can be + * undone by calling popredir. If the REDIR_BACKQ flag is set, then the + * standard output, and the standard error if it becomes a duplicate of + * stdout, is saved in memory. + */ + +void +redirect(union node *redir, int flags) +{ + union node *n; + struct redirtab *sv = NULL; + int i; + int fd; + char memory[10]; /* file descriptors to write to memory */ + + CTRACE(DBG_REDIR, ("redirect(F=0x%x):%s\n", flags, redir?"":" NONE")); + for (i = 10 ; --i >= 0 ; ) + memory[i] = 0; + memory[1] = flags & REDIR_BACKQ; + if (flags & REDIR_PUSH) { + /* + * We don't have to worry about REDIR_VFORK here, as + * flags & REDIR_PUSH is never true if REDIR_VFORK is set. + */ + sv = ckmalloc(sizeof (struct redirtab)); + sv->renamed = NULL; + sv->next = redirlist; + redirlist = sv; + } + for (n = redir ; n ; n = n->nfile.next) { + fd = n->nfile.fd; + VTRACE(DBG_REDIR, ("redir %d (max=%d) ", fd, max_user_fd)); + if (fd > max_user_fd) + max_user_fd = fd; + renumber_sh_fd(sh_fd(fd)); + if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && + n->ndup.dupfd == fd) { + /* redirect from/to same file descriptor */ + /* make sure it stays open */ + if (fcntl(fd, F_SETFD, 0) < 0) + error("fd %d: %s", fd, strerror(errno)); + VTRACE(DBG_REDIR, ("!cloexec\n")); + continue; + } + + if ((flags & REDIR_PUSH) && !is_renamed(sv->renamed, fd)) { + INTOFF; + if (big_sh_fd < 10) + find_big_fd(); + if ((i = fcntl(fd, F_DUPFD, big_sh_fd)) == -1) { + switch (errno) { + case EBADF: + i = CLOSED; + break; + case EMFILE: + case EINVAL: + find_big_fd(); + i = fcntl(fd, F_DUPFD, big_sh_fd); + if (i >= 0) + break; + /* FALLTHRU */ + default: + i = errno; + INTON; /* XXX not needed here ? */ + error("%d: %s", fd, strerror(i)); + /* NOTREACHED */ + } + } + if (i >= 0) + (void)fcntl(i, F_SETFD, FD_CLOEXEC); + fd_rename(sv, fd, i); + VTRACE(DBG_REDIR, ("saved as %d ", i)); + INTON; + } + VTRACE(DBG_REDIR, ("%s\n", fd == 0 ? "STDIN" : "")); + if (fd == 0) + fd0_redirected++; + openredirect(n, memory, flags); + } + if (memory[1]) + out1 = &memout; + if (memory[2]) + out2 = &memout; +} + + +STATIC void +openredirect(union node *redir, char memory[10], int flags) +{ + struct stat sb; + int fd = redir->nfile.fd; + char *fname; + int f; + int eflags, cloexec; + + /* + * We suppress interrupts so that we won't leave open file + * descriptors around. This may not be such a good idea because + * an open of a device or a fifo can block indefinitely. + */ + INTOFF; + if (fd < 10) + memory[fd] = 0; + switch (redir->nfile.type) { + case NFROM: + fname = redir->nfile.expfname; + if (flags & REDIR_VFORK) + eflags = O_NONBLOCK; + else + eflags = 0; + if ((f = open(fname, O_RDONLY|eflags)) < 0) + goto eopen; + VTRACE(DBG_REDIR, ("openredirect(< '%s') -> %d [%#x]", + fname, f, eflags)); + if (eflags) + (void)fcntl(f, F_SETFL, fcntl(f, F_GETFL, 0) & ~eflags); + break; + case NFROMTO: + fname = redir->nfile.expfname; + if ((f = open(fname, O_RDWR|O_CREAT, 0666)) < 0) + goto ecreate; + VTRACE(DBG_REDIR, ("openredirect(<> '%s') -> %d", fname, f)); + break; + case NTO: + if (Cflag) { + fname = redir->nfile.expfname; + if ((f = open(fname, O_WRONLY)) == -1) { + if ((f = open(fname, O_WRONLY|O_CREAT|O_EXCL, + 0666)) < 0) + goto ecreate; + } else if (fstat(f, &sb) == -1) { + int serrno = errno; + close(f); + errno = serrno; + goto ecreate; + } else if (S_ISREG(sb.st_mode)) { + close(f); + errno = EEXIST; + goto ecreate; + } + VTRACE(DBG_REDIR, ("openredirect(>| '%s') -> %d", + fname, f)); + break; + } + /* FALLTHROUGH */ + case NCLOBBER: + fname = redir->nfile.expfname; + if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) + goto ecreate; + VTRACE(DBG_REDIR, ("openredirect(> '%s') -> %d", fname, f)); + break; + case NAPPEND: + fname = redir->nfile.expfname; + if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) + goto ecreate; + VTRACE(DBG_REDIR, ("openredirect(>> '%s') -> %d", fname, f)); + break; + case NTOFD: + case NFROMFD: + if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ + if (fd < 10 && redir->ndup.dupfd < 10 && + memory[redir->ndup.dupfd]) + memory[fd] = 1; + else if (copyfd(redir->ndup.dupfd, fd, + (flags & REDIR_KEEP) == 0) < 0) + error("Redirect (from %d to %d) failed: %s", + redir->ndup.dupfd, fd, strerror(errno)); + VTRACE(DBG_REDIR, ("openredirect: %d%c&%d\n", fd, + "<>"[redir->nfile.type==NTOFD], redir->ndup.dupfd)); + } else { + (void) close(fd); + VTRACE(DBG_REDIR, ("openredirect: %d%c&-\n", fd, + "<>"[redir->nfile.type==NTOFD])); + } + INTON; + return; + case NHERE: + case NXHERE: + VTRACE(DBG_REDIR, ("openredirect: %d<<...", fd)); + f = openhere(redir); + break; + default: + abort(); + } + + cloexec = fd > 2 && (flags & REDIR_KEEP) == 0 && !posix; + if (f != fd) { + VTRACE(DBG_REDIR, (" -> %d", fd)); + if (copyfd(f, fd, cloexec) < 0) { + int e = errno; + + close(f); + error("redirect reassignment (fd %d) failed: %s", fd, + strerror(e)); + } + close(f); + } else if (cloexec) + (void)fcntl(f, F_SETFD, FD_CLOEXEC); + VTRACE(DBG_REDIR, ("%s\n", cloexec ? " cloexec" : "")); + + INTON; + return; + ecreate: + exerrno = 1; + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); + eopen: + exerrno = 1; + error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); +} + + +/* + * Handle here documents. Normally we fork off a process to write the + * data to a pipe. If the document is short, we can stuff the data in + * the pipe without forking. + */ + +STATIC int +openhere(const union node *redir) +{ + int pip[2]; + int len = 0; + + if (pipe(pip) < 0) + error("Pipe call failed"); + if (redir->type == NHERE) { + len = strlen(redir->nhere.doc->narg.text); + if (len <= PIPESIZE) { + xwrite(pip[1], redir->nhere.doc->narg.text, len); + goto out; + } + } + VTRACE(DBG_REDIR, (" forking [%d,%d]\n", pip[0], pip[1])); + if (forkshell(NULL, NULL, FORK_NOJOB) == 0) { + close(pip[0]); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGHUP, SIG_IGN); +#ifdef SIGTSTP + signal(SIGTSTP, SIG_IGN); +#endif + signal(SIGPIPE, SIG_DFL); + if (redir->type == NHERE) + xwrite(pip[1], redir->nhere.doc->narg.text, len); + else + expandhere(redir->nhere.doc, pip[1]); + _exit(0); + } + VTRACE(DBG_REDIR, ("openhere (closing %d)", pip[1])); + out: + close(pip[1]); + VTRACE(DBG_REDIR, (" (pipe fd=%d)", pip[0])); + return pip[0]; +} + + + +/* + * Undo the effects of the last redirection. + */ + +void +popredir(void) +{ + struct redirtab *rp = redirlist; + + INTOFF; + free_rl(rp, 1); + redirlist = rp->next; + ckfree(rp); + INTON; +} + +/* + * Undo all redirections. Called on error or interrupt. + */ + +#ifdef mkinit + +INCLUDE "redir.h" + +RESET { + while (redirlist) + popredir(); +} + +SHELLPROC { + clearredir(0); +} + +#endif + +/* Return true if fd 0 has already been redirected at least once. */ +int +fd0_redirected_p(void) +{ + return fd0_redirected != 0; +} + +/* + * Discard all saved file descriptors. + */ + +void +clearredir(int vforked) +{ + struct redirtab *rp; + struct renamelist *rl; + + for (rp = redirlist ; rp ; rp = rp->next) { + if (!vforked) + free_rl(rp, 0); + else for (rl = rp->renamed; rl; rl = rl->next) + if (rl->into >= 0) + close(rl->into); + } +} + + + +/* + * Copy a file descriptor to be == to. + * cloexec indicates if we want close-on-exec or not. + * Returns -1 if any error occurs. + */ + +STATIC int +copyfd(int from, int to, int cloexec) +{ + int newfd; + + if (cloexec && to > 2) + newfd = dup3(from, to, O_CLOEXEC); + else + newfd = dup2(from, to); + + return newfd; +} + +/* + * rename fd from to be fd to (closing from). + * close-on-exec is never set on 'to' (unless + * from==to and it was set on from) - ie: a no-op + * returns to (or errors() if an error occurs). + * + * This is mostly used for rearranging the + * results from pipe(). + */ +int +movefd(int from, int to) +{ + if (from == to) + return to; + + (void) close(to); + if (copyfd(from, to, 0) != to) { + int e = errno; + + (void) close(from); + error("Unable to make fd %d: %s", to, strerror(e)); + } + (void) close(from); + + return to; +} + +STATIC void +find_big_fd(void) +{ + int i, fd; + static int last_start = 3; /* aim to keep sh fd's under 20 */ + + if (last_start < 10) + last_start++; + + for (i = (1 << last_start); i >= 10; i >>= 1) { + if ((fd = fcntl(0, F_DUPFD, i - 1)) >= 0) { + close(fd); + break; + } + } + + fd = (i / 5) * 4; + if (fd < 10) + fd = 10; + + big_sh_fd = fd; +} + +/* + * If possible, move file descriptor fd out of the way + * of expected user fd values. Returns the new fd + * (which may be the input fd if things do not go well.) + * Always set close-on-exec on the result, and close + * the input fd unless it is to be our result. + */ +int +to_upper_fd(int fd) +{ + int i; + + VTRACE(DBG_REDIR|DBG_OUTPUT, ("to_upper_fd(%d)", fd)); + if (big_sh_fd < 10) + find_big_fd(); + do { + i = fcntl(fd, F_DUPFD_CLOEXEC, big_sh_fd); + if (i >= 0) { + if (fd != i) + close(fd); + VTRACE(DBG_REDIR|DBG_OUTPUT, ("-> %d\n", i)); + return i; + } + if (errno != EMFILE && errno != EINVAL) + break; + find_big_fd(); + } while (big_sh_fd > 10); + + /* + * If we wanted to move this fd to some random high number + * we certainly do not intend to pass it through exec, even + * if the reassignment failed. + */ + (void)fcntl(fd, F_SETFD, FD_CLOEXEC); + VTRACE(DBG_REDIR|DBG_OUTPUT, (" fails ->%d\n", fd)); + return fd; +} + +void +register_sh_fd(int fd, void (*cb)(int, int)) +{ + struct shell_fds *fp; + + fp = ckmalloc(sizeof (struct shell_fds)); + if (fp != NULL) { + fp->nxt = sh_fd_list; + sh_fd_list = fp; + + fp->fd = fd; + fp->cb = cb; + } +} + +void +sh_close(int fd) +{ + struct shell_fds **fpp, *fp; + + fpp = &sh_fd_list; + while ((fp = *fpp) != NULL) { + if (fp->fd == fd) { + *fpp = fp->nxt; + ckfree(fp); + break; + } + fpp = &fp->nxt; + } + (void)close(fd); +} + +STATIC struct shell_fds * +sh_fd(int fd) +{ + struct shell_fds *fp; + + for (fp = sh_fd_list; fp != NULL; fp = fp->nxt) + if (fp->fd == fd) + return fp; + return NULL; +} + +STATIC void +renumber_sh_fd(struct shell_fds *fp) +{ + int to; + + if (fp == NULL) + return; + +#ifndef F_DUPFD_CLOEXEC +#define F_DUPFD_CLOEXEC F_DUPFD +#define CLOEXEC(fd) (fcntl((fd), F_SETFD, fcntl((fd),F_GETFD) | FD_CLOEXEC)) +#else +#define CLOEXEC(fd) +#endif + + /* + * if we have had a collision, and the sh fd was a "big" one + * try moving the sh fd base to a higher number (if possible) + * so future sh fds are less likely to be in the user's sights + * (incl this one when moved) + */ + if (fp->fd >= big_sh_fd) + find_big_fd(); + + to = fcntl(fp->fd, F_DUPFD_CLOEXEC, big_sh_fd); + if (to == -1) + to = fcntl(fp->fd, F_DUPFD_CLOEXEC, big_sh_fd/2); + if (to == -1) + to = fcntl(fp->fd, F_DUPFD_CLOEXEC, fp->fd + 1); + if (to == -1) + to = fcntl(fp->fd, F_DUPFD_CLOEXEC, 10); + if (to == -1) + to = fcntl(fp->fd, F_DUPFD_CLOEXEC, 3); + if (to == -1) + error("insufficient file descriptors available"); + CLOEXEC(to); + + if (fp->fd == to) /* impossible? */ + return; + + (*fp->cb)(fp->fd, to); + (void)close(fp->fd); + fp->fd = to; +} + +static const struct flgnames { + const char *name; + uint16_t minch; + uint32_t value; +} nv[] = { +#ifdef O_APPEND + { "append", 2, O_APPEND }, +#endif +#ifdef O_ASYNC + { "async", 2, O_ASYNC }, +#endif +#ifdef O_SYNC + { "sync", 2, O_SYNC }, +#endif +#ifdef O_NONBLOCK + { "nonblock", 3, O_NONBLOCK }, +#endif +#ifdef O_FSYNC + { "fsync", 2, O_FSYNC }, +#endif +#ifdef O_DSYNC + { "dsync", 2, O_DSYNC }, +#endif +#ifdef O_RSYNC + { "rsync", 2, O_RSYNC }, +#endif +#ifdef O_ALT_IO + { "altio", 2, O_ALT_IO }, +#endif +#ifdef O_DIRECT + { "direct", 2, O_DIRECT }, +#endif +#ifdef O_NOSIGPIPE + { "nosigpipe", 3, O_NOSIGPIPE }, +#endif +#ifdef O_CLOEXEC + { "cloexec", 2, O_CLOEXEC }, +#endif + { 0, 0, 0 } +}; +#define ALLFLAGS (O_APPEND|O_ASYNC|O_SYNC|O_NONBLOCK|O_DSYNC|O_RSYNC|\ + O_ALT_IO|O_DIRECT|O_NOSIGPIPE|O_CLOEXEC) + +static int +getflags(int fd, int p) +{ + int c, f; + + if (sh_fd(fd) != NULL) { + if (!p) + return -1; + error("Can't get status for fd=%d (%s)", fd, + "Bad file descriptor"); /*XXX*/ + } + + if ((c = fcntl(fd, F_GETFD)) == -1) { + if (!p) + return -1; + error("Can't get status for fd=%d (%s)", fd, strerror(errno)); + } + if ((f = fcntl(fd, F_GETFL)) == -1) { + if (!p) + return -1; + error("Can't get flags for fd=%d (%s)", fd, strerror(errno)); + } + if (c & FD_CLOEXEC) + f |= O_CLOEXEC; + return f & ALLFLAGS; +} + +static void +printone(int fd, int p, int verbose, int pfd) +{ + int f = getflags(fd, p); + const struct flgnames *fn; + + if (f == -1) + return; + + if (pfd) + outfmt(out1, "%d: ", fd); + for (fn = nv; fn->name; fn++) { + if (f & fn->value) { + outfmt(out1, "%s%s", verbose ? "+" : "", fn->name); + f &= ~fn->value; + } else if (verbose) + outfmt(out1, "-%s", fn->name); + else + continue; + if (f || (verbose && fn[1].name)) + outfmt(out1, ","); + } + if (verbose && f) /* f should be normally be 0 */ + outfmt(out1, " +%#x", f); + outfmt(out1, "\n"); +} + +static void +parseflags(char *s, int *p, int *n) +{ + int *v, *w; + const struct flgnames *fn; + size_t len; + + *p = 0; + *n = 0; + for (s = strtok(s, ","); s; s = strtok(NULL, ",")) { + switch (*s++) { + case '+': + v = p; + w = n; + break; + case '-': + v = n; + w = p; + break; + default: + error("Missing +/- indicator before flag %s", s-1); + } + + len = strlen(s); + for (fn = nv; fn->name; fn++) + if (len >= fn->minch && strncmp(s,fn->name,len) == 0) { + *v |= fn->value; + *w &=~ fn->value; + break; + } + if (fn->name == 0) + error("Bad flag `%s'", s); + } +} + +static void +setone(int fd, int pos, int neg, int verbose) +{ + int f = getflags(fd, 1); + int n, cloexec; + + if (f == -1) + return; + + cloexec = -1; + if ((pos & O_CLOEXEC) && !(f & O_CLOEXEC)) + cloexec = FD_CLOEXEC; + if ((neg & O_CLOEXEC) && (f & O_CLOEXEC)) + cloexec = 0; + + if (cloexec != -1 && fcntl(fd, F_SETFD, cloexec) == -1) + error("Can't set status for fd=%d (%s)", fd, strerror(errno)); + + pos &= ~O_CLOEXEC; + neg &= ~O_CLOEXEC; + f &= ~O_CLOEXEC; + n = f; + n |= pos; + n &= ~neg; + if (n != f && fcntl(fd, F_SETFL, n) == -1) + error("Can't set flags for fd=%d (%s)", fd, strerror(errno)); + if (verbose) + printone(fd, 1, verbose, 1); +} + +int +fdflagscmd(int argc, char *argv[]) +{ + char *num; + int verbose = 0, ch, pos = 0, neg = 0; + char *setflags = NULL; + + optreset = 1; optind = 1; /* initialize getopt */ + while ((ch = getopt(argc, argv, ":vs:")) != -1) + switch ((char)ch) { + case 'v': + verbose = 1; + break; + case 's': + if (setflags) + goto msg; + setflags = optarg; + break; + case '?': + default: + msg: + error("Usage: fdflags [-v] [-s <flags> fd] [fd...]"); + /* NOTREACHED */ + } + + argc -= optind, argv += optind; + + if (setflags) + parseflags(setflags, &pos, &neg); + + if (argc == 0) { + int i; + + if (setflags) + goto msg; + + for (i = 0; i <= max_user_fd; i++) + printone(i, 0, verbose, 1); + return 0; + } + + while ((num = *argv++) != NULL) { + int fd = number(num); + + while (num[0] == '0' && num[1] != '\0') /* skip 0's */ + num++; + if (strlen(num) > 5) + error("%s too big to be a file descriptor", num); + + if (setflags) + setone(fd, pos, neg, verbose); + else + printone(fd, 1, verbose, argc > 1); + } + return 0; +} + +#undef MAX /* in case we inherited them from somewhere */ +#undef MIN + +#define MIN(a,b) (/*CONSTCOND*/((a)<=(b)) ? (a) : (b)) +#define MAX(a,b) (/*CONSTCOND*/((a)>=(b)) ? (a) : (b)) + + /* now make the compiler work for us... */ +#define MIN_REDIR MIN(MIN(MIN(MIN(NTO,NFROM), MIN(NTOFD,NFROMFD)), \ + MIN(MIN(NCLOBBER,NAPPEND), MIN(NHERE,NXHERE))), NFROMTO) +#define MAX_REDIR MAX(MAX(MAX(MAX(NTO,NFROM), MAX(NTOFD,NFROMFD)), \ + MAX(MAX(NCLOBBER,NAPPEND), MAX(NHERE,NXHERE))), NFROMTO) + +static const char *redir_sym[MAX_REDIR - MIN_REDIR + 1] = { + [NTO - MIN_REDIR]= ">", + [NFROM - MIN_REDIR]= "<", + [NTOFD - MIN_REDIR]= ">&", + [NFROMFD - MIN_REDIR]= "<&", + [NCLOBBER - MIN_REDIR]= ">|", + [NAPPEND - MIN_REDIR]= ">>", + [NHERE - MIN_REDIR]= "<<", + [NXHERE - MIN_REDIR]= "<<", + [NFROMTO - MIN_REDIR]= "<>", +}; + +int +outredir(struct output *out, union node *n, int sep) +{ + if (n == NULL) + return 0; + if (n->type < MIN_REDIR || n->type > MAX_REDIR || + redir_sym[n->type - MIN_REDIR] == NULL) + return 0; + + if (sep) + outc(sep, out); + + /* + * ugly, but all redir node types have "fd" in same slot... + * (and code other places assumes it as well) + */ + if ((redir_sym[n->type - MIN_REDIR][0] == '<' && n->nfile.fd != 0) || + (redir_sym[n->type - MIN_REDIR][0] == '>' && n->nfile.fd != 1)) + outfmt(out, "%d", n->nfile.fd); + + outstr(redir_sym[n->type - MIN_REDIR], out); + + switch (n->type) { + case NHERE: + outstr("'...'", out); + break; + case NXHERE: + outstr("...", out); + break; + case NTOFD: + case NFROMFD: + if (n->ndup.dupfd < 0) + outc('-', out); + else + outfmt(out, "%d", n->ndup.dupfd); + break; + default: + outstr(n->nfile.expfname, out); + break; + } + return 1; +} |