diff options
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/awk/io.c | 1303 |
1 files changed, 859 insertions, 444 deletions
diff --git a/contrib/awk/io.c b/contrib/awk/io.c index a83fc9d..8d741e2 100644 --- a/contrib/awk/io.c +++ b/contrib/awk/io.c @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 1976, 1988, 1989, 1991-2000 the Free Software Foundation, Inc. + * 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. @@ -26,7 +26,6 @@ */ #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 */ @@ -37,13 +36,6 @@ #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 @@ -51,32 +43,65 @@ #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 -#if ! defined(S_ISDIR) && defined(S_IFDIR) -#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) -#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) +#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)); +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 @@ -86,16 +111,14 @@ 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 @@ -114,6 +137,27 @@ 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. */ @@ -135,8 +179,7 @@ do_nextfile() /* nextfile --- move to the next input data file */ static IOBUF * -nextfile(skipping) -int skipping; +nextfile(int skipping) { static long i = 1; static int files = 0; @@ -159,7 +202,7 @@ int skipping; return curfile; } for (; i < (long) (ARGC_node->lnode->numbr); i++) { - arg = *assoc_lookup(ARGV_node, tmp_number((AWKNUM) i)); + arg = *assoc_lookup(ARGV_node, tmp_number((AWKNUM) i), FALSE); if (arg->stlen == 0) continue; arg->stptr[arg->stlen] = '\0'; @@ -170,7 +213,7 @@ int skipping; if (! arg_assign(arg->stptr)) { files++; fname = arg->stptr; - curfile = iop_open(fname, "r", &mybuf); + curfile = iop_open(fname, binmode("r"), &mybuf); if (curfile == NULL) goto give_up; curfile->flag |= IOP_NOFREE_OBJ; @@ -188,7 +231,7 @@ int skipping; /* FNR is init'ed to 0 */ FILENAME_node->var_value = make_string("-", 1); fname = "-"; - curfile = iop_open(fname, "r", &mybuf); + curfile = iop_open(fname, binmode("r"), &mybuf); if (curfile == NULL) goto give_up; curfile->flag |= IOP_NOFREE_OBJ; @@ -196,7 +239,7 @@ 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; @@ -221,16 +264,14 @@ set_NR() /* inrec --- This reads in a record from the input file */ static int -inrec(iop) -IOBUF *iop; +inrec(IOBUF *iop) { char *begin; register int cnt; int retval = 0; if ((cnt = iop->cnt) != EOF) - cnt = (*(iop->getrec)) - (&begin, iop, RS->stptr[0], RS_regexp, NULL); + cnt = get_a_record(&begin, iop, RS->stptr[0], RS_regexp, NULL); if (cnt == EOF) { cnt = 0; retval = 1; @@ -246,8 +287,7 @@ IOBUF *iop; /* iop_close --- close an open IOP */ static int -iop_close(iop) -IOBUF *iop; +iop_close(IOBUF *iop) { int ret; @@ -273,14 +313,13 @@ 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->flag & IOP_MMAPPED) != 0) + || iop->fd == fileno(stderr)) 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) { /* @@ -299,12 +338,7 @@ IOBUF *iop; fields_arr[0] = t; reset_record(); } - if ((iop->flag & IOP_MMAPPED) == 0) - free(iop->buf); -#ifdef HAVE_MMAP - else - (void) munmap(iop->buf, iop->size); -#endif + free(iop->buf); } if ((iop->flag & IOP_NOFREE_OBJ) == 0) free((char *) iop); @@ -326,21 +360,37 @@ 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(tree, errflg) -NODE *tree; -int *errflg; +redirect(NODE *tree, int *errflg) { register NODE *tmp; register struct redirect *rp; @@ -376,31 +426,57 @@ 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 %d in redirect()", tree->type); + fatal(_("invalid tree type %s in redirect()"), + nodetype2str(tree->type)); break; } tmp = tree_eval(tree->subnode); if (do_lint && (tmp->flags & STR) == 0) - warning("expression in `%s' redirection only has numeric value", + 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", + fatal(_("expression for `%s' redirection has null string value"), what); if (do_lint && (STREQN(str, "0", tmp->stlen) || STREQN(str, "1", tmp->stlen))) - warning("filename `%s' for `%s' redirection may be result of logical expression", str, what); - for (rp = red_head; rp != NULL; rp = rp->next) + 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))) + && (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"); @@ -421,6 +497,7 @@ 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) /* @@ -432,31 +509,48 @@ int *errflg; errno = 0; switch (tree->type) { case Node_redirect_output: - mode = "w"; + mode = binmode("w"); if ((rp->flag & RED_USED) != 0) - mode = "a"; + mode = (rp->mode[1] == 'b') ? "ab" : "a"; break; case Node_redirect_append: - mode = "a"; + mode = binmode("a"); break; case Node_redirect_pipe: /* synchronize output before new pipe */ (void) flush_io(); - if ((rp->fp = popen(str, "w")) == NULL) - fatal("can't open pipe (\"%s\") for output (%s)", + 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)", + fatal(_("can't open pipe `%s' for input (%s)"), str, strerror(errno)); break; case Node_redirect_input: direction = "from"; - rp->iop = iop_open(str, "r", NULL); + 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(); @@ -472,7 +566,16 @@ int *errflg; else if (fd == fileno(stderr)) rp->fp = stderr; else { - rp->fp = fdopen(fd, (char *) mode); +#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); @@ -494,9 +597,7 @@ int *errflg; /* too many files open -- close one and try again */ if (errno == EMFILE || errno == ENFILE) close_one(); -#if defined __MINGW32__ || defined HAVE_MMAP - /* this works for solaris 2.5, not sunos */ - /* it is also needed for MINGW32 */ +#if defined __MINGW32__ || defined solaris else if (errno == 0) /* HACK! */ close_one(); #endif @@ -520,10 +621,15 @@ int *errflg; if (errflg != NULL) *errflg = errno; if (tree->type == Node_redirect_output - || tree->type == Node_redirect_append) - fatal("can't redirect %s `%s' (%s)", - direction, str, strerror(errno)); - else { + || 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; } @@ -537,9 +643,7 @@ int *errflg; /* getredirect --- find the struct redirect for this file or pipe */ struct redirect * -getredirect(str, len) -char *str; -int len; +getredirect(char *str, int len) { struct redirect *rp; @@ -558,6 +662,13 @@ 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; @@ -567,26 +678,39 @@ 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(tree) -NODE *tree; +do_close(NODE *tree) { - NODE *tmp; + NODE *tmp, *tmp2; register struct redirect *rp; - - tmp = force_string(tree_eval(tree->subnode)); + 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 @@ -594,22 +718,24 @@ NODE *tree; break; } - 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", + 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) 0.0); + return tmp_number((AWKNUM) -1.0); } free_temp(tmp); fflush(stdout); /* synchronize regular output */ - tmp = tmp_number((AWKNUM) close_redir(rp, FALSE)); + tmp = tmp_number((AWKNUM) close_redir(rp, FALSE, how)); rp = NULL; return tmp; } @@ -617,33 +743,63 @@ NODE *tree; /* close_redir --- close an open file or pipe */ static int -close_redir(rp, exitwarn) -register struct redirect *rp; -int exitwarn; +close_redir(register struct redirect *rp, int exitwarn, two_way_close_type how) { 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_PIPE|RED_WRITE)) == (RED_PIPE|RED_WRITE)) + 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); - else if (rp->fp != NULL) + 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); - else if (rp->iop != NULL) { - if ((rp->flag & RED_PIPE) != 0) + 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); - else { + /* gawk_pclose sets rp->iop to null */ + else { /* read from file */ 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); @@ -652,29 +808,55 @@ int exitwarn; * Too many people have complained about this. * As of 2.15.6, it is now under lint control. */ - if (do_lint) - warning("failure status (%d) on %s close of \"%s\" (%s)", - status, what, rp->value, s); + 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 */ - unref(ERRNO_node->var_value); - ERRNO_node->var_value = make_string(s, strlen(s)); + update_ERRNO(); } } - if (exitwarn) - warning("no explicit close of %s `%s' provided", - what, rp->value); + 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 (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; } @@ -688,23 +870,31 @@ 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)) { - warning("%s flush of \"%s\" failed (%s).", - (rp->flag & RED_PIPE) ? "pipe" : - "file", rp->value, strerror(errno)); + 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; } @@ -724,7 +914,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)) + if (close_redir(rp, do_lint, CLOSE_ALL)) status++; rp = NULL; } @@ -734,11 +924,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; @@ -747,31 +937,167 @@ close_io() /* str2mode --- convert a string mode to an integer mode */ static int -str2mode(mode) -const char *mode; +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 */ /* @@ -781,33 +1107,38 @@ const char *mode; * no sense to use them for output. */ +/* + * Strictly speaking, "name" is not a "const char *" because we temporarily + * change the string. + */ + int -devopen(name, mode) -const char *name, *mode; +devopen(const char *name, const char *mode) { int openfd; - const char *cp; + char *cp; char *ptr; int flag = 0; - struct stat buf; extern double strtod(); flag = str2mode(mode); if (STREQ(name, "-")) - openfd = fileno(stdin); - else - openfd = INVALID_HANDLE; + return fileno(stdin); + + openfd = INVALID_HANDLE; if (do_traditional) goto strictopen; - if ((openfd = os_devopen(name, flag)) >= 0) + 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 (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) @@ -820,14 +1151,115 @@ const char *name, *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 && fstat(openfd, &buf) > 0) - if (S_ISDIR(buf.st_mode)) - fatal("file `%s' is a directory", name); + if (openfd != INVALID_HANDLE) { + if (os_isdir(openfd)) + fatal(_("file `%s' is a directory"), name); + + os_close_on_exec(openfd, name, "file", ""); + } return openfd; } @@ -835,10 +1267,7 @@ strictopen: /* spec_setup --- setup an IOBUF for a special internal file */ static void -spec_setup(iop, len, allocate) -IOBUF *iop; -int len; -int allocate; +spec_setup(IOBUF *iop, int len, int allocate) { char *cp; @@ -857,15 +1286,12 @@ 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(iop, name, mode) -IOBUF *iop; -const char *name, *mode; +specfdopen(IOBUF *iop, const char *name, const char *mode) { int fd; IOBUF *tp; @@ -894,12 +1320,13 @@ const char *name, *mode; /* pidopen --- "open" /dev/pid, /dev/ppid, and /dev/pgrpid */ static int -pidopen(iop, name, mode) -IOBUF *iop; -const char *name, *mode; +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())); @@ -926,9 +1353,7 @@ const char *name, *mode; */ static int -useropen(iop, name, mode) -IOBUF *iop; -const char *name, *mode; +useropen(IOBUF *iop, const char *name, const char *mode) { char tbuf[BUFSIZ], *cp; int i; @@ -937,13 +1362,15 @@ const char *name, *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++ = ' '; @@ -963,13 +1390,10 @@ const char *name, *mode; /* iop_open --- handle special and regular files for input */ static IOBUF * -iop_open(name, mode, iop) -const char *name, *mode; -IOBUF *iop; +iop_open(const char *name, const char *mode, IOBUF *iop) { int openfd = INVALID_HANDLE; int flag = 0; - struct stat buf; static struct internal { const char *name; int compare; @@ -980,6 +1404,7 @@ 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 }, @@ -989,15 +1414,11 @@ 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) && stat((char *) name, &buf) == -1) { + else if (STREQN(name, "/dev/", 5) || STREQN(name, "/inet/", 6)) { int i; for (i = 0; i < devcount; i++) { @@ -1010,30 +1431,185 @@ 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 && fstat(openfd, &buf) > 0) - if (S_ISDIR(buf.st_mode)) - fatal("file `%s' is a directory", name); + 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(interesting) -int interesting; /* pid of interest, if any */ +wait_any(int interesting) /* pid of interest, if any */ { RETSIGTYPE (*hstat)(), (*istat)(), (*qstat)(); int pid; @@ -1072,9 +1648,7 @@ int interesting; /* pid of interest, if any */ /* gawk_popen --- open an IOBUF on a child process */ static IOBUF * -gawk_popen(cmd, rp) -char *cmd; -struct redirect *rp; +gawk_popen(char *cmd, struct redirect *rp) { int p[2]; register int pid; @@ -1087,23 +1661,24 @@ 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("dup of pipe failed (%s)", strerror(errno)); + 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)); + fatal(_("close of pipe failed (%s)"), strerror(errno)); execl("/bin/sh", "sh", "-c", cmd, NULL); _exit(127); } if (pid == -1) - fatal("cannot fork for \"%s\" (%s)", cmd, strerror(errno)); + 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)); + 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]); @@ -1113,18 +1688,18 @@ struct redirect *rp; /* gawk_pclose --- close an open child pipe */ static int -gawk_pclose(rp) -struct redirect *rp; +gawk_pclose(struct redirect *rp) { - (void) iop_close(rp->iop); + 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) & 0xFF; + 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) & 0xFF; + return (rp->status >> 8) + ((rp->status &0xFF) ? 128 + (rp->status & 0xF) : 0); } #else /* PIPES_SIMULATED */ @@ -1134,19 +1709,22 @@ struct redirect *rp; * except if popen() provides real pipes too */ -#if defined(VMS) || defined(OS2) || defined (MSDOS) || defined(WIN32) +#if defined(VMS) || defined(OS2) || defined (MSDOS) || defined(WIN32) || defined(TANDEM) /* gawk_popen --- open an IOBUF on a child process */ static IOBUF * -gawk_popen(cmd, rp) -char *cmd; -struct redirect *rp; +gawk_popen(char *cmd, struct redirect *rp) { FILE *current; - if ((current = popen(cmd, "r")) == NULL) + 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); @@ -1159,19 +1737,20 @@ struct redirect *rp; /* gawk_pclose --- close an open child pipe */ static int -gawk_pclose(rp) -struct redirect *rp; +gawk_pclose(struct redirect *rp) { int rval, aval, fd = rp->iop->fd; - rp->iop->fd = dup(fd); /* kludge to allow close() + pclose() */ - rval = iop_close(rp->iop); + 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) */ +#else /* not (VMS || OS2 || MSDOS || TANDEM) */ static struct pipeinfo { char *command; @@ -1181,9 +1760,7 @@ static struct pipeinfo { /* gawk_popen --- open an IOBUF on a child process */ static IOBUF * -gawk_popen(cmd, rp) -char *cmd; -struct redirect *rp; +gawk_popen(char *cmd, struct redirect *rp) { extern char *strdup P((const char *)); int current; @@ -1199,6 +1776,7 @@ 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); @@ -1208,13 +1786,13 @@ struct redirect *rp; /* gawk_pclose --- close an open child pipe */ static int -gawk_pclose(rp) -struct redirect *rp; +gawk_pclose(struct redirect *rp) { int cur = rp->iop->fd; - int rval; + int rval = 0; - rval = iop_close(rp->iop); + if (rp->iop != NULL) + rval = iop_close(rp->iop); rp->iop = NULL; /* check for an open file */ @@ -1226,15 +1804,14 @@ struct redirect *rp; free(pipes[cur].command); return rval; } -#endif /* not (VMS || OS2 || MSDOS) */ +#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(tree) -NODE *tree; +do_getline(NODE *tree) { struct redirect *rp = NULL; IOBUF *iop; @@ -1252,13 +1829,9 @@ NODE *tree; rp = redirect(tree->rnode, &redir_error); if (rp == NULL && redir_error) { /* failed redirect */ - if (! do_traditional) { - s = strerror(redir_error); + if (! do_traditional) + update_ERRNO(); - unref(ERRNO_node->var_value); - ERRNO_node->var_value = - make_string(s, strlen(s)); - } return tmp_number((AWKNUM) -1.0); } iop = rp->iop; @@ -1266,14 +1839,11 @@ NODE *tree; return tmp_number((AWKNUM) 0.0); } errcode = 0; - cnt = (*(iop->getrec))(&s, iop, RS->stptr[0], RS_regexp, &errcode); + cnt = get_a_record(&s, iop, RS->stptr[0], RS_regexp, &errcode); if (errcode != 0) { - if (! do_traditional) { - s = strerror(errcode); + if (! do_traditional) + update_ERRNO(); - unref(ERRNO_node->var_value); - ERRNO_node->var_value = make_string(s, strlen(s)); - } return tmp_number((AWKNUM) -1.0); } if (cnt == EOF) { @@ -1283,7 +1853,7 @@ NODE *tree; * reading from a pipe; otherwise * gawk_pclose will not be called. */ - if ((rp->flag & RED_PIPE) == 0) { + if ((rp->flag & (RED_PIPE|RED_TWOWAY)) == 0) { (void) iop_close(iop); rp->iop = NULL; } @@ -1302,7 +1872,7 @@ NODE *tree; Func_ptr after_assign = NULL; NODE **lhs; - lhs = get_lhs(tree->lnode, &after_assign); + lhs = get_lhs(tree->lnode, &after_assign, FALSE); unref(*lhs); *lhs = make_string(s, cnt); (*lhs)->flags |= MAYBE_NUM; @@ -1317,8 +1887,7 @@ NODE *tree; /* pathopen --- pathopen with default file extension handling */ int -pathopen(file) -const char *file; +pathopen(const char *file) { int fd = do_pathopen(file); @@ -1351,8 +1920,7 @@ const char *file; /* do_pathopen --- search $AWKPATH for source file */ static int -do_pathopen(file) -const char *file; +do_pathopen(const char *file) { static const char *savepath = NULL; static int first = TRUE; @@ -1412,8 +1980,7 @@ const char *file; int bufsize = 8192; void -fatal(s) -char *s; +fatal(char *s) { printf("%s\n", s); exit(1); @@ -1423,10 +1990,7 @@ char *s; /* iop_alloc --- allocate an IOBUF structure for an open fd */ static IOBUF * -iop_alloc(fd, name, iop) -int fd; -const char *name; -IOBUF *iop; +iop_alloc(int fd, const char *name, IOBUF *iop) { struct stat sbuf; @@ -1439,63 +2003,16 @@ 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) - warning("data file `%s' is empty", name); + 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; - 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)) @@ -1524,12 +2041,11 @@ out: */ static int -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 */ +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; @@ -1540,11 +2056,21 @@ int *errcode; /* pointer to error variable */ 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__); } @@ -1554,14 +2080,21 @@ int *errcode; /* pointer to error variable */ 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[rs]; + rs = casetable[(unsigned char) rs]; /* set up sentinel */ if (iop->buf) { @@ -1611,6 +2144,10 @@ int *errcode; /* pointer to error variable */ } 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; @@ -1635,7 +2172,7 @@ int *errcode; /* pointer to error variable */ 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) { /* @@ -1687,6 +2224,14 @@ int *errcode; /* pointer to error variable */ * 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) { @@ -1715,20 +2260,34 @@ int *errcode; /* pointer to error variable */ /* case 3, regex match at exact end */ if (start + REEND(rsre, start) >= iop->end) { if (iop->cnt != EOF) { - bp = iop->end; - continuing = continued = TRUE; - continue; + /* + * 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! - * - * Is this code ever executed? */ - if (RS_is_null && RESTART(rsre, start) == 0) { - start += REEND(rsre, start); + 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); @@ -1739,10 +2298,10 @@ int *errcode; /* pointer to error variable */ } /* search for RS, #2, RS = <single char> */ if (onecase) { - while (casetable[(int) *bp++] != rs) + while (casetable[(unsigned char) *bp++] != rs && not_past_end()) continue; } else { - while (*bp++ != rs) + while (*bp++ != rs && not_past_end()) continue; } set_RT(bp - 1, 1); @@ -1764,15 +2323,12 @@ int *errcode; /* pointer to error variable */ } if (do_traditional || rsre == NULL) { - char *bstart; - - bstart = iop->off = bp; + iop->off = bp; bp--; - if (onecase ? casetable[(int) *bp] != rs : *bp != rs) { + if (onecase ? casetable[(unsigned char) *bp] != rs : *bp != rs) bp++; - bstart = bp; - } - *bp = '\0'; + if (MRL == 0) + *bp = '\0'; } else if (RS_is_null && iop->cnt == EOF) { /* * special case, delete trailing newlines, @@ -1789,9 +2345,7 @@ int *errcode; /* pointer to error variable */ #ifdef TEST int -main(argc, argv) -int argc; -char *argv[]; +main(int argc, char *argv[]) { IOBUF *iop; char *out; @@ -1812,145 +2366,6 @@ 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 @@ -1976,7 +2391,7 @@ set_RS() RS_regexp = make_regexp(RS->stptr, RS->stlen, IGNORECASE, TRUE); if (do_lint && ! warned) { - warning("multicharacter value of `RS' is not portable"); + lintwarn(_("multicharacter value of `RS' is a gawk extension")); warned = TRUE; } } |