freebsd-skq/gnu/usr.bin/awk/io.c
1994-05-31 18:58:00 +00:00

1283 lines
28 KiB
C

/*
* io.c --- routines for dealing with input and output and records
*/
/*
* Copyright (C) 1986, 1988, 1989, 1991, 1992, 1993 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.
*/
#if !defined(VMS) && !defined(VMS_POSIX) && !defined(_MSC_VER)
#include <sys/param.h>
#endif
#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 ENFILE
#define ENFILE EMFILE
#endif
#ifndef atarist
#define INVALID_HANDLE (-1)
#else
#define INVALID_HANDLE (__SMALLEST_VALID_HANDLE - 1)
#endif
#if defined(MSDOS) || defined(OS2) || 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, int exitwarn));
#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((const char *file, const char *how));
static int gawk_pclose P((struct redirect *rp));
static int do_pathopen P((const char *file));
static int str2mode P((const char *mode));
static void spec_setup P((IOBUF *iop, int len, int allocate));
static int specfdopen P((IOBUF *iop, const char *name, const char *mode));
static int pidopen P((IOBUF *iop, const char *name, const char *mode));
static int useropen P((IOBUF *iop, const char *name, const char *mode));
extern FILE *fdopen();
#if defined (MSDOS)
#include "popen.h"
#define popen(c,m) os_popen(c,m)
#define pclose(f) os_pclose(f)
#elif defined (OS2) /* OS/2, but not family mode */
#if defined (_MSC_VER)
#define popen(c,m) _popen(c,m)
#define pclose(f) _pclose(f)
#endif
#else
extern FILE *popen();
#endif
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;
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 */
/* FNR is init'ed to 0 */
FILENAME_node->var_value = make_string("-", 1);
curfile = iop_alloc(fileno(stdin));
}
return curfile;
}
void
set_FNR()
{
FNR = (long) FNR_node->var_value->numbr;
}
void
set_NR()
{
NR = (long) 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;
(void) setjmp(filebuf);
while ((iop = nextfile(0)) != NULL) {
if (inrec(iop) == 0)
while (interpret(expression_value) && inrec(iop) == 0)
continue;
/* recover any space from C based alloca */
(void) alloca(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;
const char *direction = "to";
const char *mode;
int fd;
const 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, (char *) mode);
/* don't leak file descriptors */
if (rp->fp == NULL)
close(fd);
}
if (rp->fp != NULL && 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 || errno == ENFILE)
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, 0));
rp = NULL;
return tmp;
}
static int
close_redir(rp, exitwarn)
register struct redirect *rp;
int exitwarn;
{
int status = 0;
char *what;
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;
}
}
what = (rp->flag & RED_PIPE) ? "pipe" : "file";
if (exitwarn)
warning("no explicit close of %s \"%s\" provided",
what, rp->value);
/* 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, what, 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;
for (rp = red_head; rp != NULL; rp = next) {
next = rp->next;
/* close_redir() will print a message if needed */
/* if do_lint, warn about lack of explicit close */
if (close_redir(rp, do_lint))
status++;
rp = NULL;
}
/*
* Some of the non-Unix os's have problems doing an fclose
* on stdout and stderr. Since we don't really need to close
* them, we just flush them, and do that across the board.
*/
if (fflush(stdout)) {
warning("error writing standard output (%s).", strerror(errno));
status++;
}
if (fflush(stderr)) {
warning("error writing standard error (%s).", strerror(errno));
status++;
}
return status;
}
/* str2mode --- convert a string mode to an integer mode */
static int
str2mode(mode)
const 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:
ret = 0; /* lint */
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)
const char *name, *mode;
{
int openfd = INVALID_HANDLE;
const 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((char *) 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 */
static 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 */
static int
specfdopen(iop, name, mode)
IOBUF *iop;
const 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;
}
/*
* Following mess will improve in 2.16; this is written to avoid
* long lines, avoid splitting #if with backslash, and avoid #elif
* to maximize portability.
*/
#ifndef GETPGRP_NOARG
#if defined(__svr4__) || defined(BSD4_4) || defined(_POSIX_SOURCE)
#define GETPGRP_NOARG
#else
#if defined(i860) || defined(_AIX) || defined(hpux) || defined(VMS)
#define GETPGRP_NOARG
#else
#if defined(OS2) || defined(MSDOS) || defined(AMIGA) || defined(atarist)
#define GETPGRP_NOARG
#endif
#endif
#endif
#endif
#ifdef GETPGRP_NOARG
#define getpgrp_ARG /* nothing */
#else
#define getpgrp_ARG getpid()
#endif
/* pidopen --- "open" /dev/pid, /dev/ppid, and /dev/pgrpid */
static int
pidopen(iop, name, mode)
IOBUF *iop;
const char *name, *mode;
{
char tbuf[BUFSIZ];
int i;
if (name[6] == 'g')
sprintf(tbuf, "%d\n", getpgrp( getpgrp_ARG ));
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.
*/
static int
useropen(iop, name, mode)
IOBUF *iop;
const char *name, *mode;
{
char tbuf[BUFSIZ], *cp;
int i;
#if defined(NGROUPS_MAX) && NGROUPS_MAX > 0
#if defined(atarist) || defined(__svr4__) || defined(__osf__)
gid_t groupset[NGROUPS_MAX];
#else
int groupset[NGROUPS_MAX];
#endif
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", (int)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)
const char *name, *mode;
{
int openfd = INVALID_HANDLE;
int flag = 0;
struct stat buf;
IOBUF *iop;
static struct internal {
const char *name;
int compare;
int (*fp) P((IOBUF*,const char *,const char *));
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((char *) name, &buf) == -1) {
int i;
for (i = 0; i < devcount; i++) {
if (STREQN(name, table[i].name, table[i].compare)) {
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 */
/* except if popen() provides real pipes too */
#if defined(VMS) || defined(OS2) || defined (MSDOS)
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, (char *) "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 || OS2 || MSDOS */
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 || OS2 || MSDOS */
#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) {
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) {
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)
const 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)
const char *file;
{
static const char *savepath = DEFPATH; /* defined in config.h */
static int first = 1;
const char *awkpath;
char *cp, 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*/
#if defined(MSDOS) || defined(OS2)
if (strchr(file, '/') != strchr(file, '\\')
|| strchr(file, ':') != NULL)
#else
if (strchr(file, '/') != NULL)
#endif /*MSDOS*/
#endif /*VMS*/
return (devopen(file, "r"));
#if defined(MSDOS) || defined(OS2)
_searchenv(file, "AWKPATH", trypath);
if (trypath[0] == '\0')
_searchenv(file, "PATH", trypath);
return (trypath[0] == '\0') ? 0 : devopen(trypath, "r");
#else
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
#if defined(MSDOS) || defined(OS2)
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"));
#endif
}