diff options
Diffstat (limited to 'contrib/awk/io.c')
-rw-r--r-- | contrib/awk/io.c | 1305 |
1 files changed, 446 insertions, 859 deletions
diff --git a/contrib/awk/io.c b/contrib/awk/io.c index 7589185..a83fc9d 100644 --- a/contrib/awk/io.c +++ b/contrib/awk/io.c @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 1976, 1988, 1989, 1991-2001 the Free Software Foundation, Inc. + * Copyright (C) 1976, 1988, 1989, 1991-2000 the Free Software Foundation, Inc. * * This file is part of GAWK, the GNU implementation of the * AWK Programming Language. @@ -21,9 +21,12 @@ * 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" +#undef HAVE_MMAP /* for now, probably forever */ #ifdef HAVE_SYS_PARAM_H #undef RE_DUP_MAX /* avoid spurious conflict w/regex.h */ @@ -34,6 +37,13 @@ #include <sys/wait.h> #endif /* HAVE_SYS_WAIT_H */ +#ifdef HAVE_MMAP +#include <sys/mman.h> +#ifndef MAP_FAILED +#define MAP_FAILED ((caddr_t) -1) +#endif /* ! defined (MAP_FAILED) */ +#endif /* HAVE_MMAP */ + #ifndef O_RDONLY #include <fcntl.h> #endif @@ -41,65 +51,32 @@ #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 +#if ! defined(S_ISDIR) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif -#ifndef SHUT_WR -#define SHUT_WR 1 -#endif - -#ifndef SHUT_RDWR -#define SHUT_RDWR 2 +#ifndef ENFILE +#define ENFILE EMFILE #endif -#endif /* HAVE_SOCKETS */ - #ifdef atarist #include <stddef.h> #endif -#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(TANDEM) +#if defined(MSDOS) || defined(OS2) || defined(WIN32) #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)); +static int close_redir P((struct redirect *rp, int exitwarn)); #ifndef PIPES_SIMULATED static int wait_any P((int interesting)); #endif @@ -109,14 +86,16 @@ 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)); +#ifdef HAVE_MMAP +static int mmap_get_record P((char **out, IOBUF *iop, int rs, Regexp *RSre, int *errcode)); +#endif /* HAVE_MMAP */ 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) +#if defined (HAVE_POPEN_H) #include "popen.h" #endif @@ -135,27 +114,6 @@ 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. */ @@ -177,7 +135,8 @@ do_nextfile() /* nextfile --- move to the next input data file */ static IOBUF * -nextfile(int skipping) +nextfile(skipping) +int skipping; { static long i = 1; static int files = 0; @@ -200,7 +159,7 @@ nextfile(int skipping) return curfile; } for (; i < (long) (ARGC_node->lnode->numbr); i++) { - arg = *assoc_lookup(ARGV_node, tmp_number((AWKNUM) i), FALSE); + arg = *assoc_lookup(ARGV_node, tmp_number((AWKNUM) i)); if (arg->stlen == 0) continue; arg->stptr[arg->stlen] = '\0'; @@ -211,7 +170,7 @@ nextfile(int skipping) if (! arg_assign(arg->stptr)) { files++; fname = arg->stptr; - curfile = iop_open(fname, binmode("r"), &mybuf); + curfile = iop_open(fname, "r", &mybuf); if (curfile == NULL) goto give_up; curfile->flag |= IOP_NOFREE_OBJ; @@ -229,7 +188,7 @@ nextfile(int skipping) /* FNR is init'ed to 0 */ FILENAME_node->var_value = make_string("-", 1); fname = "-"; - curfile = iop_open(fname, binmode("r"), &mybuf); + curfile = iop_open(fname, "r", &mybuf); if (curfile == NULL) goto give_up; curfile->flag |= IOP_NOFREE_OBJ; @@ -237,7 +196,7 @@ nextfile(int skipping) return curfile; give_up: - fatal(_("cannot open file `%s' for reading (%s)"), + fatal("cannot open file `%s' for reading (%s)", fname, strerror(errno)); /* NOTREACHED */ return 0; @@ -262,14 +221,16 @@ set_NR() /* inrec --- This reads in a record from the input file */ static int -inrec(IOBUF *iop) +inrec(iop) +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); + cnt = (*(iop->getrec)) + (&begin, iop, RS->stptr[0], RS_regexp, NULL); if (cnt == EOF) { cnt = 0; retval = 1; @@ -285,7 +246,8 @@ inrec(IOBUF *iop) /* iop_close --- close an open IOP */ static int -iop_close(IOBUF *iop) +iop_close(iop) +IOBUF *iop; { int ret; @@ -311,13 +273,14 @@ iop_close(IOBUF *iop) /* 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)) + || iop->fd == fileno(stderr) + || (iop->flag & IOP_MMAPPED) != 0) ret = 0; else ret = close(iop->fd); if (ret == -1) - warning(_("close of fd %d (`%s') failed (%s)"), iop->fd, + warning("close of fd %d (`%s') failed (%s)", iop->fd, iop->name, strerror(errno)); if ((iop->flag & IOP_NO_FREE) == 0) { /* @@ -336,7 +299,12 @@ iop_close(IOBUF *iop) fields_arr[0] = t; reset_record(); } - free(iop->buf); + if ((iop->flag & IOP_MMAPPED) == 0) + free(iop->buf); +#ifdef HAVE_MMAP + else + (void) munmap(iop->buf, iop->size); +#endif } if ((iop->flag & IOP_NOFREE_OBJ) == 0) free((char *) iop); @@ -358,37 +326,21 @@ do_input() if (inrec(iop) == 0) while (interpret(expression_value) && inrec(iop) == 0) continue; +#ifdef C_ALLOCA + /* recover any space from C based alloca */ + (void) alloca(0); +#endif 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) +redirect(tree, errflg) +NODE *tree; +int *errflg; { register NODE *tmp; register struct redirect *rp; @@ -424,57 +376,31 @@ redirect(NODE *tree, int *errflg) 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)); + fatal("invalid tree type %d in redirect()", tree->type); break; } tmp = tree_eval(tree->subnode); if (do_lint && (tmp->flags & STR) == 0) - lintwarn(_("expression in `%s' redirection only has numeric value"), + 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"), + 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) { + 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 != 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); - + && (rp->flag & (RED_FILE|RED_WRITE)) == outflag))) break; - } - } - if (rp == NULL) { emalloc(rp, struct redirect *, sizeof(struct redirect), "redirect"); @@ -495,7 +421,6 @@ redirect(NODE *tree, int *errflg) red_head = rp; } else str = rp->value; /* get \0 terminated string */ - while (rp->fp == NULL && rp->iop == NULL) { if (rp->flag & RED_EOF) /* @@ -507,48 +432,31 @@ redirect(NODE *tree, int *errflg) errno = 0; switch (tree->type) { case Node_redirect_output: - mode = binmode("w"); + mode = "w"; if ((rp->flag & RED_USED) != 0) - mode = (rp->mode[1] == 'b') ? "ab" : "a"; + mode = "a"; break; case Node_redirect_append: - mode = binmode("a"); + mode = "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)"), + if ((rp->fp = popen(str, "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)"), + 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)); - } + rp->iop = iop_open(str, "r", NULL); break; default: cant_happen(); @@ -564,16 +472,7 @@ redirect(NODE *tree, int *errflg) 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; + rp->fp = fdopen(fd, (char *) mode); /* don't leak file descriptors */ if (rp->fp == NULL) close(fd); @@ -595,7 +494,9 @@ redirect(NODE *tree, int *errflg) /* too many files open -- close one and try again */ if (errno == EMFILE || errno == ENFILE) close_one(); -#if defined __MINGW32__ || defined solaris +#if defined __MINGW32__ || defined HAVE_MMAP + /* this works for solaris 2.5, not sunos */ + /* it is also needed for MINGW32 */ else if (errno == 0) /* HACK! */ close_one(); #endif @@ -619,15 +520,10 @@ redirect(NODE *tree, int *errflg) 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 { + || tree->type == Node_redirect_append) + fatal("can't redirect %s `%s' (%s)", + direction, str, strerror(errno)); + else { free_temp(tmp); return NULL; } @@ -641,7 +537,9 @@ redirect(NODE *tree, int *errflg) /* getredirect --- find the struct redirect for this file or pipe */ struct redirect * -getredirect(char *str, int len) +getredirect(str, len) +char *str; +int len; { struct redirect *rp; @@ -660,13 +558,6 @@ 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; @@ -676,39 +567,26 @@ close_one() rp->flag |= RED_USED; errno = 0; if (/* do_lint && */ fclose(rp->fp) != 0) - warning(_("close of `%s' failed (%s)."), + 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")); + fatal("too many pipes or input files open"); } /* do_close --- completely close an open file or pipe */ NODE * -do_close(NODE *tree) +do_close(tree) +NODE *tree; { - NODE *tmp, *tmp2; + NODE *tmp; 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); - } + + tmp = force_string(tree_eval(tree->subnode)); for (rp = red_head; rp != NULL; rp = rp->next) { if (strlen(rp->value) == tmp->stlen @@ -716,24 +594,22 @@ do_close(NODE *tree) 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"), + if (rp == NULL) { /* no match */ + /* icky special case: close(FILENAME) called. */ + if (tree->subnode == FILENAME_node + || (tmp->stlen == FILENAME_node->var_value->stlen + && STREQN(tmp->stptr, FILENAME_node->var_value->stptr, tmp->stlen))) { + (void) nextfile(TRUE); + } else if (do_lint) + warning("close: `%.*s' is not an open file or pipe", 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); + return tmp_number((AWKNUM) 0.0); } free_temp(tmp); fflush(stdout); /* synchronize regular output */ - tmp = tmp_number((AWKNUM) close_redir(rp, FALSE, how)); + tmp = tmp_number((AWKNUM) close_redir(rp, FALSE)); rp = NULL; return tmp; } @@ -741,63 +617,33 @@ do_close(NODE *tree) /* close_redir --- close an open file or pipe */ static int -close_redir(register struct redirect *rp, int exitwarn, two_way_close_type how) +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; - - 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 */ + if ((rp->flag & (RED_PIPE|RED_WRITE)) == (RED_PIPE|RED_WRITE)) 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 */ + else if (rp->fp != NULL) 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 */ + else if (rp->iop != NULL) { + if ((rp->flag & RED_PIPE) != 0) status = gawk_pclose(rp); - /* gawk_pclose sets rp->iop to null */ - else { /* read from file */ + else { status = iop_close(rp->iop); rp->iop = NULL; } } + what = ((rp->flag & RED_PIPE) != 0) ? "pipe" : "file"; + /* SVR4 awk checks and warns about status of close */ if (status != 0) { char *s = strerror(errno); @@ -806,55 +652,29 @@ close_redir(register struct redirect *rp, int exitwarn, two_way_close_type how) * 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_lint) + warning("failure status (%d) on %s close of \"%s\" (%s)", + status, what, rp->value, s); if (! do_traditional) { /* set ERRNO too so that program can get at it */ - update_ERRNO(); + unref(ERRNO_node->var_value); + ERRNO_node->var_value = make_string(s, strlen(s)); } } - 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); - } + if (exitwarn) + warning("no explicit close of %s `%s' provided", + what, rp->value); + 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; } @@ -868,31 +688,23 @@ flush_io() errno = 0; if (fflush(stdout)) { - warning(_("error writing standard output (%s)"), strerror(errno)); + warning("error writing standard output (%s)", strerror(errno)); status++; } if (fflush(stderr)) { - warning(_("error writing standard error (%s)"), strerror(errno)); + 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)); + warning("%s flush of \"%s\" failed (%s).", + (rp->flag & RED_PIPE) ? "pipe" : + "file", rp->value, strerror(errno)); status++; } } - if (status != 0) - status = -1; /* canonicalize it */ return status; } @@ -912,7 +724,7 @@ close_io() * 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)) + if (close_redir(rp, do_lint)) status++; rp = NULL; } @@ -922,11 +734,11 @@ close_io() * them, we just flush them, and do that across the board. */ if (fflush(stdout)) { - warning(_("error writing standard output (%s)"), strerror(errno)); + warning("error writing standard output (%s)", strerror(errno)); status++; } if (fflush(stderr)) { - warning(_("error writing standard error (%s)"), strerror(errno)); + warning("error writing standard error (%s)", strerror(errno)); status++; } return status; @@ -935,167 +747,31 @@ close_io() /* str2mode --- convert a string mode to an integer mode */ static int -str2mode(const char *mode) +str2mode(mode) +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 */ /* @@ -1105,38 +781,33 @@ socketopen(enum inet_prot type, int localport, int remoteport, char *remotehostn * 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) +devopen(name, mode) +const char *name, *mode; { int openfd; - char *cp; + const char *cp; char *ptr; int flag = 0; + struct stat buf; extern double strtod(); flag = str2mode(mode); if (STREQ(name, "-")) - return fileno(stdin); - - openfd = INVALID_HANDLE; + openfd = fileno(stdin); + else + openfd = INVALID_HANDLE; if (do_traditional) goto strictopen; - if ((openfd = os_devopen(name, flag)) != INVALID_HANDLE) { - os_close_on_exec(openfd, name, "file", ""); + if ((openfd = os_devopen(name, flag)) >= 0) return openfd; - } - - if (STREQN(name, "/dev/", 5)) { - cp = (char *) name + 5; + if (STREQN(name, "/dev/", 5) && stat((char *) name, &buf) == -1) { + cp = name + 5; + if (STREQ(cp, "stdin") && (flag & O_ACCMODE) == O_RDONLY) openfd = fileno(stdin); else if (STREQ(cp, "stdout") && (flag & O_ACCMODE) == O_WRONLY) @@ -1149,115 +820,14 @@ devopen(const char *name, const char *mode) 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", ""); - } + if (openfd != INVALID_HANDLE && fstat(openfd, &buf) > 0) + if (S_ISDIR(buf.st_mode)) + fatal("file `%s' is a directory", name); return openfd; } @@ -1265,7 +835,10 @@ strictopen: /* spec_setup --- setup an IOBUF for a special internal file */ static void -spec_setup(IOBUF *iop, int len, int allocate) +spec_setup(iop, len, allocate) +IOBUF *iop; +int len; +int allocate; { char *cp; @@ -1284,12 +857,15 @@ spec_setup(IOBUF *iop, int len, int allocate) iop->end = iop->buf + len; iop->fd = -1; iop->flag = IOP_IS_INTERNAL; + iop->getrec = get_a_record; } /* specfdopen --- open an fd special file */ static int -specfdopen(IOBUF *iop, const char *name, const char *mode) +specfdopen(iop, name, mode) +IOBUF *iop; +const char *name, *mode; { int fd; IOBUF *tp; @@ -1318,13 +894,12 @@ specfdopen(IOBUF *iop, const char *name, const char *mode) /* pidopen --- "open" /dev/pid, /dev/ppid, and /dev/pgrpid */ static int -pidopen(IOBUF *iop, const char *name, const char *mode) +pidopen(iop, name, mode) +IOBUF *iop; +const char *name, *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())); @@ -1351,7 +926,9 @@ pidopen(IOBUF *iop, const char *name, const char *mode) */ static int -useropen(IOBUF *iop, const char *name, const char *mode) +useropen(iop, name, mode) +IOBUF *iop; +const char *name, *mode; { char tbuf[BUFSIZ], *cp; int i; @@ -1360,15 +937,13 @@ useropen(IOBUF *iop, const char *name, const char *mode) 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)); + fatal("could not find groups: %s", strerror(errno)); for (i = 0; i < ngroups; i++) { *cp++ = ' '; @@ -1388,10 +963,13 @@ useropen(IOBUF *iop, const char *name, const char *mode) /* iop_open --- handle special and regular files for input */ static IOBUF * -iop_open(const char *name, const char *mode, IOBUF *iop) +iop_open(name, mode, iop) +const char *name, *mode; +IOBUF *iop; { int openfd = INVALID_HANDLE; int flag = 0; + struct stat buf; static struct internal { const char *name; int compare; @@ -1402,7 +980,6 @@ iop_open(const char *name, const char *mode, IOBUF *iop) { "/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 }, @@ -1412,11 +989,15 @@ iop_open(const char *name, const char *mode, IOBUF *iop) flag = str2mode(mode); + /* + * FIXME: remove the stat call, and always process these files + * internally. + */ if (STREQ(name, "-")) openfd = fileno(stdin); else if (do_traditional) goto strictopen; - else if (STREQN(name, "/dev/", 5) || STREQN(name, "/inet/", 6)) { + else if (STREQN(name, "/dev/", 5) && stat((char *) name, &buf) == -1) { int i; for (i = 0; i < devcount; i++) { @@ -1429,185 +1010,30 @@ iop_open(const char *name, const char *mode, IOBUF *iop) } else if ((*table[i].fp)(iop, name, mode) == 0) return iop; else { - warning(_("could not open `%s', mode `%s'"), + 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", ""); - } + if (openfd != INVALID_HANDLE && fstat(openfd, &buf) > 0) + if (S_ISDIR(buf.st_mode)) + fatal("file `%s' is a directory", name); 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 */ +wait_any(interesting) +int interesting; /* pid of interest, if any */ { RETSIGTYPE (*hstat)(), (*istat)(), (*qstat)(); int pid; @@ -1646,7 +1072,9 @@ wait_any(int interesting) /* pid of interest, if any */ /* gawk_popen --- open an IOBUF on a child process */ static IOBUF * -gawk_popen(char *cmd, struct redirect *rp) +gawk_popen(cmd, rp) +char *cmd; +struct redirect *rp; { int p[2]; register int pid; @@ -1659,24 +1087,23 @@ gawk_popen(char *cmd, struct redirect *rp) /*(void) wait_any(0);*/ /* wait for outstanding processes */ if (pipe(p) < 0) - fatal(_("cannot open pipe `%s' (%s)"), cmd, strerror(errno)); + 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)"), + 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)); + 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)); + 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)); + 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)); - os_close_on_exec(p[0], cmd, "pipe", "from"); + fatal("close of pipe failed (%s)", strerror(errno)); rp->iop = iop_alloc(p[0], cmd, NULL); if (rp->iop == NULL) (void) close(p[0]); @@ -1686,18 +1113,18 @@ gawk_popen(char *cmd, struct redirect *rp) /* gawk_pclose --- close an open child pipe */ static int -gawk_pclose(struct redirect *rp) +gawk_pclose(rp) +struct redirect *rp; { - if (rp->iop != NULL) - (void) iop_close(rp->iop); + (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); + return (rp->status >> 8) & 0xFF; rp->status = wait_any(rp->pid); rp->pid = -1; - return (rp->status >> 8) + ((rp->status &0xFF) ? 128 + (rp->status & 0xF) : 0); + return (rp->status >> 8) & 0xFF; } #else /* PIPES_SIMULATED */ @@ -1707,22 +1134,19 @@ gawk_pclose(struct redirect *rp) * except if popen() provides real pipes too */ -#if defined(VMS) || defined(OS2) || defined (MSDOS) || defined(WIN32) || defined(TANDEM) +#if defined(VMS) || defined(OS2) || defined (MSDOS) || defined(WIN32) /* gawk_popen --- open an IOBUF on a child process */ static IOBUF * -gawk_popen(char *cmd, struct redirect *rp) +gawk_popen(cmd, rp) +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) + if ((current = popen(cmd, "r")) == 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); @@ -1735,20 +1159,19 @@ gawk_popen(char *cmd, struct redirect *rp) /* gawk_pclose --- close an open child pipe */ static int -gawk_pclose(struct redirect *rp) +gawk_pclose(rp) +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->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) */ +#else /* not (VMS || OS2 || MSDOS) */ static struct pipeinfo { char *command; @@ -1758,7 +1181,9 @@ static struct pipeinfo { /* gawk_popen --- open an IOBUF on a child process */ static IOBUF * -gawk_popen(char *cmd, struct redirect *rp) +gawk_popen(cmd, rp) +char *cmd; +struct redirect *rp; { extern char *strdup P((const char *)); int current; @@ -1774,7 +1199,6 @@ gawk_popen(char *cmd, struct redirect *rp) 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); @@ -1784,13 +1208,13 @@ gawk_popen(char *cmd, struct redirect *rp) /* gawk_pclose --- close an open child pipe */ static int -gawk_pclose(struct redirect *rp) +gawk_pclose(rp) +struct redirect *rp; { int cur = rp->iop->fd; - int rval = 0; + int rval; - if (rp->iop != NULL) - rval = iop_close(rp->iop); + rval = iop_close(rp->iop); rp->iop = NULL; /* check for an open file */ @@ -1802,14 +1226,15 @@ gawk_pclose(struct redirect *rp) free(pipes[cur].command); return rval; } -#endif /* not (VMS || OS2 || MSDOS || TANDEM) */ +#endif /* not (VMS || OS2 || MSDOS) */ #endif /* PIPES_SIMULATED */ /* do_getline --- read in a line, into var and with redirection, as needed */ NODE * -do_getline(NODE *tree) +do_getline(tree) +NODE *tree; { struct redirect *rp = NULL; IOBUF *iop; @@ -1827,9 +1252,13 @@ do_getline(NODE *tree) rp = redirect(tree->rnode, &redir_error); if (rp == NULL && redir_error) { /* failed redirect */ - if (! do_traditional) - update_ERRNO(); + if (! do_traditional) { + 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; @@ -1837,11 +1266,14 @@ do_getline(NODE *tree) return tmp_number((AWKNUM) 0.0); } errcode = 0; - cnt = get_a_record(&s, iop, RS->stptr[0], RS_regexp, &errcode); + cnt = (*(iop->getrec))(&s, iop, RS->stptr[0], RS_regexp, &errcode); if (errcode != 0) { - if (! do_traditional) - update_ERRNO(); + if (! do_traditional) { + 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) { @@ -1851,7 +1283,7 @@ do_getline(NODE *tree) * reading from a pipe; otherwise * gawk_pclose will not be called. */ - if ((rp->flag & (RED_PIPE|RED_TWOWAY)) == 0) { + if ((rp->flag & RED_PIPE) == 0) { (void) iop_close(iop); rp->iop = NULL; } @@ -1870,7 +1302,7 @@ do_getline(NODE *tree) Func_ptr after_assign = NULL; NODE **lhs; - lhs = get_lhs(tree->lnode, &after_assign, FALSE); + lhs = get_lhs(tree->lnode, &after_assign); unref(*lhs); *lhs = make_string(s, cnt); (*lhs)->flags |= MAYBE_NUM; @@ -1885,7 +1317,8 @@ do_getline(NODE *tree) /* pathopen --- pathopen with default file extension handling */ int -pathopen(const char *file) +pathopen(file) +const char *file; { int fd = do_pathopen(file); @@ -1918,7 +1351,8 @@ pathopen(const char *file) /* do_pathopen --- search $AWKPATH for source file */ static int -do_pathopen(const char *file) +do_pathopen(file) +const char *file; { static const char *savepath = NULL; static int first = TRUE; @@ -1978,7 +1412,8 @@ do_pathopen(const char *file) int bufsize = 8192; void -fatal(char *s) +fatal(s) +char *s; { printf("%s\n", s); exit(1); @@ -1988,7 +1423,10 @@ fatal(char *s) /* iop_alloc --- allocate an IOBUF structure for an open fd */ static IOBUF * -iop_alloc(int fd, const char *name, IOBUF *iop) +iop_alloc(fd, name, iop) +int fd; +const char *name; +IOBUF *iop; { struct stat sbuf; @@ -2001,16 +1439,63 @@ iop_alloc(int fd, const char *name, IOBUF *iop) 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); + warning("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; + iop->getrec = get_a_record; +#ifdef HAVE_MMAP + /* Use mmap only for regular files with positive sizes. + The size must fit into size_t, so that mmap works correctly. + Also, it must fit into int, so that iop->cnt won't overflow. */ + if (S_ISREG(sbuf.st_mode) && sbuf.st_size > 0 + && sbuf.st_size == (size_t) sbuf.st_size + && sbuf.st_size == (int) sbuf.st_size) { + register char *cp; + + iop->buf = iop->off = mmap((caddr_t) 0, sbuf.st_size, + PROT_READ|PROT_WRITE, MAP_PRIVATE, + fd, 0L); + /* cast is for buggy compilers (e.g. DEC OSF/1) */ + if (iop->buf == (caddr_t)MAP_FAILED) { + iop->buf = iop->off = NULL; + goto out; + } + + iop->flag |= IOP_MMAPPED; + iop->size = sbuf.st_size; + iop->secsiz = 0; + iop->end = iop->buf + iop->size; + iop->cnt = sbuf.st_size; + iop->getrec = mmap_get_record; + (void) close(fd); + iop->fd = INVALID_HANDLE; + +#if defined(HAVE_MADVISE) && defined(MADV_SEQUENTIAL) + madvise(iop->buf, iop->size, MADV_SEQUENTIAL); +#endif + /* + * The following is a really gross hack. + * We want to ensure that we have a copy of the input + * data that won't go away, on the off chance that someone + * will truncate the data file we've just mmap'ed. + * So, we go through and touch each page, forcing the + * system to give us a private copy. A page size of 512 + * guarantees this will work, even on the least common + * denominator system (like, oh say, a VAX). + */ + for (cp = iop->buf; cp < iop->end; cp += 512) + *cp = *cp; + } +out: +#endif /* HAVE_MMAP */ return iop; } +/* These macros used by both record reading routines */ #define set_RT_to_null() \ (void)(! do_traditional && (unref(RT_node->var_value), \ RT_node->var_value = Nnull_string)) @@ -2039,11 +1524,12 @@ iop_alloc(int fd, const char *name, IOBUF *iop) */ 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 */ +get_a_record(out, iop, grRS, RSre, errcode) +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; @@ -2054,21 +1540,11 @@ get_a_record(char **out, /* pointer to pointer to data */ 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"), + fatal("internal error: file `%s', line %d\n", __FILE__, __LINE__); } @@ -2078,21 +1554,14 @@ get_a_record(char **out, /* pointer to pointer to data */ 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)); + onecase = (IGNORECASE && isalpha(rs)); if (onecase) - rs = casetable[(unsigned char) rs]; + rs = casetable[rs]; /* set up sentinel */ if (iop->buf) { @@ -2142,10 +1611,6 @@ get_a_record(char **out, /* pointer to pointer to data */ } 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; @@ -2170,7 +1635,7 @@ get_a_record(char **out, /* pointer to pointer to data */ iop->cnt = EOF; break; } else - fatal(_("error reading input file `%s': %s"), + fatal("error reading input file `%s': %s", iop->name, strerror(errno)); } else if (iop->cnt == 0) { /* @@ -2222,14 +1687,6 @@ get_a_record(char **out, /* pointer to pointer to data */ * 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) { @@ -2258,34 +1715,20 @@ get_a_record(char **out, /* pointer to pointer to data */ /* 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; - } + bp = iop->end; + continuing = continued = TRUE; + continue; } } /* got a match! */ /* * Leading newlines at the beginning of the file * should be ignored. Whew! + * + * Is this code ever executed? */ - 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++; + if (RS_is_null && RESTART(rsre, start) == 0) { + start += REEND(rsre, start); goto again; } bp = start + RESTART(rsre, start); @@ -2296,10 +1739,10 @@ get_a_record(char **out, /* pointer to pointer to data */ } /* search for RS, #2, RS = <single char> */ if (onecase) { - while (casetable[(unsigned char) *bp++] != rs && not_past_end()) + while (casetable[(int) *bp++] != rs) continue; } else { - while (*bp++ != rs && not_past_end()) + while (*bp++ != rs) continue; } set_RT(bp - 1, 1); @@ -2321,12 +1764,15 @@ get_a_record(char **out, /* pointer to pointer to data */ } if (do_traditional || rsre == NULL) { - iop->off = bp; + char *bstart; + + bstart = iop->off = bp; bp--; - if (onecase ? casetable[(unsigned char) *bp] != rs : *bp != rs) + if (onecase ? casetable[(int) *bp] != rs : *bp != rs) { bp++; - if (MRL == 0) - *bp = '\0'; + bstart = bp; + } + *bp = '\0'; } else if (RS_is_null && iop->cnt == EOF) { /* * special case, delete trailing newlines, @@ -2343,7 +1789,9 @@ get_a_record(char **out, /* pointer to pointer to data */ #ifdef TEST int -main(int argc, char *argv[]) +main(argc, argv) +int argc; +char *argv[]; { IOBUF *iop; char *out; @@ -2364,6 +1812,145 @@ main(int argc, char *argv[]) } #endif +#ifdef HAVE_MMAP +/* mmap_get_record --- pull a record out of a memory-mapped file */ + +static int +mmap_get_record(out, iop, grRS, RSre, errcode) +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 *start = iop->off; /* beginning of record */ + int rs; + static Regexp *RS_null_re = NULL; + Regexp *rsre = NULL; + int onecase; + register char *end = iop->end; + int cnt; + + /* 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->off >= iop->end) { /* previous record was last */ + *out = NULL; + set_RT_to_null(); + iop->cnt = EOF; /* tested by higher level code */ + return EOF; + } + + if (RS_is_null) /* special case: RS == "" */ + rs = '\n'; + else + rs = (char) grRS; + + onecase = (IGNORECASE && isalpha(rs)); + if (onecase) + rs = casetable[rs]; + + /* if RS = "", skip leading newlines at the front of the file */ + if (RS_is_null && iop->off == iop->buf) { + for (bp = iop->off; *bp == '\n'; bp++) + continue; + + if (bp != iop->off) + iop->off = start = bp; + } + + /* + * Regexp based searching. Either RS = "" or RS = <regex> + * See comments in get_a_record. + */ + 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 + * + * #1 means that the record ends the file + * and there is no text that actually matched RS. + * + * #2: is probably like #1. + * + * #3 is simple; since we have the whole file mapped, it's + * the last record in the file. + */ + if (rsre != NULL) { + if (research(rsre, start, 0, iop->end - start, TRUE) == -1 + || RESTART(rsre, start) == REEND(rsre, start)) { + /* no matching text, we have the record */ + *out = start; + iop->off = iop->end; /* all done with the record */ + set_RT_to_null(); + /* special case, don't allow trailing newlines */ + if (RS_is_null && *(iop->end - 1) == '\n') + return iop->end - start - 1; + else + return iop->end - start; + + } + /* have a match */ + *out = start; + bp = start + RESTART(rsre, start); + set_RT(bp, REEND(rsre, start) - RESTART(rsre, start)); + *bp = '\0'; + iop->off = start + REEND(rsre, start); + return bp - start; + } + + /* + * RS = "?", i.e., one character based searching. + * + * Alas, we can't just plug the sentinel character in at + * the end of the mmapp'ed file ( *(iop->end) = rs; ). This + * works if we're lucky enough to have a file that does not + * take up all of its last disk block. But if we end up with + * file whose size is an even multiple of the disk block size, + * assigning past the end of it delivers a SIGBUS. So, we have to + * add the extra test in the while loop at the front that looks + * for going past the end of the mapped object. Sigh. + */ + /* search for RS, #2, RS = <single char> */ + if (onecase) { + while (bp < end && casetable[*bp++] != rs) + continue; + } else { + while (bp < end && *bp++ != rs) + continue; + } + cnt = (bp - start) - 1; + if (bp >= iop->end) { + /* at end, may have actually seen rs, or may not */ + if (*(bp-1) == rs) + set_RT(bp - 1, 1); /* real RS seen */ + else { + cnt++; + set_RT_to_null(); + } + } else + set_RT(bp - 1, 1); + + iop->off = bp; + *out = start; + return cnt; +} +#endif /* HAVE_MMAP */ + /* set_RS --- update things as appropriate when RS is set */ void @@ -2389,7 +1976,7 @@ set_RS() RS_regexp = make_regexp(RS->stptr, RS->stlen, IGNORECASE, TRUE); if (do_lint && ! warned) { - lintwarn(_("multicharacter value of `RS' is a gawk extension")); + warning("multicharacter value of `RS' is not portable"); warned = TRUE; } } |