diff options
Diffstat (limited to 'gnu/usr.bin/awk/io.c')
-rw-r--r-- | gnu/usr.bin/awk/io.c | 1207 |
1 files changed, 1207 insertions, 0 deletions
diff --git a/gnu/usr.bin/awk/io.c b/gnu/usr.bin/awk/io.c new file mode 100644 index 0000000..7004aed --- /dev/null +++ b/gnu/usr.bin/awk/io.c @@ -0,0 +1,1207 @@ +/* + * io.c --- routines for dealing with input and output and records + */ + +/* + * Copyright (C) 1986, 1988, 1989, 1991, 1992 the Free Software Foundation, Inc. + * + * This file is part of GAWK, the GNU implementation of the + * AWK Progamming Language. + * + * GAWK is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GAWK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GAWK; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "awk.h" + +#ifndef O_RDONLY +#include <fcntl.h> +#endif + +#if !defined(S_ISDIR) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +#ifndef atarist +#define INVALID_HANDLE (-1) +#else +#define INVALID_HANDLE (__SMALLEST_VALID_HANDLE - 1) +#endif + +#if defined(MSDOS) || defined(atarist) +#define PIPES_SIMULATED +#endif + +static IOBUF *nextfile P((int skipping)); +static int inrec P((IOBUF *iop)); +static int iop_close P((IOBUF *iop)); +struct redirect *redirect P((NODE *tree, int *errflg)); +static void close_one P((void)); +static int close_redir P((struct redirect *rp)); +#ifndef PIPES_SIMULATED +static int wait_any P((int interesting)); +#endif +static IOBUF *gawk_popen P((char *cmd, struct redirect *rp)); +static IOBUF *iop_open P((char *file, char *how)); +static int gawk_pclose P((struct redirect *rp)); +static int do_pathopen P((char *file)); + +extern FILE *fdopen(); +extern FILE *popen(); + +static struct redirect *red_head = NULL; + +extern int output_is_tty; +extern NODE *ARGC_node; +extern NODE *ARGV_node; +extern NODE *ARGIND_node; +extern NODE *ERRNO_node; +extern NODE **fields_arr; + +static jmp_buf filebuf; /* for do_nextfile() */ + +/* do_nextfile --- implement gawk "next file" extension */ + +void +do_nextfile() +{ + (void) nextfile(1); + longjmp(filebuf, 1); +} + +static IOBUF * +nextfile(skipping) +int skipping; +{ + static int i = 1; + static int files = 0; + NODE *arg; + int fd = INVALID_HANDLE; + static IOBUF *curfile = NULL; + + if (skipping) { + if (curfile != NULL) + iop_close(curfile); + curfile = NULL; + return NULL; + } + if (curfile != NULL) { + if (curfile->cnt == EOF) { + (void) iop_close(curfile); + curfile = NULL; + } else + return curfile; + } + for (; i < (int) (ARGC_node->lnode->numbr); i++) { + arg = *assoc_lookup(ARGV_node, tmp_number((AWKNUM) i)); + if (arg->stptr[0] == '\0') + continue; + arg->stptr[arg->stlen] = '\0'; + if (! do_unix) { + ARGIND_node->var_value->numbr = i; + ARGIND_node->var_value->flags = NUM|NUMBER; + } + if (!arg_assign(arg->stptr)) { + files++; + curfile = iop_open(arg->stptr, "r"); + if (curfile == NULL) + fatal("cannot open file `%s' for reading (%s)", + arg->stptr, strerror(errno)); + /* NOTREACHED */ + /* This is a kludge. */ + unref(FILENAME_node->var_value); + FILENAME_node->var_value = + dupnode(arg); + FNR = 0; + i++; + break; + } + } + if (files == 0) { + files++; + /* no args. -- use stdin */ + /* FILENAME is init'ed to "-" */ + /* FNR is init'ed to 0 */ + curfile = iop_alloc(fileno(stdin)); + } + return curfile; +} + +void +set_FNR() +{ + FNR = (int) FNR_node->var_value->numbr; +} + +void +set_NR() +{ + NR = (int) NR_node->var_value->numbr; +} + +/* + * This reads in a record from the input file + */ +static int +inrec(iop) +IOBUF *iop; +{ + char *begin; + register int cnt; + int retval = 0; + + cnt = get_a_record(&begin, iop, *RS, NULL); + if (cnt == EOF) { + cnt = 0; + retval = 1; + } else { + NR += 1; + FNR += 1; + } + set_record(begin, cnt, 1); + + return retval; +} + +static int +iop_close(iop) +IOBUF *iop; +{ + int ret; + + if (iop == NULL) + return 0; + errno = 0; + +#ifdef _CRAY + /* Work around bug in UNICOS popen */ + if (iop->fd < 3) + ret = 0; + else +#endif + /* save these for re-use; don't free the storage */ + if ((iop->flag & IOP_IS_INTERNAL) != 0) { + iop->off = iop->buf; + iop->end = iop->buf + strlen(iop->buf); + iop->cnt = 0; + iop->secsiz = 0; + return 0; + } + + /* Don't close standard files or else crufty code elsewhere will lose */ + if (iop->fd == fileno(stdin) || + iop->fd == fileno(stdout) || + iop->fd == fileno(stderr)) + ret = 0; + else + ret = close(iop->fd); + if (ret == -1) + warning("close of fd %d failed (%s)", iop->fd, strerror(errno)); + if ((iop->flag & IOP_NO_FREE) == 0) { + /* + * be careful -- $0 may still reference the buffer even though + * an explicit close is being done; in the future, maybe we + * can do this a bit better + */ + if (iop->buf) { + if ((fields_arr[0]->stptr >= iop->buf) + && (fields_arr[0]->stptr < iop->end)) { + NODE *t; + + t = make_string(fields_arr[0]->stptr, + fields_arr[0]->stlen); + unref(fields_arr[0]); + fields_arr [0] = t; + reset_record (); + } + free(iop->buf); + } + free((char *)iop); + } + return ret == -1 ? 1 : 0; +} + +void +do_input() +{ + IOBUF *iop; + extern int exiting; + + if (setjmp(filebuf) != 0) { + } + while ((iop = nextfile(0)) != NULL) { + if (inrec(iop) == 0) + while (interpret(expression_value) && inrec(iop) == 0) + ; + if (exiting) + break; + } +} + +/* Redirection for printf and print commands */ +struct redirect * +redirect(tree, errflg) +NODE *tree; +int *errflg; +{ + register NODE *tmp; + register struct redirect *rp; + register char *str; + int tflag = 0; + int outflag = 0; + char *direction = "to"; + char *mode; + int fd; + char *what = NULL; + + switch (tree->type) { + case Node_redirect_append: + tflag = RED_APPEND; + /* FALL THROUGH */ + case Node_redirect_output: + outflag = (RED_FILE|RED_WRITE); + tflag |= outflag; + if (tree->type == Node_redirect_output) + what = ">"; + else + what = ">>"; + break; + case Node_redirect_pipe: + tflag = (RED_PIPE|RED_WRITE); + what = "|"; + break; + case Node_redirect_pipein: + tflag = (RED_PIPE|RED_READ); + what = "|"; + break; + case Node_redirect_input: + tflag = (RED_FILE|RED_READ); + what = "<"; + break; + default: + fatal ("invalid tree type %d in redirect()", tree->type); + break; + } + tmp = tree_eval(tree->subnode); + if (do_lint && ! (tmp->flags & STR)) + warning("expression in `%s' redirection only has numeric value", + what); + tmp = force_string(tmp); + str = tmp->stptr; + if (str == NULL || *str == '\0') + fatal("expression for `%s' redirection has null string value", + what); + if (do_lint + && (STREQN(str, "0", tmp->stlen) || STREQN(str, "1", tmp->stlen))) + warning("filename `%s' for `%s' redirection may be result of logical expression", str, what); + for (rp = red_head; rp != NULL; rp = rp->next) + if (strlen(rp->value) == tmp->stlen + && STREQN(rp->value, str, tmp->stlen) + && ((rp->flag & ~(RED_NOBUF|RED_EOF)) == tflag + || (outflag + && (rp->flag & (RED_FILE|RED_WRITE)) == outflag))) + break; + if (rp == NULL) { + emalloc(rp, struct redirect *, sizeof(struct redirect), + "redirect"); + emalloc(str, char *, tmp->stlen+1, "redirect"); + memcpy(str, tmp->stptr, tmp->stlen); + str[tmp->stlen] = '\0'; + rp->value = str; + rp->flag = tflag; + rp->fp = NULL; + rp->iop = NULL; + rp->pid = 0; /* unlikely that we're worried about init */ + rp->status = 0; + /* maintain list in most-recently-used first order */ + if (red_head) + red_head->prev = rp; + rp->prev = NULL; + rp->next = red_head; + red_head = rp; + } + while (rp->fp == NULL && rp->iop == NULL) { + if (rp->flag & RED_EOF) + /* encountered EOF on file or pipe -- must be cleared + * by explicit close() before reading more + */ + return rp; + mode = NULL; + errno = 0; + switch (tree->type) { + case Node_redirect_output: + mode = "w"; + if (rp->flag & RED_USED) + mode = "a"; + break; + case Node_redirect_append: + mode = "a"; + break; + case Node_redirect_pipe: + if ((rp->fp = popen(str, "w")) == NULL) + fatal("can't open pipe (\"%s\") for output (%s)", + str, strerror(errno)); + rp->flag |= RED_NOBUF; + break; + case Node_redirect_pipein: + direction = "from"; + if (gawk_popen(str, rp) == NULL) + fatal("can't open pipe (\"%s\") for input (%s)", + str, strerror(errno)); + break; + case Node_redirect_input: + direction = "from"; + rp->iop = iop_open(str, "r"); + break; + default: + cant_happen(); + } + if (mode != NULL) { + fd = devopen(str, mode); + if (fd > INVALID_HANDLE) { + if (fd == fileno(stdin)) + rp->fp = stdin; + else if (fd == fileno(stdout)) + rp->fp = stdout; + else if (fd == fileno(stderr)) + rp->fp = stderr; + else + rp->fp = fdopen(fd, mode); + if (isatty(fd)) + rp->flag |= RED_NOBUF; + } + } + if (rp->fp == NULL && rp->iop == NULL) { + /* too many files open -- close one and try again */ + if (errno == EMFILE) + close_one(); + else { + /* + * Some other reason for failure. + * + * On redirection of input from a file, + * just return an error, so e.g. getline + * can return -1. For output to file, + * complain. The shell will complain on + * a bad command to a pipe. + */ + *errflg = errno; + if (tree->type == Node_redirect_output + || tree->type == Node_redirect_append) + fatal("can't redirect %s `%s' (%s)", + direction, str, strerror(errno)); + else { + free_temp(tmp); + return NULL; + } + } + } + } + free_temp(tmp); + return rp; +} + +static void +close_one() +{ + register struct redirect *rp; + register struct redirect *rplast = NULL; + + /* go to end of list first, to pick up least recently used entry */ + for (rp = red_head; rp != NULL; rp = rp->next) + rplast = rp; + /* now work back up through the list */ + for (rp = rplast; rp != NULL; rp = rp->prev) + if (rp->fp && (rp->flag & RED_FILE)) { + rp->flag |= RED_USED; + errno = 0; + if (fclose(rp->fp)) + warning("close of \"%s\" failed (%s).", + rp->value, strerror(errno)); + rp->fp = NULL; + break; + } + if (rp == NULL) + /* surely this is the only reason ??? */ + fatal("too many pipes or input files open"); +} + +NODE * +do_close(tree) +NODE *tree; +{ + NODE *tmp; + register struct redirect *rp; + + tmp = force_string(tree_eval(tree->subnode)); + for (rp = red_head; rp != NULL; rp = rp->next) { + if (strlen(rp->value) == tmp->stlen + && STREQN(rp->value, tmp->stptr, tmp->stlen)) + break; + } + free_temp(tmp); + if (rp == NULL) /* no match */ + return tmp_number((AWKNUM) 0.0); + fflush(stdout); /* synchronize regular output */ + tmp = tmp_number((AWKNUM)close_redir(rp)); + rp = NULL; + return tmp; +} + +static int +close_redir(rp) +register struct redirect *rp; +{ + int status = 0; + + if (rp == NULL) + return 0; + if (rp->fp == stdout || rp->fp == stderr) + return 0; + errno = 0; + if ((rp->flag & (RED_PIPE|RED_WRITE)) == (RED_PIPE|RED_WRITE)) + status = pclose(rp->fp); + else if (rp->fp) + status = fclose(rp->fp); + else if (rp->iop) { + if (rp->flag & RED_PIPE) + status = gawk_pclose(rp); + else { + status = iop_close(rp->iop); + rp->iop = NULL; + } + } + /* SVR4 awk checks and warns about status of close */ + if (status) { + char *s = strerror(errno); + + warning("failure status (%d) on %s close of \"%s\" (%s).", + status, + (rp->flag & RED_PIPE) ? "pipe" : + "file", rp->value, s); + + if (! do_unix) { + /* set ERRNO too so that program can get at it */ + unref(ERRNO_node->var_value); + ERRNO_node->var_value = make_string(s, strlen(s)); + } + } + if (rp->next) + rp->next->prev = rp->prev; + if (rp->prev) + rp->prev->next = rp->next; + else + red_head = rp->next; + free(rp->value); + free((char *)rp); + return status; +} + +int +flush_io () +{ + register struct redirect *rp; + int status = 0; + + errno = 0; + if (fflush(stdout)) { + warning("error writing standard output (%s).", strerror(errno)); + status++; + } + if (fflush(stderr)) { + warning("error writing standard error (%s).", strerror(errno)); + status++; + } + for (rp = red_head; rp != NULL; rp = rp->next) + /* flush both files and pipes, what the heck */ + if ((rp->flag & RED_WRITE) && rp->fp != NULL) { + if (fflush(rp->fp)) { + warning("%s flush of \"%s\" failed (%s).", + (rp->flag & RED_PIPE) ? "pipe" : + "file", rp->value, strerror(errno)); + status++; + } + } + return status; +} + +int +close_io () +{ + register struct redirect *rp; + register struct redirect *next; + int status = 0; + + errno = 0; + if (fclose(stdout)) { + warning("error writing standard output (%s).", strerror(errno)); + status++; + } + if (fclose(stderr)) { + warning("error writing standard error (%s).", strerror(errno)); + status++; + } + for (rp = red_head; rp != NULL; rp = next) { + next = rp->next; + if (close_redir(rp)) + status++; + rp = NULL; + } + return status; +} + +/* str2mode --- convert a string mode to an integer mode */ + +static int +str2mode(mode) +char *mode; +{ + int ret; + + switch(mode[0]) { + case 'r': + ret = O_RDONLY; + break; + + case 'w': + ret = O_WRONLY|O_CREAT|O_TRUNC; + break; + + case 'a': + ret = O_WRONLY|O_APPEND|O_CREAT; + break; + default: + cant_happen(); + } + return ret; +} + +/* devopen --- handle /dev/std{in,out,err}, /dev/fd/N, regular files */ + +/* + * This separate version is still needed for output, since file and pipe + * output is done with stdio. iop_open() handles input with IOBUFs of + * more "special" files. Those files are not handled here since it makes + * no sense to use them for output. + */ + +int +devopen(name, mode) +char *name, *mode; +{ + int openfd = INVALID_HANDLE; + char *cp, *ptr; + int flag = 0; + struct stat buf; + extern double strtod(); + + flag = str2mode(mode); + + if (do_unix) + goto strictopen; + +#ifdef VMS + if ((openfd = vms_devopen(name, flag)) >= 0) + return openfd; +#endif /* VMS */ + + if (STREQ(name, "-")) + openfd = fileno(stdin); + else if (STREQN(name, "/dev/", 5) && stat(name, &buf) == -1) { + cp = name + 5; + + if (STREQ(cp, "stdin") && (flag & O_RDONLY) == O_RDONLY) + openfd = fileno(stdin); + else if (STREQ(cp, "stdout") && (flag & O_WRONLY) == O_WRONLY) + openfd = fileno(stdout); + else if (STREQ(cp, "stderr") && (flag & O_WRONLY) == O_WRONLY) + openfd = fileno(stderr); + else if (STREQN(cp, "fd/", 3)) { + cp += 3; + openfd = (int)strtod(cp, &ptr); + if (openfd <= INVALID_HANDLE || ptr == cp) + openfd = INVALID_HANDLE; + } + } + +strictopen: + if (openfd == INVALID_HANDLE) + openfd = open(name, flag, 0666); + if (openfd != INVALID_HANDLE && fstat(openfd, &buf) > 0) + if (S_ISDIR(buf.st_mode)) + fatal("file `%s' is a directory", name); + return openfd; +} + + +/* spec_setup --- setup an IOBUF for a special internal file */ + +void +spec_setup(iop, len, allocate) +IOBUF *iop; +int len; +int allocate; +{ + char *cp; + + if (allocate) { + emalloc(cp, char *, len+2, "spec_setup"); + iop->buf = cp; + } else { + len = strlen(iop->buf); + iop->buf[len++] = '\n'; /* get_a_record clobbered it */ + iop->buf[len] = '\0'; /* just in case */ + } + iop->off = iop->buf; + iop->cnt = 0; + iop->secsiz = 0; + iop->size = len; + iop->end = iop->buf + len; + iop->fd = -1; + iop->flag = IOP_IS_INTERNAL; +} + +/* specfdopen --- open a fd special file */ + +int +specfdopen(iop, name, mode) +IOBUF *iop; +char *name, *mode; +{ + int fd; + IOBUF *tp; + + fd = devopen(name, mode); + if (fd == INVALID_HANDLE) + return INVALID_HANDLE; + tp = iop_alloc(fd); + if (tp == NULL) + return INVALID_HANDLE; + *iop = *tp; + iop->flag |= IOP_NO_FREE; + free(tp); + return 0; +} + +/* pidopen --- "open" /dev/pid, /dev/ppid, and /dev/pgrpid */ + +int +pidopen(iop, name, mode) +IOBUF *iop; +char *name, *mode; +{ + char tbuf[BUFSIZ]; + int i; + + if (name[6] == 'g') +/* following #if will improve in 2.16 */ +#if defined(__svr4__) || defined(i860) || defined(_AIX) || defined(BSD4_4) || defined(__386BSD__) + sprintf(tbuf, "%d\n", getpgrp()); +#else + sprintf(tbuf, "%d\n", getpgrp(getpid())); +#endif + else if (name[6] == 'i') + sprintf(tbuf, "%d\n", getpid()); + else + sprintf(tbuf, "%d\n", getppid()); + i = strlen(tbuf); + spec_setup(iop, i, 1); + strcpy(iop->buf, tbuf); + return 0; +} + +/* useropen --- "open" /dev/user */ + +/* + * /dev/user creates a record as follows: + * $1 = getuid() + * $2 = geteuid() + * $3 = getgid() + * $4 = getegid() + * If multiple groups are supported, the $5 through $NF are the + * supplementary group set. + */ + +int +useropen(iop, name, mode) +IOBUF *iop; +char *name, *mode; +{ + char tbuf[BUFSIZ], *cp; + int i; +#if defined(NGROUPS_MAX) && NGROUPS_MAX > 0 + int groupset[NGROUPS_MAX]; + int ngroups; +#endif + + sprintf(tbuf, "%d %d %d %d", getuid(), geteuid(), getgid(), getegid()); + + cp = tbuf + strlen(tbuf); +#if defined(NGROUPS_MAX) && NGROUPS_MAX > 0 + ngroups = getgroups(NGROUPS_MAX, groupset); + if (ngroups == -1) + fatal("could not find groups: %s", strerror(errno)); + + for (i = 0; i < ngroups; i++) { + *cp++ = ' '; + sprintf(cp, "%d", groupset[i]); + cp += strlen(cp); + } +#endif + *cp++ = '\n'; + *cp++ = '\0'; + + + i = strlen(tbuf); + spec_setup(iop, i, 1); + strcpy(iop->buf, tbuf); + return 0; +} + +/* iop_open --- handle special and regular files for input */ + +static IOBUF * +iop_open(name, mode) +char *name, *mode; +{ + int openfd = INVALID_HANDLE; + char *cp, *ptr; + int flag = 0; + int i; + struct stat buf; + IOBUF *iop; + static struct internal { + char *name; + int compare; + int (*fp)(); + IOBUF iob; + } table[] = { + { "/dev/fd/", 8, specfdopen }, + { "/dev/stdin", 10, specfdopen }, + { "/dev/stdout", 11, specfdopen }, + { "/dev/stderr", 11, specfdopen }, + { "/dev/pid", 8, pidopen }, + { "/dev/ppid", 9, pidopen }, + { "/dev/pgrpid", 11, pidopen }, + { "/dev/user", 9, useropen }, + }; + int devcount = sizeof(table) / sizeof(table[0]); + + flag = str2mode(mode); + + if (do_unix) + goto strictopen; + + if (STREQ(name, "-")) + openfd = fileno(stdin); + else if (STREQN(name, "/dev/", 5) && stat(name, &buf) == -1) { + int i; + + for (i = 0; i < devcount; i++) { + if (STREQN(name, table[i].name, table[i].compare)) { + IOBUF *iop = & table[i].iob; + + if (iop->buf != NULL) { + spec_setup(iop, 0, 0); + return iop; + } else if ((*table[i].fp)(iop, name, mode) == 0) + return iop; + else { + warning("could not open %s, mode `%s'", + name, mode); + return NULL; + } + } + } + } + +strictopen: + if (openfd == INVALID_HANDLE) + openfd = open(name, flag, 0666); + if (openfd != INVALID_HANDLE && fstat(openfd, &buf) > 0) + if ((buf.st_mode & S_IFMT) == S_IFDIR) + fatal("file `%s' is a directory", name); + iop = iop_alloc(openfd); + return iop; +} + +#ifndef PIPES_SIMULATED + /* real pipes */ +static int +wait_any(interesting) +int interesting; /* pid of interest, if any */ +{ + SIGTYPE (*hstat)(), (*istat)(), (*qstat)(); + int pid; + int status = 0; + struct redirect *redp; + extern int errno; + + hstat = signal(SIGHUP, SIG_IGN); + istat = signal(SIGINT, SIG_IGN); + qstat = signal(SIGQUIT, SIG_IGN); + for (;;) { +#ifdef NeXT + pid = wait((union wait *)&status); +#else + pid = wait(&status); +#endif /* NeXT */ + if (interesting && pid == interesting) { + break; + } else if (pid != -1) { + for (redp = red_head; redp != NULL; redp = redp->next) + if (pid == redp->pid) { + redp->pid = -1; + redp->status = status; + if (redp->fp) { + pclose(redp->fp); + redp->fp = 0; + } + if (redp->iop) { + (void) iop_close(redp->iop); + redp->iop = 0; + } + break; + } + } + if (pid == -1 && errno == ECHILD) + break; + } + signal(SIGHUP, hstat); + signal(SIGINT, istat); + signal(SIGQUIT, qstat); + return(status); +} + +static IOBUF * +gawk_popen(cmd, rp) +char *cmd; +struct redirect *rp; +{ + int p[2]; + register int pid; + + /* used to wait for any children to synchronize input and output, + * but this could cause gawk to hang when it is started in a pipeline + * and thus has a child process feeding it input (shell dependant) + */ + /*(void) wait_any(0);*/ /* wait for outstanding processes */ + + if (pipe(p) < 0) + fatal("cannot open pipe \"%s\" (%s)", cmd, strerror(errno)); + if ((pid = fork()) == 0) { + if (close(1) == -1) + fatal("close of stdout in child failed (%s)", + strerror(errno)); + if (dup(p[1]) != 1) + fatal("dup of pipe failed (%s)", strerror(errno)); + if (close(p[0]) == -1 || close(p[1]) == -1) + fatal("close of pipe failed (%s)", strerror(errno)); + if (close(0) == -1) + fatal("close of stdin in child failed (%s)", + strerror(errno)); + execl("/bin/sh", "sh", "-c", cmd, 0); + _exit(127); + } + if (pid == -1) + fatal("cannot fork for \"%s\" (%s)", cmd, strerror(errno)); + rp->pid = pid; + if (close(p[1]) == -1) + fatal("close of pipe failed (%s)", strerror(errno)); + return (rp->iop = iop_alloc(p[0])); +} + +static int +gawk_pclose(rp) +struct redirect *rp; +{ + (void) iop_close(rp->iop); + rp->iop = NULL; + + /* process previously found, return stored status */ + if (rp->pid == -1) + return (rp->status >> 8) & 0xFF; + rp->status = wait_any(rp->pid); + rp->pid = -1; + return (rp->status >> 8) & 0xFF; +} + +#else /* PIPES_SIMULATED */ + /* use temporary file rather than pipe */ + +#ifdef VMS +static IOBUF * +gawk_popen(cmd, rp) +char *cmd; +struct redirect *rp; +{ + FILE *current; + + if ((current = popen(cmd, "r")) == NULL) + return NULL; + return (rp->iop = iop_alloc(fileno(current))); +} + +static int +gawk_pclose(rp) +struct redirect *rp; +{ + int rval, aval, fd = rp->iop->fd; + FILE *kludge = fdopen(fd, "r"); /* pclose needs FILE* w/ right fileno */ + + rp->iop->fd = dup(fd); /* kludge to allow close() + pclose() */ + rval = iop_close(rp->iop); + rp->iop = NULL; + aval = pclose(kludge); + return (rval < 0 ? rval : aval); +} +#else /* VMS */ + +static +struct { + char *command; + char *name; +} pipes[_NFILE]; + +static IOBUF * +gawk_popen(cmd, rp) +char *cmd; +struct redirect *rp; +{ + extern char *strdup(const char *); + int current; + char *name; + static char cmdbuf[256]; + + /* get a name to use. */ + if ((name = tempnam(".", "pip")) == NULL) + return NULL; + sprintf(cmdbuf,"%s > %s", cmd, name); + system(cmdbuf); + if ((current = open(name,O_RDONLY)) == INVALID_HANDLE) + return NULL; + pipes[current].name = name; + pipes[current].command = strdup(cmd); + rp->iop = iop_alloc(current); + return (rp->iop = iop_alloc(current)); +} + +static int +gawk_pclose(rp) +struct redirect *rp; +{ + int cur = rp->iop->fd; + int rval; + + rval = iop_close(rp->iop); + rp->iop = NULL; + + /* check for an open file */ + if (pipes[cur].name == NULL) + return -1; + unlink(pipes[cur].name); + free(pipes[cur].name); + pipes[cur].name = NULL; + free(pipes[cur].command); + return rval; +} +#endif /* VMS */ + +#endif /* PIPES_SIMULATED */ + +NODE * +do_getline(tree) +NODE *tree; +{ + struct redirect *rp = NULL; + IOBUF *iop; + int cnt = EOF; + char *s = NULL; + int errcode; + + while (cnt == EOF) { + if (tree->rnode == NULL) { /* no redirection */ + iop = nextfile(0); + if (iop == NULL) /* end of input */ + return tmp_number((AWKNUM) 0.0); + } else { + int redir_error = 0; + + rp = redirect(tree->rnode, &redir_error); + if (rp == NULL && redir_error) { /* failed redirect */ + if (! do_unix) { + char *s = strerror(redir_error); + + unref(ERRNO_node->var_value); + ERRNO_node->var_value = + make_string(s, strlen(s)); + } + return tmp_number((AWKNUM) -1.0); + } + iop = rp->iop; + if (iop == NULL) /* end of input */ + return tmp_number((AWKNUM) 0.0); + } + errcode = 0; + cnt = get_a_record(&s, iop, *RS, & errcode); + if (! do_unix && errcode != 0) { + char *s = strerror(errcode); + + unref(ERRNO_node->var_value); + ERRNO_node->var_value = make_string(s, strlen(s)); + return tmp_number((AWKNUM) -1.0); + } + if (cnt == EOF) { + if (rp) { + /* + * Don't do iop_close() here if we are + * reading from a pipe; otherwise + * gawk_pclose will not be called. + */ + if (!(rp->flag & RED_PIPE)) { + (void) iop_close(iop); + rp->iop = NULL; + } + rp->flag |= RED_EOF; /* sticky EOF */ + return tmp_number((AWKNUM) 0.0); + } else + continue; /* try another file */ + } + if (!rp) { + NR += 1; + FNR += 1; + } + if (tree->lnode == NULL) /* no optional var. */ + set_record(s, cnt, 1); + else { /* assignment to variable */ + Func_ptr after_assign = NULL; + NODE **lhs; + + lhs = get_lhs(tree->lnode, &after_assign); + unref(*lhs); + *lhs = make_string(s, strlen(s)); + (*lhs)->flags |= MAYBE_NUM; + /* we may have to regenerate $0 here! */ + if (after_assign) + (*after_assign)(); + } + } + return tmp_number((AWKNUM) 1.0); +} + +int +pathopen (file) +char *file; +{ + int fd = do_pathopen(file); + +#ifdef DEFAULT_FILETYPE + if (! do_unix && fd <= INVALID_HANDLE) { + char *file_awk; + int save = errno; +#ifdef VMS + int vms_save = vaxc$errno; +#endif + + /* append ".awk" and try again */ + emalloc(file_awk, char *, strlen(file) + + sizeof(DEFAULT_FILETYPE) + 1, "pathopen"); + sprintf(file_awk, "%s%s", file, DEFAULT_FILETYPE); + fd = do_pathopen(file_awk); + free(file_awk); + if (fd <= INVALID_HANDLE) { + errno = save; +#ifdef VMS + vaxc$errno = vms_save; +#endif + } + } +#endif /*DEFAULT_FILETYPE*/ + + return fd; +} + +static int +do_pathopen (file) +char *file; +{ + static char *savepath = DEFPATH; /* defined in config.h */ + static int first = 1; + char *awkpath, *cp; + char trypath[BUFSIZ]; + int fd; + + if (STREQ(file, "-")) + return (0); + + if (do_unix) + return (devopen(file, "r")); + + if (first) { + first = 0; + if ((awkpath = getenv ("AWKPATH")) != NULL && *awkpath) + savepath = awkpath; /* used for restarting */ + } + awkpath = savepath; + + /* some kind of path name, no search */ +#ifdef VMS /* (strchr not equal implies either or both not NULL) */ + if (strchr(file, ':') != strchr(file, ']') + || strchr(file, '>') != strchr(file, '/')) +#else /*!VMS*/ +#ifdef MSDOS + if (strchr(file, '/') != strchr(file, '\\') + || strchr(file, ':') != NULL) +#else + if (strchr(file, '/') != NULL) +#endif /*MSDOS*/ +#endif /*VMS*/ + return (devopen(file, "r")); + + do { + trypath[0] = '\0'; + /* this should take into account limits on size of trypath */ + for (cp = trypath; *awkpath && *awkpath != ENVSEP; ) + *cp++ = *awkpath++; + + if (cp != trypath) { /* nun-null element in path */ + /* add directory punctuation only if needed */ +#ifdef VMS + if (strchr(":]>/", *(cp-1)) == NULL) +#else +#ifdef MSDOS + if (strchr(":\\/", *(cp-1)) == NULL) +#else + if (*(cp-1) != '/') +#endif +#endif + *cp++ = '/'; + /* append filename */ + strcpy (cp, file); + } else + strcpy (trypath, file); + if ((fd = devopen(trypath, "r")) >= 0) + return (fd); + + /* no luck, keep going */ + if(*awkpath == ENVSEP && awkpath[1] != '\0') + awkpath++; /* skip colon */ + } while (*awkpath); + /* + * You might have one of the awk + * paths defined, WITHOUT the current working directory in it. + * Therefore try to open the file in the current directory. + */ + return (devopen(file, "r")); +} |