diff options
Diffstat (limited to 'contrib/awk/io.c')
-rw-r--r-- | contrib/awk/io.c | 2400 |
1 files changed, 0 insertions, 2400 deletions
diff --git a/contrib/awk/io.c b/contrib/awk/io.c deleted file mode 100644 index 8d741e2..0000000 --- a/contrib/awk/io.c +++ /dev/null @@ -1,2400 +0,0 @@ -/* - * io.c --- routines for dealing with input and output and records - */ - -/* - * Copyright (C) 1976, 1988, 1989, 1991-2001 the Free Software Foundation, Inc. - * - * This file is part of GAWK, the GNU implementation of the - * AWK Programming 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - * - * $FreeBSD$ - */ - -#include "awk.h" - -#ifdef HAVE_SYS_PARAM_H -#undef RE_DUP_MAX /* avoid spurious conflict w/regex.h */ -#include <sys/param.h> -#endif /* HAVE_SYS_PARAM_H */ - -#ifdef HAVE_SYS_WAIT_H -#include <sys/wait.h> -#endif /* HAVE_SYS_WAIT_H */ - -#ifndef O_RDONLY -#include <fcntl.h> -#endif -#ifndef O_ACCMODE -#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) -#endif - -#ifdef HAVE_SOCKETS -#ifdef HAVE_SYS_SOCKET_H -#include <sys/socket.h> -#else -#include <socket.h> -#endif /* HAVE_SYS_SOCKET_H */ -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#else -#include <in.h> -#endif /* HAVE_NETINET_IN_H */ -#ifdef HAVE_NETDB_H -#include <netdb.h> -#endif /* HAVE_NETDB_H */ -#endif /* HAVE_SOCKETS */ - -#if ! defined(S_ISREG) && defined(S_IFREG) -#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) -#endif - -#ifndef ENFILE -#define ENFILE EMFILE -#endif - -extern int MRL; - -#ifdef HAVE_SOCKETS -enum inet_prot { INET_NONE, INET_TCP, INET_UDP, INET_RAW }; - -#ifndef SHUT_RD -#define SHUT_RD 0 -#endif - -#ifndef SHUT_WR -#define SHUT_WR 1 -#endif - -#ifndef SHUT_RDWR -#define SHUT_RDWR 2 -#endif - -#endif /* HAVE_SOCKETS */ - -#ifdef atarist -#include <stddef.h> -#endif - -#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(TANDEM) -#define PIPES_SIMULATED -#endif - -typedef enum { CLOSE_ALL, CLOSE_TO, CLOSE_FROM } two_way_close_type; - -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, two_way_close_type how)); -#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, IOBUF *buf)); -static IOBUF *iop_alloc P((int fd, const char *name, IOBUF *buf)); -static int gawk_pclose P((struct redirect *rp)); -static int do_pathopen P((const char *file)); -static int get_a_record P((char **out, IOBUF *iop, int rs, Regexp *RSre, int *errcode)); -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)); -static int two_way_open P((char *str, struct redirect *rp)); - -#if defined(HAVE_POPEN_H) -#include "popen.h" -#endif - -static struct redirect *red_head = NULL; -static NODE *RS; -static Regexp *RS_regexp; - -int RS_is_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() */ - -#if defined(MSDOS) || defined(OS2) -static const char * -binmode(char *mode) -{ - switch (mode[0]) { - case 'r': - if ((BINMODE & 1) != 0) - mode = "rb"; - break; - case 'w': - case 'a': - if ((BINMODE & 2) != 0) - mode = (mode[0] == 'w' ? "wb" : "ab"); - break; - } - return mode; -} -#else -#define binmode(mode) (mode) -#endif - -#ifdef VMS -/* File pointers have an extra level of indirection, and there are cases where - `stdin' can be null. That can crash gawk if fileno() is used as-is. */ -static int vmsrtl_fileno P((FILE *)); -static int vmsrtl_fileno(fp) FILE *fp; { return fileno(fp); } -#undef fileno -#define fileno(FP) (((FP) && *(FP)) ? vmsrtl_fileno(FP) : -1) -#endif /* VMS */ - -/* do_nextfile --- implement gawk "nextfile" extension */ - -void -do_nextfile() -{ - (void) nextfile(TRUE); - longjmp(filebuf, 1); -} - -/* nextfile --- move to the next input data file */ - -static IOBUF * -nextfile(int skipping) -{ - static long i = 1; - static int files = 0; - NODE *arg; - static IOBUF *curfile = NULL; - static IOBUF mybuf; - const char *fname; - - 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 < (long) (ARGC_node->lnode->numbr); i++) { - arg = *assoc_lookup(ARGV_node, tmp_number((AWKNUM) i), FALSE); - if (arg->stlen == 0) - continue; - arg->stptr[arg->stlen] = '\0'; - if (! do_traditional) { - unref(ARGIND_node->var_value); - ARGIND_node->var_value = make_number((AWKNUM) i); - } - if (! arg_assign(arg->stptr)) { - files++; - fname = arg->stptr; - curfile = iop_open(fname, binmode("r"), &mybuf); - if (curfile == NULL) - goto give_up; - curfile->flag |= IOP_NOFREE_OBJ; - /* 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); - fname = "-"; - curfile = iop_open(fname, binmode("r"), &mybuf); - if (curfile == NULL) - goto give_up; - curfile->flag |= IOP_NOFREE_OBJ; - } - return curfile; - - give_up: - fatal(_("cannot open file `%s' for reading (%s)"), - fname, strerror(errno)); - /* NOTREACHED */ - return 0; -} - -/* set_FNR --- update internal FNR from awk variable */ - -void -set_FNR() -{ - FNR = (long) FNR_node->var_value->numbr; -} - -/* set_NR --- update internal NR from awk variable */ - -void -set_NR() -{ - NR = (long) NR_node->var_value->numbr; -} - -/* inrec --- This reads in a record from the input file */ - -static int -inrec(IOBUF *iop) -{ - char *begin; - register int cnt; - int retval = 0; - - if ((cnt = iop->cnt) != EOF) - cnt = get_a_record(&begin, iop, RS->stptr[0], RS_regexp, NULL); - if (cnt == EOF) { - cnt = 0; - retval = 1; - } else { - NR += 1; - FNR += 1; - set_record(begin, cnt, TRUE); - } - - return retval; -} - -/* iop_close --- close an open IOP */ - -static int -iop_close(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 (`%s') failed (%s)"), iop->fd, - iop->name, 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->buf + iop->secsiz + iop->size))) { - 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); - } - if ((iop->flag & IOP_NOFREE_OBJ) == 0) - free((char *) iop); - } - return ret == -1 ? 1 : 0; -} - -/* do_input --- the main input processing loop */ - -void -do_input() -{ - IOBUF *iop; - extern int exiting; - - (void) setjmp(filebuf); /* for `nextfile' */ - - while ((iop = nextfile(FALSE)) != NULL) { - if (inrec(iop) == 0) - while (interpret(expression_value) && inrec(iop) == 0) - continue; - if (exiting) - break; - } -} - -/* redflags2str --- turn redirection flags into a string, for debugging */ - -char * -redflags2str(int flags) -{ - static struct flagtab redtab[] = { - { RED_FILE, "RED_FILE" }, - { RED_PIPE, "RED_PIPE" }, - { RED_READ, "RED_READ" }, - { RED_WRITE, "RED_WRITE" }, - { RED_APPEND, "RED_APPEND" }, - { RED_NOBUF, "RED_NOBUF" }, - { RED_EOF, "RED_EOF" }, - { RED_TWOWAY, "RED_TWOWAY" }, - { RED_SOCKET, "RED_SOCKET" }, - { RED_TCP, "RED_TCP" }, - { 0, NULL } - }; - - return genflags2str(flags, redtab); -} - -/* redirect --- Redirection for printf and print commands */ - -struct redirect * -redirect(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; - case Node_redirect_twoway: - tflag = (RED_READ|RED_WRITE|RED_TWOWAY); - what = "|&"; - break; - default: - fatal(_("invalid tree type %s in redirect()"), - nodetype2str(tree->type)); - break; - } - tmp = tree_eval(tree->subnode); - if (do_lint && (tmp->flags & STR) == 0) - lintwarn(_("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))) - lintwarn(_("filename `%s' for `%s' redirection may be result of logical expression"), str, what); - -#ifdef HAVE_SOCKETS - if (STREQN(str, "/inet/", 6)) { - tflag |= RED_SOCKET; - if (STREQN(str + 6, "tcp/", 4)) - tflag |= RED_TCP; /* use shutdown when closing */ - } -#endif /* HAVE_SOCKETS */ - - 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 != 0 - && (rp->flag & (RED_FILE|RED_WRITE)) == outflag))) { - - int rpflag = (rp->flag & ~(RED_NOBUF|RED_EOF)); - int newflag = (tflag & ~(RED_NOBUF|RED_EOF)); - - if (do_lint && rpflag != newflag) - lintwarn( - _("unnecessary mixing of `>' and `>>' for file `%.*s'"), - tmp->stlen, rp->value); - - 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 != NULL) - red_head->prev = rp; - rp->prev = NULL; - rp->next = red_head; - red_head = rp; - } else - str = rp->value; /* get \0 terminated string */ - - 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 = binmode("w"); - if ((rp->flag & RED_USED) != 0) - mode = (rp->mode[1] == 'b') ? "ab" : "a"; - break; - case Node_redirect_append: - mode = binmode("a"); - break; - case Node_redirect_pipe: - /* synchronize output before new pipe */ - (void) flush_io(); - - os_restore_mode(fileno(stdin)); - if ((rp->fp = popen(str, binmode("w"))) == NULL) - fatal(_("can't open pipe `%s' for output (%s)"), - str, strerror(errno)); - /* set close-on-exec */ - os_close_on_exec(fileno(rp->fp), str, "pipe", "to"); - 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, binmode("r"), NULL); - break; - case Node_redirect_twoway: - direction = "to/from"; - if (!two_way_open(str, rp)) { -#ifdef HAVE_SOCKETS - /* multiple messages make life easier for translators */ - if (STREQN(str, "/inet/", 6)) - fatal(_("can't open two way socket `%s' for input/output (%s)"), - str, strerror(errno)); - else -#endif - fatal(_("can't open two way pipe `%s' for input/output (%s)"), - str, strerror(errno)); - } - break; - default: - cant_happen(); - } - if (mode != NULL) { - errno = 0; - 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 { -#if defined(F_GETFL) && defined(O_APPEND) - int fd_flags; - - fd_flags = fcntl(fd, F_GETFL); - if (fd_flags != -1 && (fd_flags & O_APPEND) == O_APPEND) - rp->fp = fdopen(fd, binmode("a")); - else -#endif - rp->fp = fdopen(fd, (char *) mode); - rp->mode = (char *) mode; - /* don't leak file descriptors */ - if (rp->fp == NULL) - close(fd); - } - if (rp->fp != NULL && isatty(fd)) - rp->flag |= RED_NOBUF; - /* Move rp to the head of the list. */ - if (red_head != rp) { - if ((rp->prev->next = rp->next) != NULL) - rp->next->prev = rp->prev; - red_head->prev = rp; - rp->prev = NULL; - rp->next = red_head; - red_head = rp; - } - } - } - if (rp->fp == NULL && rp->iop == NULL) { - /* too many files open -- close one and try again */ - if (errno == EMFILE || errno == ENFILE) - close_one(); -#if defined __MINGW32__ || defined solaris - else if (errno == 0) /* HACK! */ - close_one(); -#endif -#ifdef VMS - /* Alpha/VMS V7.1's C RTL is returning this instead - of EMFILE (haven't tried other post-V6.2 systems) */ -#define SS$_EXQUOTA 0x001C - else if (errno == EIO && vaxc$errno == SS$_EXQUOTA) - close_one(); -#endif - 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. - */ - if (errflg != NULL) - *errflg = errno; - if (tree->type == Node_redirect_output - || tree->type == Node_redirect_append) { - /* multiple messages make life easier for translators */ - if (*direction == 'f') - fatal(_("can't redirect from `%s' (%s)"), - str, strerror(errno)); - else - fatal(_("can't redirect to `%s' (%s)"), - str, strerror(errno)); - } else { - free_temp(tmp); - return NULL; - } - } - } - } - free_temp(tmp); - return rp; -} - -/* getredirect --- find the struct redirect for this file or pipe */ - -struct redirect * -getredirect(char *str, int len) -{ - struct redirect *rp; - - for (rp = red_head; rp != NULL; rp = rp->next) - if (strlen(rp->value) == len && STREQN(rp->value, str, len)) - return rp; - - return NULL; -} - -/* close_one --- temporarily close an open file to re-use the fd */ - -static void -close_one() -{ - register struct redirect *rp; - register struct redirect *rplast = NULL; - - static short warned = FALSE; - - if (do_lint && ! warned) { - warned = TRUE; - lintwarn(_("reached system limit for open files: starting to multiplex file descriptors")); - } - - /* 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 != NULL && (rp->flag & RED_FILE) != 0) { - rp->flag |= RED_USED; - errno = 0; - if (/* do_lint && */ fclose(rp->fp) != 0) - 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")); -} - -/* do_close --- completely close an open file or pipe */ - -NODE * -do_close(NODE *tree) -{ - NODE *tmp, *tmp2; - register struct redirect *rp; - two_way_close_type how = CLOSE_ALL; /* default */ - - tmp = force_string(tree_eval(tree->lnode)); /* 1st arg: redir to close */ - - if (tree->rnode != NULL) { - /* 2nd arg if present: "to" or "from" for two-way pipe */ - /* DO NOT use _() on the strings here! */ - tmp2 = force_string(tree->rnode->lnode); - if (strcasecmp(tmp2->stptr, "to") == 0) - how = CLOSE_TO; - else if (strcasecmp(tmp2->stptr, "from") == 0) - how = CLOSE_FROM; - else - fatal(_("close: second argument must be `to' or `from'")); - free_temp(tmp2); - } - - for (rp = red_head; rp != NULL; rp = rp->next) { - if (strlen(rp->value) == tmp->stlen - && STREQN(rp->value, tmp->stptr, tmp->stlen)) - break; - } - - if (rp == NULL) { /* no match, return -1 */ - char *cp; - - if (do_lint) - lintwarn(_("close: `%.*s' is not an open file, pipe or co-process"), - tmp->stlen, tmp->stptr); - - /* update ERRNO manually, using errno = ENOENT is a stretch. */ - cp = _("close of redirection that was never opened"); - unref(ERRNO_node->var_value); - ERRNO_node->var_value = make_string(cp, strlen(cp)); - - free_temp(tmp); - return tmp_number((AWKNUM) -1.0); - } - free_temp(tmp); - fflush(stdout); /* synchronize regular output */ - tmp = tmp_number((AWKNUM) close_redir(rp, FALSE, how)); - rp = NULL; - return tmp; -} - -/* close_redir --- close an open file or pipe */ - -static int -close_redir(register struct redirect *rp, int exitwarn, two_way_close_type how) -{ - int status = 0; - - if (rp == NULL) - return 0; - if (rp->fp == stdout || rp->fp == stderr) - return 0; - - if (do_lint && (rp->flag & RED_TWOWAY) == 0 && how != CLOSE_ALL) - lintwarn(_("close: redirection `%s' not opened with `|&', second argument ignored"), - rp->value); - - errno = 0; - if ((rp->flag & RED_TWOWAY) != 0) { /* two-way pipe */ - /* write end: */ - if ((how == CLOSE_ALL || how == CLOSE_TO) && rp->fp != NULL) { -#ifdef HAVE_SOCKETS - if ((rp->flag & RED_TCP) != 0) - (void) shutdown(fileno(rp->fp), SHUT_WR); -#endif /* HAVE_SOCKETS */ - status = fclose(rp->fp); - rp->fp = NULL; - } - - /* read end: */ - if (how == CLOSE_ALL || how == CLOSE_FROM) { - if ((rp->flag & RED_SOCKET) != 0 && rp->iop != NULL) { -#ifdef HAVE_SOCKETS - if ((rp->flag & RED_TCP) != 0) - (void) shutdown(rp->iop->fd, SHUT_RD); -#endif /* HAVE_SOCKETS */ - (void) iop_close(rp->iop); - } else - status = gawk_pclose(rp); - - rp->iop = NULL; - } - } else if ((rp->flag & (RED_PIPE|RED_WRITE)) == (RED_PIPE|RED_WRITE)) { /* write to pipe */ - status = pclose(rp->fp); - if ((BINMODE & 1) != 0) - os_setbinmode(fileno(stdin), O_BINARY); - - rp->fp = NULL; - } else if (rp->fp != NULL) { /* write to file */ - status = fclose(rp->fp); - rp->fp = NULL; - } else if (rp->iop != NULL) { /* read from pipe/file */ - if ((rp->flag & RED_PIPE) != 0) /* read from pipe */ - status = gawk_pclose(rp); - /* gawk_pclose sets rp->iop to null */ - else { /* read from file */ - status = iop_close(rp->iop); - rp->iop = NULL; - } - } - - /* SVR4 awk checks and warns about status of close */ - if (status != 0) { - char *s = strerror(errno); - - /* - * Too many people have complained about this. - * As of 2.15.6, it is now under lint control. - */ - if (do_lint) { - if ((rp->flag & RED_PIPE) != 0) - lintwarn(_("failure status (%d) on pipe close of `%s' (%s)"), - status, rp->value, s); - else - lintwarn(_("failure status (%d) on file close of `%s' (%s)"), - status, rp->value, s); - } - - if (! do_traditional) { - /* set ERRNO too so that program can get at it */ - update_ERRNO(); - } - } - - if (exitwarn) { - /* - * Don't use lintwarn() here. If lint warnings are fatal, - * doing so prevents us from closing other open redirections. - * - * Using multiple full messages instead of string parameters - * for the types makes message translation easier. - */ - if ((rp->flag & RED_SOCKET) != 0) - warning(_("no explicit close of socket `%s' provided"), - rp->value); - else if ((rp->flag & RED_TWOWAY) != 0) - warning(_("no explicit close of co-process `%s' provided"), - rp->value); - else if ((rp->flag & RED_PIPE) != 0) - warning(_("no explicit close of pipe `%s' provided"), - rp->value); - else - warning(_("no explicit close of file `%s' provided"), - rp->value); - } - - /* remove it from the list if closing both or both ends have been closed */ - if (how == CLOSE_ALL || (rp->iop == NULL && rp->fp == NULL)) { - if (rp->next != NULL) - rp->next->prev = rp->prev; - if (rp->prev != NULL) - rp->prev->next = rp->next; - else - red_head = rp->next; - free(rp->value); - free((char *) rp); - } - - return status; -} - -/* flush_io --- flush all open output files */ - -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)) { - if (rp->flag & RED_PIPE) - warning(_("pipe flush of `%s' failed (%s)."), - rp->value, strerror(errno)); - else if (rp->flag & RED_TWOWAY) - warning(_("co-process flush of pipe to `%s' failed (%s)."), - rp->value, strerror(errno)); - else - warning(_("file flush of `%s' failed (%s)."), - rp->value, strerror(errno)); - status++; - } - } - if (status != 0) - status = -1; /* canonicalize it */ - return status; -} - -/* close_io --- close all open files, called when exiting */ - -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, CLOSE_ALL)) - 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(const char *mode) -{ - int ret; - const char *second = & mode[1]; - - if (*second == 'b') - second++; - - switch(mode[0]) { - case 'r': - ret = O_RDONLY; - if (*second == '+' || *second == 'w') - ret = O_RDWR; - break; - - case 'w': - ret = O_WRONLY|O_CREAT|O_TRUNC; - if (*second == '+' || *second == 'r') - ret = O_RDWR|O_CREAT|O_TRUNC; - break; - - case 'a': - ret = O_WRONLY|O_APPEND|O_CREAT; - if (*second == '+') - ret = O_RDWR|O_APPEND|O_CREAT; - break; - - default: - ret = 0; /* lint */ - cant_happen(); - } - if (strchr(mode, 'b') != NULL) - ret |= O_BINARY; - return ret; -} - -#ifdef HAVE_SOCKETS -/* socketopen --- open a socket and set it into connected state */ - -int -socketopen(enum inet_prot type, int localport, int remoteport, char *remotehostname) -{ - struct hostent *hp = gethostbyname(remotehostname); - struct sockaddr_in local_addr, remote_addr; - int socket_fd; - int any_remote_host = strcmp(remotehostname, "0"); - - socket_fd = INVALID_HANDLE; - switch (type) { - case INET_TCP: - if (localport != 0 || remoteport != 0) { - int on = 1; -#ifdef SO_LINGER - struct linger linger; - - memset(& linger, '\0', sizeof(linger)); -#endif - socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, - (char *) & on, sizeof(on)); -#ifdef SO_LINGER - linger.l_onoff = 1; - linger.l_linger = 30; /* linger for 30/100 second */ - setsockopt(socket_fd, SOL_SOCKET, SO_LINGER, - (char *) & linger, sizeof(linger)); -#endif - } - break; - case INET_UDP: - if (localport != 0 || remoteport != 0) - socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - break; - case INET_RAW: -#ifdef SOCK_RAW - if (localport == 0 && remoteport == 0) - socket_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); -#endif - break; - case INET_NONE: - /* fall through */ - default: - cant_happen(); - break; - } - - if (socket_fd < 0 || socket_fd == INVALID_HANDLE - || (hp == NULL && any_remote_host != 0)) - return INVALID_HANDLE; - - local_addr.sin_family = remote_addr.sin_family = AF_INET; - local_addr.sin_addr.s_addr = htonl(INADDR_ANY); - remote_addr.sin_addr.s_addr = htonl(INADDR_ANY); - local_addr.sin_port = htons(localport); - remote_addr.sin_port = htons(remoteport); - if (bind(socket_fd, (struct sockaddr *) &local_addr, sizeof(local_addr)) == 0) { - if (any_remote_host != 0) { /* not ANY => create a client */ - if (type == INET_TCP || type == INET_UDP) { - memcpy(&remote_addr.sin_addr, hp->h_addr, - sizeof(remote_addr.sin_addr)); - if (connect(socket_fd, - (struct sockaddr *) &remote_addr, - sizeof(remote_addr)) != 0) { - close(socket_fd); - if (localport == 0) - socket_fd = INVALID_HANDLE; - else - socket_fd = socketopen(type, localport, 0, "0"); - } - } else { - /* /inet/raw client not ready yet */ - fatal(_("/inet/raw client not ready yet, sorry")); - if (geteuid() != 0) - fatal(_("only root may use `/inet/raw'.")); - } - } else { /* remote host is ANY => create a server */ - if (type == INET_TCP) { - int clientsocket_fd = INVALID_HANDLE; - int namelen = sizeof(remote_addr); - - if (listen(socket_fd, 1) >= 0 - && (clientsocket_fd = accept(socket_fd, - (struct sockaddr *) &remote_addr, - &namelen)) >= 0) { - close(socket_fd); - socket_fd = clientsocket_fd; - } else { - close(socket_fd); - socket_fd = INVALID_HANDLE; - } - } else if (type == INET_UDP) { - char buf[10]; - int readle; - -#ifdef MSG_PEEK - if (recvfrom(socket_fd, buf, 1, MSG_PEEK, - (struct sockaddr *) & remote_addr, - & readle) < 1 - || readle != sizeof(remote_addr) - || connect(socket_fd, - (struct sockaddr *)& remote_addr, - readle) != 0) { - close(socket_fd); - socket_fd = INVALID_HANDLE; - } -#endif - } else { - /* /inet/raw server not ready yet */ - fatal(_("/inet/raw server not ready yet, sorry")); - if (geteuid() != 0) - fatal(_("only root may use `/inet/raw'.")); - } - } - } else { - close(socket_fd); - socket_fd = INVALID_HANDLE; - } - - return socket_fd; -} -#endif /* HAVE_SOCKETS */ - -/* 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. - */ - -/* - * Strictly speaking, "name" is not a "const char *" because we temporarily - * change the string. - */ - -int -devopen(const char *name, const char *mode) -{ - int openfd; - char *cp; - char *ptr; - int flag = 0; - extern double strtod(); - - flag = str2mode(mode); - - if (STREQ(name, "-")) - return fileno(stdin); - - openfd = INVALID_HANDLE; - - if (do_traditional) - goto strictopen; - - if ((openfd = os_devopen(name, flag)) != INVALID_HANDLE) { - os_close_on_exec(openfd, name, "file", ""); - return openfd; - } - - if (STREQN(name, "/dev/", 5)) { - cp = (char *) name + 5; - - if (STREQ(cp, "stdin") && (flag & O_ACCMODE) == O_RDONLY) - openfd = fileno(stdin); - else if (STREQ(cp, "stdout") && (flag & O_ACCMODE) == O_WRONLY) - openfd = fileno(stdout); - else if (STREQ(cp, "stderr") && (flag & O_ACCMODE) == 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; - } - /* do not set close-on-exec for inherited fd's */ - if (openfd != INVALID_HANDLE) - return openfd; - } else if (STREQN(name, "/inet/", 6)) { -#ifdef HAVE_SOCKETS - /* /inet/protocol/localport/hostname/remoteport */ - enum inet_prot protocol = INET_NONE; - int localport, remoteport; - char *hostname; - char *hostnameslastcharp; - char *localpname; - char proto[4]; - struct servent *service; - - cp = (char *) name + 6; - /* which protocol? */ - if (STREQN(cp, "tcp/", 4)) - protocol = INET_TCP; - else if (STREQN(cp, "udp/", 4)) - protocol = INET_UDP; - else if (STREQN(cp, "raw/", 4)) - protocol = INET_RAW; - else - fatal(_("no (known) protocol supplied in special filename `%s'"), - name); - - proto[0] = cp[0]; - proto[1] = cp[1]; - proto[2] = cp[2]; - proto[3] = '\0'; - cp += 4; - - /* which localport? */ - localpname = cp; - while (*cp != '/' && *cp != '\0') - cp++; - /* - * Require a port, let them explicitly put 0 if - * they don't care. - */ - if (*cp != '/' || cp == localpname) - fatal(_("special file name `%s' is incomplete"), name); - /* We change the special file name temporarily because we - * need a 0-terminated string here for conversion with atoi(). - * By using atoi() the use of decimal numbers is enforced. - */ - *cp = '\0'; - - localport = atoi(localpname); - if (strcmp(localpname, "0") != 0 - && (localport <= 0 || localport > 65535)) { - service = getservbyname(localpname, proto); - if (service == NULL) - fatal(_("local port invalid in `%s'"), name); - else - localport = ntohs(service->s_port); - } - *cp = '/'; - - /* which hostname? */ - cp++; - hostname = cp; - while (*cp != '/' && *cp != '\0') - cp++; - if (*cp != '/' || cp == hostname) - fatal(_("must supply a remote hostname to `/inet'")); - *cp = '\0'; - hostnameslastcharp = cp; - - /* which remoteport? */ - cp++; - /* - * The remote port ends the special file name. - * This means there already is a 0 at the end of the string. - * Therefore no need to patch any string ending. - * - * Here too, require a port, let them explicitly put 0 if - * they don't care. - */ - if (*cp == '\0') - fatal(_("must supply a remote port to `/inet'")); - remoteport = atoi(cp); - if (strcmp(cp, "0") != 0 - && (remoteport <= 0 || remoteport > 65535)) { - service = getservbyname(cp, proto); - if (service == NULL) - fatal(_("remote port invalid in `%s'"), name); - else - remoteport = ntohs(service->s_port); - } - - /* Open Sesame! */ - openfd = socketopen(protocol, localport, remoteport, hostname); - *hostnameslastcharp = '/'; - -#else /* ! HAVE_SOCKETS */ - fatal(_("TCP/IP communications are not supported")); -#endif /* HAVE_SOCKETS */ - } - -strictopen: - if (openfd == INVALID_HANDLE) - openfd = open(name, flag, 0666); - if (openfd != INVALID_HANDLE) { - if (os_isdir(openfd)) - fatal(_("file `%s' is a directory"), name); - - os_close_on_exec(openfd, name, "file", ""); - } - return openfd; -} - - -/* spec_setup --- setup an IOBUF for a special internal file */ - -static void -spec_setup(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 an fd special file */ - -static int -specfdopen(IOBUF *iop, const char *name, const char *mode) -{ - int fd; - IOBUF *tp; - - fd = devopen(name, mode); - if (fd == INVALID_HANDLE) - return INVALID_HANDLE; - tp = iop_alloc(fd, name, NULL); - if (tp == NULL) { - /* don't leak fd's */ - close(fd); - return INVALID_HANDLE; - } - *iop = *tp; - iop->flag |= IOP_NO_FREE; - free(tp); - return 0; -} - -#ifdef GETPGRP_VOID -#define getpgrp_arg() /* nothing */ -#else -#define getpgrp_arg() getpid() -#endif - -/* pidopen --- "open" /dev/pid, /dev/ppid, and /dev/pgrpid */ - -static int -pidopen(IOBUF *iop, const char *name, const char *mode) -{ - char tbuf[BUFSIZ]; - int i; - const char *cp = name + 5; - - warning(_("use `PROCINFO[\"%s\"]' instead of `%s'"), cp, name); - - if (name[6] == 'g') - sprintf(tbuf, "%d\n", (int) getpgrp(getpgrp_arg())); - else if (name[6] == 'i') - sprintf(tbuf, "%d\n", (int) getpid()); - else - sprintf(tbuf, "%d\n", (int) getppid()); - i = strlen(tbuf); - spec_setup(iop, i, TRUE); - 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, then $5 through $NF are the - * supplementary group set. - */ - -static int -useropen(IOBUF *iop, const char *name, const char *mode) -{ - char tbuf[BUFSIZ], *cp; - int i; -#if defined(NGROUPS_MAX) && NGROUPS_MAX > 0 - GETGROUPS_T groupset[NGROUPS_MAX]; - int ngroups; -#endif - - warning(_("use `PROCINFO[...]' instead of `/dev/user'")); - - sprintf(tbuf, "%d %d %d %d", (int) getuid(), (int) geteuid(), (int) getgid(), (int) 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, TRUE); - strcpy(iop->buf, tbuf); - return 0; -} - -/* iop_open --- handle special and regular files for input */ - -static IOBUF * -iop_open(const char *name, const char *mode, IOBUF *iop) -{ - int openfd = INVALID_HANDLE; - int flag = 0; - 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 }, - { "/inet/", 6, 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 (STREQ(name, "-")) - openfd = fileno(stdin); - else if (do_traditional) - goto strictopen; - else if (STREQN(name, "/dev/", 5) || STREQN(name, "/inet/", 6)) { - 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, FALSE); - 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; - } - } - } - /* not in table, fall through to regular code */ - } - -strictopen: - if (openfd == INVALID_HANDLE) - openfd = open(name, flag, 0666); - if (openfd != INVALID_HANDLE) { - if (os_isdir(openfd)) - fatal(_("file `%s' is a directory"), name); - - os_close_on_exec(openfd, name, "file", ""); - } - return iop_alloc(openfd, name, iop); -} - -/* two_way_open --- open a two way communications channel */ - -static int -two_way_open(char *str, struct redirect *rp) -{ -#ifdef HAVE_SOCKETS - /* case 1: socket */ - if (STREQN(str, "/inet/", 6)) { - int fd, newfd; - - fd = devopen(str, "rw"); - if (fd == INVALID_HANDLE) - return FALSE; - rp->fp = fdopen(fd, "w"); - if (rp->fp == NULL) { - close(fd); - return FALSE; - } - newfd = dup(fd); - if (newfd < 0) { - fclose(rp->fp); - return FALSE; - } - os_close_on_exec(newfd, str, "socket", "to/from"); - rp->iop = iop_alloc(newfd, str, NULL); - if (rp->iop == NULL) { - fclose(rp->fp); - return FALSE; - } - rp->flag |= RED_SOCKET; - return TRUE; - } -#endif /* HAVE_SOCKETS */ - -#ifdef HAVE_PORTALS - /* case 1.5: portal */ - if (STREQN(str, "/p/", 3)) { - int fd, newfd; - - fd = open(str, O_RDWR); - if (fd == INVALID_HANDLE) - return FALSE; - rp->fp = fdopen(fd, "w"); - if (rp->fp == NULL) { - close(fd); - return FALSE; - } - newfd = dup(fd); - if (newfd < 0) { - fclose(rp->fp); - return FALSE; - } - os_close_on_exec(newfd, str, "portal", "to/from"); - rp->iop = iop_alloc(newfd, str, NULL); - if (rp->iop == NULL) { - fclose(rp->fp); - return FALSE; - } - rp->flag |= RED_SOCKET; - return TRUE; - } -#endif /* HAVE_PORTALS */ - -#ifndef PIPES_SIMULATED /* real pipes */ - /* case 2: two way pipe to a child process */ - { - int ptoc[2], ctop[2]; - int pid; - int save_errno; - - if (pipe(ptoc) < 0) - return FALSE; /* errno set, diagnostic from caller */ - - if (pipe(ctop) < 0) { - save_errno = errno; - close(ptoc[0]); - close(ptoc[1]); - errno = save_errno; - return FALSE; - } - - if ((pid = fork()) < 0) { - save_errno = errno; - close(ptoc[0]); close(ptoc[1]); - close(ctop[0]); close(ctop[1]); - errno = save_errno; - return FALSE; - } - - if (pid == 0) { /* child */ - if (close(1) == -1) - fatal(_("close of stdout in child failed (%s)"), - strerror(errno)); - if (dup(ctop[1]) != 1) - fatal(_("moving pipe to stdout in child failed (dup: %s)"), strerror(errno)); - if (close(0) == -1) - fatal(_("close of stdin in child failed (%s)"), - strerror(errno)); - if (dup(ptoc[0]) != 0) - fatal(_("moving pipe to stdin in child failed (dup: %s)"), strerror(errno)); - if ( close(ptoc[0]) == -1 || close(ptoc[1]) == -1 - || close(ctop[0]) == -1 || close(ctop[1]) == -1) - fatal(_("close of pipe failed (%s)"), strerror(errno)); - /* stderr does NOT get dup'ed onto child's stdout */ - execl("/bin/sh", "sh", "-c", str, NULL); - _exit(127); - } - - /* parent */ - rp->pid = pid; - rp->iop = iop_alloc(ctop[0], str, NULL); - if (rp->iop == NULL) { - (void) close(ctop[0]); - (void) close(ctop[1]); - (void) close(ptoc[0]); - (void) close(ptoc[1]); - (void) kill(pid, SIGKILL); /* overkill? (pardon pun) */ - return FALSE; - } - rp->fp = fdopen(ptoc[1], "w"); - if (rp->fp == NULL) { - iop_close(rp->iop); - rp->iop = NULL; - (void) close(ctop[0]); - (void) close(ctop[1]); - (void) close(ptoc[0]); - (void) close(ptoc[1]); - (void) kill(pid, SIGKILL); /* overkill? (pardon pun) */ - return FALSE; - } - if (fcntl(ctop[0], F_SETFD, 1) < 0) { - warning(_("pipe from `%s': could not set close-on-exec (fcntl: %s)"), - str, strerror(errno));; - } - if (fcntl(ptoc[1], F_SETFD, 1) < 0) { - warning(_("pipe to `%s': could not set close-on-exec (fcntl: %s)"), - str, strerror(errno));; - } - (void) close(ptoc[0]); - (void) close(ctop[1]); - return TRUE; - } - -#else /*PIPES_SIMULATED*/ - - fatal(_("`|&' not supported")); - /*NOTREACHED*/ - return FALSE; - -#endif -} - -#ifndef PIPES_SIMULATED /* real pipes */ - -/* wait_any --- wait for a child process, close associated pipe */ - -static int -wait_any(int interesting) /* pid of interest, if any */ -{ - RETSIGTYPE (*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 HAVE_SYS_WAIT_H /* Posix compatible sys/wait.h */ - pid = wait(&status); -#else - pid = wait((union 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; - break; - } - } - if (pid == -1 && errno == ECHILD) - break; - } - signal(SIGHUP, hstat); - signal(SIGINT, istat); - signal(SIGQUIT, qstat); - return(status); -} - -/* gawk_popen --- open an IOBUF on a child process */ - -static IOBUF * -gawk_popen(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(_("moving pipe to stdout in child failed (dup: %s)"), strerror(errno)); - if (close(p[0]) == -1 || close(p[1]) == -1) - fatal(_("close of pipe failed (%s)"), strerror(errno)); - execl("/bin/sh", "sh", "-c", cmd, NULL); - _exit(127); - } - if (pid == -1) - fatal(_("cannot create child process for `%s' (fork: %s)"), cmd, strerror(errno)); - rp->pid = pid; - if (close(p[1]) == -1) - fatal(_("close of pipe failed (%s)"), strerror(errno)); - os_close_on_exec(p[0], cmd, "pipe", "from"); - rp->iop = iop_alloc(p[0], cmd, NULL); - if (rp->iop == NULL) - (void) close(p[0]); - return (rp->iop); -} - -/* gawk_pclose --- close an open child pipe */ - -static int -gawk_pclose(struct redirect *rp) -{ - if (rp->iop != NULL) - (void) iop_close(rp->iop); - rp->iop = NULL; - - /* process previously found, return stored status */ - if (rp->pid == -1) - return (rp->status >> 8) + ((rp->status &0xFF) ? 128 + (rp->status & 0xF) : 0); - rp->status = wait_any(rp->pid); - rp->pid = -1; - return (rp->status >> 8) + ((rp->status &0xFF) ? 128 + (rp->status & 0xF) : 0); -} - -#else /* PIPES_SIMULATED */ - -/* - * use temporary file rather than pipe - * except if popen() provides real pipes too - */ - -#if defined(VMS) || defined(OS2) || defined (MSDOS) || defined(WIN32) || defined(TANDEM) - -/* gawk_popen --- open an IOBUF on a child process */ - -static IOBUF * -gawk_popen(char *cmd, struct redirect *rp) -{ - FILE *current; - - os_restore_mode(fileno(stdin)); - current = popen(cmd, binmode("r")); - if ((BINMODE & 1) != 0) - os_setbinmode(fileno(stdin), O_BINARY); - if (current == NULL) - return NULL; - os_close_on_exec(fileno(current), cmd, "pipe", "from"); - rp->iop = iop_alloc(fileno(current), cmd, NULL); - if (rp->iop == NULL) { - (void) pclose(current); - current = NULL; - } - rp->ifp = current; - return (rp->iop); -} - -/* gawk_pclose --- close an open child pipe */ - -static int -gawk_pclose(struct redirect *rp) -{ - int rval, aval, fd = rp->iop->fd; - - if (rp->iop != NULL) { - rp->iop->fd = dup(fd); /* kludge to allow close() + pclose() */ - rval = iop_close(rp->iop); - } - rp->iop = NULL; - aval = pclose(rp->ifp); - rp->ifp = NULL; - return (rval < 0 ? rval : aval); -} -#else /* not (VMS || OS2 || MSDOS || TANDEM) */ - -static struct pipeinfo { - char *command; - char *name; -} pipes[_NFILE]; - -/* gawk_popen --- open an IOBUF on a child process */ - -static IOBUF * -gawk_popen(char *cmd, struct redirect *rp) -{ - extern char *strdup P((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); - os_close_on_exec(current, cmd, "pipe", "from"); - rp->iop = iop_alloc(current, name, NULL); - if (rp->iop == NULL) - (void) close(current); - return (rp->iop); -} - -/* gawk_pclose --- close an open child pipe */ - -static int -gawk_pclose(struct redirect *rp) -{ - int cur = rp->iop->fd; - int rval = 0; - - if (rp->iop != NULL) - 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 /* not (VMS || OS2 || MSDOS || TANDEM) */ - -#endif /* PIPES_SIMULATED */ - -/* do_getline --- read in a line, into var and with redirection, as needed */ - -NODE * -do_getline(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(FALSE); - 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_traditional) - update_ERRNO(); - - 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->stptr[0], RS_regexp, &errcode); - if (errcode != 0) { - if (! do_traditional) - update_ERRNO(); - - return tmp_number((AWKNUM) -1.0); - } - if (cnt == EOF) { - if (rp != NULL) { - /* - * 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|RED_TWOWAY)) == 0) { - (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 == NULL) { - NR++; - FNR++; - } - if (tree->lnode == NULL) /* no optional var. */ - set_record(s, cnt, TRUE); - else { /* assignment to variable */ - Func_ptr after_assign = NULL; - NODE **lhs; - - lhs = get_lhs(tree->lnode, &after_assign, FALSE); - unref(*lhs); - *lhs = make_string(s, cnt); - (*lhs)->flags |= MAYBE_NUM; - /* we may have to regenerate $0 here! */ - if (after_assign != NULL) - (*after_assign)(); - } - } - return tmp_number((AWKNUM) 1.0); -} - -/* pathopen --- pathopen with default file extension handling */ - -int -pathopen(const char *file) -{ - int fd = do_pathopen(file); - -#ifdef DEFAULT_FILETYPE - if (! do_traditional && 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; -} - -/* do_pathopen --- search $AWKPATH for source file */ - -static int -do_pathopen(const char *file) -{ - static const char *savepath = NULL; - static int first = TRUE; - const char *awkpath; - char *cp, trypath[BUFSIZ]; - int fd; - - if (STREQ(file, "-")) - return (0); - - if (do_traditional) - return (devopen(file, "r")); - - if (first) { - first = FALSE; - if ((awkpath = getenv("AWKPATH")) != NULL && *awkpath) - savepath = awkpath; /* used for restarting */ - else - savepath = defpath; - } - awkpath = savepath; - - /* some kind of path name, no search */ - if (ispath(file)) - 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 */ - if (! isdirpunct(*(cp-1))) - *cp++ = '/'; - /* append filename */ - strcpy(cp, file); - } else - strcpy(trypath, file); - if ((fd = devopen(trypath, "r")) > INVALID_HANDLE) - return (fd); - - /* no luck, keep going */ - if(*awkpath == envsep && awkpath[1] != '\0') - awkpath++; /* skip colon */ - } while (*awkpath != '\0'); - /* - * 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")); -} - -#ifdef TEST -int bufsize = 8192; - -void -fatal(char *s) -{ - printf("%s\n", s); - exit(1); -} -#endif - -/* iop_alloc --- allocate an IOBUF structure for an open fd */ - -static IOBUF * -iop_alloc(int fd, const char *name, IOBUF *iop) -{ - struct stat sbuf; - - if (fd == INVALID_HANDLE) - return NULL; - if (iop == NULL) - emalloc(iop, IOBUF *, sizeof(IOBUF), "iop_alloc"); - iop->flag = 0; - if (isatty(fd)) - iop->flag |= IOP_IS_TTY; - iop->size = optimal_bufsize(fd, & sbuf); - if (do_lint && S_ISREG(sbuf.st_mode) && sbuf.st_size == 0) - lintwarn(_("data file `%s' is empty"), name); - iop->secsiz = -2; - errno = 0; - iop->fd = fd; - iop->off = iop->buf = NULL; - iop->cnt = 0; - iop->name = name; - return iop; -} - -#define set_RT_to_null() \ - (void)(! do_traditional && (unref(RT_node->var_value), \ - RT_node->var_value = Nnull_string)) - -#define set_RT(str, len) \ - (void)(! do_traditional && (unref(RT_node->var_value), \ - RT_node->var_value = make_string(str, len))) - -/* - * get_a_record: - * Get the next record. Uses a "split buffer" where the latter part is - * the normal read buffer and the head part is an "overflow" area that is used - * when a record spans the end of the normal buffer, in which case the first - * part of the record is copied into the overflow area just before the - * normal buffer. Thus, the eventual full record can be returned as a - * contiguous area of memory with a minimum of copying. The overflow area - * is expanded as needed, so that records are unlimited in length. - * We also mark both the end of the buffer and the end of the read() with - * a sentinel character (the current record separator) so that the inside - * loop can run as a single test. - * - * Note that since we know or can compute the end of the read and the end - * of the buffer, the sentinel character does not get in the way of regexp - * based searching, since we simply search up to that character, but not - * including it. - */ - -static int -get_a_record(char **out, /* pointer to pointer to data */ - IOBUF *iop, /* input IOP */ - register int grRS, /* first char in RS->stptr */ - Regexp *RSre, /* regexp for RS */ - int *errcode) /* pointer to error variable */ -{ - register char *bp = iop->off; - char *bufend; - char *start = iop->off; /* beginning of record */ - int rs; - static Regexp *RS_null_re = NULL; - Regexp *rsre = NULL; - int continuing = FALSE, continued = FALSE; /* used for re matching */ - int onecase; - -#ifdef TANDEM - char *mend; -#endif - -#ifdef TANDEM -#define not_past_end() (bp < mend) -#else -#define not_past_end() (1) -#endif - - /* first time through */ - if (RS_null_re == NULL) { - RS_null_re = make_regexp("\n\n+", 3, TRUE, TRUE); - if (RS_null_re == NULL) - fatal(_("internal error: file `%s', line %d\n"), - __FILE__, __LINE__); - } - - if (iop->cnt == EOF) { /* previous read hit EOF */ - *out = NULL; - set_RT_to_null(); - return EOF; - } - -#ifdef TANDEM - if (MRL) - mend = start + MRL; - else - mend = (char *) LONG_MAX; -#endif - - if (RS_is_null) /* special case: RS == "" */ - rs = '\n'; - else - rs = (char) grRS; - - onecase = (IGNORECASE && ISALPHA(rs)); - if (onecase) - rs = casetable[(unsigned char) rs]; - - /* set up sentinel */ - if (iop->buf) { - bufend = iop->buf + iop->size + iop->secsiz; - *bufend = rs; /* add sentinel to buffer */ - } else - bufend = NULL; - - for (;;) { /* break on end of record, read error or EOF */ -/* buffer mgmt, chunk #1 */ - /* - * Following code is entered on the first call of this routine - * for a new iop, or when we scan to the end of the buffer. - * In the latter case, we copy the current partial record to - * the space preceding the normal read buffer. If necessary, - * we expand this space. This is done so that we can return - * the record as a contiguous area of memory. - */ - if ((iop->flag & IOP_IS_INTERNAL) == 0 && bp >= bufend) { - char *oldbuf = NULL; - char *oldsplit = iop->buf + iop->secsiz; - long len; /* record length so far */ - - len = bp - start; - if (len > iop->secsiz) { - /* expand secondary buffer */ - if (iop->secsiz == -2) - iop->secsiz = 256; - while (len > iop->secsiz) - iop->secsiz *= 2; - oldbuf = iop->buf; - emalloc(iop->buf, char *, - iop->size+iop->secsiz+2, "get_a_record"); - bufend = iop->buf + iop->size + iop->secsiz; - *bufend = rs; - } - if (len > 0) { - char *newsplit = iop->buf + iop->secsiz; - - if (start < oldsplit) { - memcpy(newsplit - len, start, - oldsplit - start); - memcpy(newsplit - (bp - oldsplit), - oldsplit, bp - oldsplit); - } else - memcpy(newsplit - len, start, len); - } - bp = iop->end = iop->off = iop->buf + iop->secsiz; - start = bp - len; -#ifdef TANDEM - if (MRL) - mend = start + MRL; -#endif - if (oldbuf != NULL) { - free(oldbuf); - oldbuf = NULL; - } - } -/* buffer mgmt, chunk #2 */ - /* - * Following code is entered whenever we have no more data to - * scan. In most cases this will read into the beginning of - * the main buffer, but in some cases (terminal, pipe etc.) - * we may be doing smallish reads into more advanced positions. - */ - if (bp >= iop->end) { - if ((iop->flag & IOP_IS_INTERNAL) != 0) { - iop->cnt = EOF; - break; - } - iop->cnt = read(iop->fd, iop->end, bufend - iop->end); - if (iop->cnt == -1) { - if (! do_traditional && errcode != NULL) { - *errcode = errno; - iop->cnt = EOF; - break; - } else - fatal(_("error reading input file `%s': %s"), - iop->name, strerror(errno)); - } else if (iop->cnt == 0) { - /* - * hit EOF before matching RS, so end - * the record and set RT to "" - */ - iop->cnt = EOF; - /* see comments below about this test */ - if (! continuing) { - set_RT_to_null(); - break; - } - } - if (iop->cnt != EOF) { - iop->end += iop->cnt; - *iop->end = rs; /* reset the sentinel */ - } - } -/* buffers are now setup and filled with data */ -/* search for RS, #1, regexp based, or RS = "" */ - /* - * Attempt to simplify the code a bit. The case where - * RS = "" can also be described by a regexp, RS = "\n\n+". - * The buffer managment and searching code can thus now - * use a common case (the one for regexps) both when RS is - * a regexp, and when RS = "". This particularly benefits - * us for keeping track of how many newlines were matched - * in order to set RT. - */ - if (! do_traditional && RSre != NULL) /* regexp */ - rsre = RSre; - else if (RS_is_null) /* RS = "" */ - rsre = RS_null_re; - else - rsre = NULL; - - /* - * Look for regexp match of RS. Non-match conditions are: - * 1. No match at all - * 2. Match of a null string - * 3. Match ends at exact end of buffer - * Number 3 is subtle; we have to add more to the buffer - * in case the match would have extended further into the - * file, since regexp match by definition always matches the - * longest possible match. - * - * It is even more subtle than you might think. Suppose - * the re matches at exactly the end of file. We don't know - * that until we try to add more to the buffer. Thus, we - * set a flag to indicate, that if eof really does happen, - * don't break early. - * - * Still more subtlety. Suppose RS is a multi-character regexp, - * but it doesn't have the metacharacters that would let it - * match an arbitrary number of characters. So it's an exact - * string match. We need to check for this, in the case where - * there is an exact match at the end, and NOT read more - * data. Otherwise, this might bite us for an interactive - * networking program that uses CR-LF as the line terminator. - */ - continuing = FALSE; - if (rsre != NULL) { - again: - /* cases 1 and 2 are simple, just keep going */ - if (research(rsre, start, 0, iop->end - start, TRUE) == -1 - || RESTART(rsre, start) == REEND(rsre, start)) { - /* - * Leading newlines at the beginning of the file - * should be ignored. Whew! - */ - if (RS_is_null && *start == '\n') { - /* - * have to catch the case of a - * single newline at the front of - * the record, which the regex - * doesn't. gurr. - */ - while (*start == '\n' && start < iop->end) - start++; - goto again; - } - bp = iop->end; - continue; - } - /* case 3, regex match at exact end */ - if (start + REEND(rsre, start) >= iop->end) { - if (iop->cnt != EOF) { - /* - * Only do the test if not at EOF - */ - int isstring; - - isstring = reisstring(RS->stptr, - RS->stlen, rsre, start); - if (isstring == FALSE) { - bp = iop->end; - continuing = continued = TRUE; - continue; - } - } - } - /* got a match! */ - /* - * Leading newlines at the beginning of the file - * should be ignored. Whew! - */ - if (RS_is_null && *start == '\n') { - /* - * have to catch the case of a - * single newline at the front of - * the record, which the regex - * doesn't. gurr. - */ - while (*start == '\n' && start < iop->end) - start++; - goto again; - } - bp = start + RESTART(rsre, start); - set_RT(bp, REEND(rsre, start) - RESTART(rsre, start)); - *bp = '\0'; - iop->off = start + REEND(rsre, start); - break; - } -/* search for RS, #2, RS = <single char> */ - if (onecase) { - while (casetable[(unsigned char) *bp++] != rs && not_past_end()) - continue; - } else { - while (*bp++ != rs && not_past_end()) - continue; - } - set_RT(bp - 1, 1); - - if (bp <= iop->end) - break; - else - bp--; - - if ((iop->flag & IOP_IS_INTERNAL) != 0) - iop->cnt = bp - start; - } - if (iop->cnt == EOF - && (((iop->flag & IOP_IS_INTERNAL) != 0) - || (start == bp && ! continued))) { - *out = NULL; - set_RT_to_null(); - return EOF; - } - - if (do_traditional || rsre == NULL) { - iop->off = bp; - bp--; - if (onecase ? casetable[(unsigned char) *bp] != rs : *bp != rs) - bp++; - if (MRL == 0) - *bp = '\0'; - } else if (RS_is_null && iop->cnt == EOF) { - /* - * special case, delete trailing newlines, - * should never be more than one. - */ - while (bp[-1] == '\n') - bp--; - *bp = '\0'; - } - - *out = start; - return bp - start; -} - -#ifdef TEST -int -main(int argc, char *argv[]) -{ - IOBUF *iop; - char *out; - int cnt; - char rs[2]; - - rs[0] = '\0'; - if (argc > 1) - bufsize = atoi(argv[1]); - if (argc > 2) - rs[0] = *argv[2]; - iop = iop_alloc(0, "stdin", NULL); - while ((cnt = get_a_record(&out, iop, rs[0], NULL, NULL)) > 0) { - fwrite(out, 1, cnt, stdout); - fwrite(rs, 1, 1, stdout); - } - return 0; -} -#endif - -/* set_RS --- update things as appropriate when RS is set */ - -void -set_RS() -{ - static NODE *save_rs = NULL; - - if (save_rs && cmp_nodes(RS_node->var_value, save_rs) == 0) - return; - unref(save_rs); - save_rs = dupnode(RS_node->var_value); - RS_is_null = FALSE; - RS = force_string(RS_node->var_value); - if (RS_regexp != NULL) { - refree(RS_regexp); - RS_regexp = NULL; - } - if (RS->stlen == 0) - RS_is_null = TRUE; - else if (RS->stlen > 1) { - static int warned = FALSE; - - RS_regexp = make_regexp(RS->stptr, RS->stlen, IGNORECASE, TRUE); - - if (do_lint && ! warned) { - lintwarn(_("multicharacter value of `RS' is a gawk extension")); - warned = TRUE; - } - } - - set_FS_if_not_FIELDWIDTHS(); -} |