diff options
Diffstat (limited to 'io.c')
-rw-r--r-- | io.c | 441 |
1 files changed, 313 insertions, 128 deletions
@@ -2,23 +2,23 @@ * io.c --- routines for dealing with input and output and records */ -/* +/* * Copyright (C) 1986, 1988, 1989, 1991-2016, * the Free Software Foundation, Inc. - * + * * This file is part of GAWK, the GNU implementation of the * AWK Programming Language. - * + * * GAWK is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. - * + * * GAWK is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA @@ -191,7 +191,7 @@ #define setsid() /* nothing */ #endif /* HAVE_SETSID */ -#if defined(GAWK_AIX) +#if defined(_AIX) #undef TANDEM /* AIX defines this in one of its header files */ #endif @@ -264,7 +264,6 @@ struct recmatch { static int iop_close(IOBUF *iop); -struct redirect *redirect(NODE *redir_exp, int redirtype, int *errflg); static void close_one(void); static int close_redir(struct redirect *rp, bool exitwarn, two_way_close_type how); #ifndef PIPES_SIMULATED @@ -275,7 +274,7 @@ static IOBUF *iop_alloc(int fd, const char *name, int errno_val); static IOBUF *iop_finish(IOBUF *iop); static int gawk_pclose(struct redirect *rp); static int str2mode(const char *mode); -static int two_way_open(const char *str, struct redirect *rp); +static int two_way_open(const char *str, struct redirect *rp, int extfd); static int pty_vs_pipe(const char *command); static void find_input_parser(IOBUF *iop); static bool find_output_wrapper(awk_output_buf_t *outbuf); @@ -323,6 +322,8 @@ static Regexp *RS_re_yes_case; /* regexp for RS when ignoring case */ static Regexp *RS_re_no_case; /* regexp for RS when not ignoring case */ static Regexp *RS_regexp; +static const char nonfatal[] = "NONFATAL"; + bool RS_is_null; extern NODE *ARGC_node; @@ -356,7 +357,7 @@ init_io() * PROCINFO entries for timeout are dynamic; * We can't be any more specific than this. */ - if (PROCINFO_node != NULL) + if (PROCINFO_node != NULL) read_can_timeout = true; } @@ -416,7 +417,7 @@ after_beginfile(IOBUF **curfile) bool valid; fname = iop->public.name; - errcode = iop->errcode; + errcode = iop->errcode; valid = iop->valid; errno = 0; update_ERRNO_int(errcode); @@ -465,12 +466,12 @@ nextfile(IOBUF **curfile, bool skipping) (void) iop_close(iop); *curfile = NULL; return 1; /* run endfile block */ - } else + } else return 0; } argc = get_number_si(ARGC_node->var_value); - + for (; i < argc; i++) { tmp = make_number((AWKNUM) i); (void) force_string(tmp); @@ -596,10 +597,11 @@ inrec(IOBUF *iop, int *errcode) cnt = EOF; else if ((iop->flag & IOP_CLOSED) != 0) cnt = EOF; - else + else cnt = get_a_record(& begin, iop, errcode); - if (cnt == EOF) { + /* Note that get_a_record may return -2 when I/O would block */ + if (cnt < 0) { retval = false; } else { INCREMENT_REC(NR); @@ -727,13 +729,13 @@ redflags2str(int flags) return genflags2str(flags, redtab); } -/* redirect --- Redirection for printf and print commands */ +/* redirect_string --- Redirection for printf and print commands, use string info */ struct redirect * -redirect(NODE *redir_exp, int redirtype, int *errflg) +redirect_string(const char *str, size_t explen, bool not_string, + int redirtype, int *errflg, int extfd, bool failure_fatal) { struct redirect *rp; - char *str; int tflag = 0; int outflag = 0; const char *direction = "to"; @@ -782,18 +784,16 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) default: cant_happen(); } - if (do_lint && (redir_exp->flags & STRCUR) == 0) - lintwarn(_("expression in `%s' redirection only has numeric value"), + if (do_lint && not_string) + lintwarn(_("expression in `%s' redirection is a number"), what); - redir_exp = force_string(redir_exp); - str = redir_exp->stptr; if (str == NULL || *str == '\0') fatal(_("expression for `%s' redirection has null string value"), what); - if (do_lint && (strncmp(str, "0", redir_exp->stlen) == 0 - || strncmp(str, "1", redir_exp->stlen) == 0)) + if (do_lint && (strncmp(str, "0", explen) == 0 + || strncmp(str, "1", explen) == 0)) lintwarn(_("filename `%s' for `%s' redirection may be result of logical expression"), str, what); @@ -831,8 +831,8 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) #endif /* PIPES_SIMULATED */ /* now check for a match */ - if (strlen(rp->value) == redir_exp->stlen - && memcmp(rp->value, str, redir_exp->stlen) == 0 + if (strlen(rp->value) == explen + && memcmp(rp->value, str, explen) == 0 && ((rp->flag & ~(RED_NOBUF|RED_EOF|RED_PTY)) == tflag || (outflag != 0 && (rp->flag & (RED_FILE|RED_WRITE)) == outflag))) { @@ -843,23 +843,25 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) if (do_lint && rpflag != newflag) lintwarn( _("unnecessary mixing of `>' and `>>' for file `%.*s'"), - (int) redir_exp->stlen, rp->value); + (int) explen, rp->value); break; } } if (rp == NULL) { + char *newstr; new_rp = true; if (save_rp != NULL) { rp = save_rp; efree(rp->value); } else emalloc(rp, struct redirect *, sizeof(struct redirect), "redirect"); - emalloc(str, char *, redir_exp->stlen + 1, "redirect"); - memcpy(str, redir_exp->stptr, redir_exp->stlen); - str[redir_exp->stlen] = '\0'; - rp->value = str; + emalloc(newstr, char *, explen + 1, "redirect"); + memcpy(newstr, str, explen); + newstr[explen] = '\0'; + str = newstr; + rp->value = newstr; rp->flag = tflag; init_output_wrapper(& rp->output); rp->output.name = str; @@ -891,6 +893,10 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) mode = binmode("a"); break; case redirect_pipe: + if (extfd >= 0) { + warning(_("get_file cannot create pipe `%s' with fd %d"), str, extfd); + return NULL; + } /* synchronize output before new pipe */ (void) flush_io(); @@ -898,6 +904,12 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) #ifdef SIGPIPE signal(SIGPIPE, SIG_DFL); #endif + /* + * Don't check failure_fatal; see input pipe below. + * Note that the failure happens upon failure to fork, + * using a non-existant program will still succeed the + * popen(). + */ if ((rp->output.fp = popen(str, binmode("w"))) == NULL) fatal(_("can't open pipe `%s' for output (%s)"), str, strerror(errno)); @@ -910,6 +922,10 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) rp->flag |= RED_NOBUF; break; case redirect_pipein: + if (extfd >= 0) { + warning(_("get_file cannot create pipe `%s' with fd %d"), str, extfd); + return NULL; + } direction = "from"; if (gawk_popen(str, rp) == NULL) fatal(_("can't open pipe `%s' for input (%s)"), @@ -917,7 +933,7 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) break; case redirect_input: direction = "from"; - fd = devopen(str, binmode("r")); + fd = (extfd >= 0) ? extfd : devopen(str, binmode("r")); if (fd == INVALID_HANDLE && errno == EISDIR) { *errflg = EISDIR; /* do not free rp, saving it for reuse (save_rp = rp) */ @@ -934,15 +950,19 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) } break; case redirect_twoway: +#ifndef HAVE_SOCKETS + if (extfd >= 0) { + warning(_("get_file socket creation not supported on this platform for `%s' with fd %d"), str, extfd); + return NULL; + } +#endif direction = "to/from"; - if (! two_way_open(str, rp)) { -#ifdef HAVE_SOCKETS - if (inetfile(str, NULL)) { + if (! two_way_open(str, rp, extfd)) { + if (! failure_fatal || is_non_fatal_redirect(str)) { *errflg = errno; /* do not free rp, saving it for reuse (save_rp = rp) */ return NULL; } else -#endif fatal(_("can't open two way pipe `%s' for input/output (%s)"), str, strerror(errno)); } @@ -954,7 +974,7 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) if (mode != NULL) { errno = 0; rp->output.mode = mode; - fd = devopen(str, mode); + fd = (extfd >= 0) ? extfd : devopen(str, mode); if (fd > INVALID_HANDLE) { if (fd == fileno(stdin)) @@ -1002,7 +1022,7 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) #ifdef VMS /* Alpha/VMS V7.1+ C RTL is returning these instead of EMFILE (haven't tried other post-V6.2 systems) */ - else if ((errno == EIO || errno == EVMSERR) && + else if ((errno == EIO || errno == EVMSERR) && (vaxc$errno == SS$_EXQUOTA || vaxc$errno == SS$_EXBYTLM || vaxc$errno == RMS$_ACC || @@ -1020,11 +1040,14 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) * can return -1. For output to file, * complain. The shell will complain on * a bad command to a pipe. + * + * 12/2014: Take nonfatal settings in PROCINFO into account. */ if (errflg != NULL) *errflg = errno; - if ( redirtype == redirect_output - || redirtype == redirect_append) { + if (failure_fatal && ! is_non_fatal_redirect(str) && + (redirtype == redirect_output + || redirtype == redirect_append)) { /* multiple messages make life easier for translators */ if (*direction == 'f') fatal(_("can't redirect from `%s' (%s)"), @@ -1055,6 +1078,18 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) return rp; } +/* redirect --- Redirection for printf and print commands */ + +struct redirect * +redirect(NODE *redir_exp, int redirtype, int *errflg, bool failure_fatal) +{ + bool not_string = ((fixtype(redir_exp)->flags & STRING) == 0); + + redir_exp = force_string(redir_exp); + return redirect_string(redir_exp->stptr, redir_exp->stlen, not_string, + redirtype, errflg, -1, failure_fatal); +} + /* getredirect --- find the struct redirect for this file or pipe */ struct redirect * @@ -1069,6 +1104,34 @@ getredirect(const char *str, int len) return NULL; } +/* is_non_fatal_std --- return true if fp is stdout/stderr and nonfatal */ + +bool +is_non_fatal_std(FILE *fp) +{ + if (in_PROCINFO(nonfatal, NULL, NULL)) + return true; + + /* yucky logic. sigh. */ + if (fp == stdout) { + return ( in_PROCINFO("-", nonfatal, NULL) != NULL + || in_PROCINFO("/dev/stdout", nonfatal, NULL) != NULL); + } else if (fp == stderr) { + return (in_PROCINFO("/dev/stderr", nonfatal, NULL) != NULL); + } + + return false; +} + +/* is_non_fatal_redirect --- return true if redirected I/O should be nonfatal */ + +bool +is_non_fatal_redirect(const char *str) +{ + return in_PROCINFO(nonfatal, NULL, NULL) != NULL + || in_PROCINFO(str, nonfatal, NULL) != NULL; +} + /* close_one --- temporarily close an open file to re-use the fd */ static void @@ -1105,7 +1168,7 @@ close_one() } 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 */ @@ -1164,7 +1227,8 @@ do_close(int nargs) * POSIX says close() returns 0 on success, non-zero otherwise. * For POSIX, at this point we just return 0. Otherwise we * return the exit status of the process or of pclose(), depending. - * This whole business is a mess. + * Down in the call tree of close_redir(), we rationalize the + * value like we do for system(). */ if (do_posix) { unref(tmp); @@ -1206,13 +1270,14 @@ close_rp(struct redirect *rp, two_way_close_type how) #endif /* HAVE_SOCKETS */ (void) iop_close(rp->iop); } else + /* status already sanitized */ 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->output.fp); + status = sanitize_exit_status(pclose(rp->output.fp)); if ((BINMODE & BINMODE_INPUT) != 0) os_setbinmode(fileno(stdin), O_BINARY); @@ -1442,7 +1507,7 @@ str2mode(const char *mode) static int socketopen(int family, int type, const char *localpname, - const char *remotepname, const char *remotehostname) + const char *remotepname, const char *remotehostname, bool *hard_error) { struct addrinfo *lres, *lres0; struct addrinfo lhints; @@ -1461,8 +1526,11 @@ socketopen(int family, int type, const char *localpname, lerror = getaddrinfo(NULL, localpname, & lhints, & lres); if (lerror) { - if (strcmp(localpname, "0") != 0) - fatal(_("local port %s invalid in `/inet'"), localpname); + if (strcmp(localpname, "0") != 0) { + warning(_("local port %s invalid in `/inet'"), localpname); + *hard_error = true; + return INVALID_HANDLE; + } lres0 = NULL; lres = & lhints; } else @@ -1480,7 +1548,9 @@ socketopen(int family, int type, const char *localpname, if (rerror) { if (lres0 != NULL) freeaddrinfo(lres0); - fatal(_("remote host and port information (%s, %s) invalid"), remotehostname, remotepname); + warning(_("remote host and port information (%s, %s) invalid"), remotehostname, remotepname); + *hard_error = true; + return INVALID_HANDLE; } rres0 = rres; socket_fd = INVALID_HANDLE; @@ -1646,6 +1716,7 @@ devopen(const char *name, const char *mode) char *cp; int flag; struct inet_socket_info isi; + int save_errno = 0; openfd = devopen_simple(name, mode, false); if (openfd != INVALID_HANDLE) @@ -1657,6 +1728,14 @@ devopen(const char *name, const char *mode) goto strictopen; } else if (inetfile(name, & isi)) { #ifdef HAVE_SOCKETS +#define DEFAULT_RETRIES 20 + static unsigned long def_retries = DEFAULT_RETRIES; + static bool first_time = true; + unsigned long retries = 0; + static long msleep = 1000; + bool hard_error = false; + bool non_fatal = is_non_fatal_redirect(name); + cp = (char *) name; /* socketopen requires NUL-terminated strings */ @@ -1664,18 +1743,11 @@ devopen(const char *name, const char *mode) cp[isi.remotehost.offset+isi.remotehost.len] = '\0'; /* remoteport comes last, so already NUL-terminated */ - { -#define DEFAULT_RETRIES 20 - static unsigned long def_retries = DEFAULT_RETRIES; - static bool first_time = true; - unsigned long retries = 0; - static long msleep = 1000; - if (first_time) { char *cp, *end; unsigned long count = 0; char *ms2; - + first_time = false; if ((cp = getenv("GAWK_SOCK_RETRIES")) != NULL) { count = strtoul(cp, & end, 10); @@ -1696,25 +1768,41 @@ devopen(const char *name, const char *mode) msleep *= 1000; } } - retries = def_retries; + /* + * PROCINFO["NONFATAL"] or PROCINFO[name, "NONFATAL"] overrrides + * GAWK_SOCK_RETRIES. The explicit code in the program carries + * a bigger stick than the environment variable does. + */ + retries = non_fatal ? 1 : def_retries; + errno = 0; do { - openfd = socketopen(isi.family, isi.protocol, name+isi.localport.offset, name+isi.remoteport.offset, name+isi.remotehost.offset); + openfd = socketopen(isi.family, isi.protocol, name+isi.localport.offset, + name+isi.remoteport.offset, name+isi.remotehost.offset, + & hard_error); retries--; - } while (openfd == INVALID_HANDLE && retries > 0 && usleep(msleep) == 0); - } + } while (openfd == INVALID_HANDLE && ! hard_error && retries > 0 && usleep(msleep) == 0); + save_errno = errno; - /* restore original name string */ - cp[isi.localport.offset+isi.localport.len] = '/'; - cp[isi.remotehost.offset+isi.remotehost.len] = '/'; + /* restore original name string */ + cp[isi.localport.offset+isi.localport.len] = '/'; + cp[isi.remotehost.offset+isi.remotehost.len] = '/'; #else /* ! HAVE_SOCKETS */ - fatal(_("TCP/IP communications are not supported")); + fatal(_("TCP/IP communications are not supported")); #endif /* HAVE_SOCKETS */ } strictopen: - if (openfd == INVALID_HANDLE) + if (openfd == INVALID_HANDLE) { openfd = open(name, flag, 0666); + /* + * ENOENT means there is no such name in the filesystem. + * Therefore it's ok to propagate up the error from + * getaddrinfo() that's in save_errno. + */ + if (openfd == INVALID_HANDLE && errno == ENOENT && save_errno) + errno = save_errno; + } #if defined(__EMX__) || defined(__MINGW32__) if (openfd == INVALID_HANDLE && errno == EACCES) { /* On OS/2 and Windows directory access via open() is @@ -1737,16 +1825,16 @@ strictopen: /* two_way_open --- open a two way communications channel */ static int -two_way_open(const char *str, struct redirect *rp) +two_way_open(const char *str, struct redirect *rp, int extfd) { static bool no_ptys = false; #ifdef HAVE_SOCKETS /* case 1: socket */ - if (inetfile(str, NULL)) { + if (extfd >= 0 || inetfile(str, NULL)) { int fd, newfd; - fd = devopen(str, "rw"); + fd = (extfd >= 0) ? extfd : devopen(str, "rw"); if (fd == INVALID_HANDLE) return false; if ((BINMODE & BINMODE_OUTPUT) != 0) @@ -1798,7 +1886,7 @@ two_way_open(const char *str, struct redirect *rp) char c; int master, dup_master; int slave; - int save_errno; + int save_errno; pid_t pid; struct stat statb; struct termios st; @@ -2053,7 +2141,7 @@ use_pipes: #if defined(__EMX__) || defined(__MINGW32__) save_stdin = dup(0); /* duplicate stdin */ save_stdout = dup(1); /* duplicate stdout */ - + if (save_stdout == -1 || save_stdin == -1) { /* if an error occurs close all open file handles */ save_errno = errno; @@ -2066,7 +2154,7 @@ use_pipes: errno = save_errno; return false; } - + /* connect pipes to stdin and stdout */ close(1); /* close stdout */ if (dup(ctop[1]) != 1) { /* connect pipe input to stdout */ @@ -2086,7 +2174,7 @@ use_pipes: /* none of these handles must be inherited by the child process */ (void) close(ptoc[0]); /* close pipe output, child will use stdin instead */ (void) close(ctop[1]); /* close pipe input, child will use stdout instead */ - + os_close_on_exec(ptoc[1], str, "pipe", "from"); /* pipe input: output of the parent process */ os_close_on_exec(ctop[0], str, "pipe", "from"); /* pipe output: input of the parent process */ os_close_on_exec(save_stdin, str, "pipe", "from"); /* saved stdin of the parent process */ @@ -2100,7 +2188,7 @@ use_pipes: qcmd = quote_cmd(str), NULL); efree(qcmd); #endif - + /* restore stdin and stdout */ close(1); if (dup(save_stdout) != 1) { @@ -2109,7 +2197,7 @@ use_pipes: fatal(_("restoring stdout in parent process failed\n")); } close(save_stdout); - + close(0); if (dup(save_stdin) != 0) { close(save_stdin); @@ -2135,7 +2223,7 @@ use_pipes: errno = save_errno; return false; } - + if (pid == 0) { /* child */ if (close(1) == -1) fatal(_("close of stdout in child failed (%s)"), @@ -2217,17 +2305,43 @@ use_pipes: #ifndef PIPES_SIMULATED /* real pipes */ -/* wait_any --- wait for a child process, close associated pipe */ +/* + * wait_any --- if the argument pid is 0, wait for all child processes that + * have exited. We loop to make sure to reap all children that have exited to + * minimize the risk of running out of process slots. Since we don't process + * SIGCHLD, we do not immediately reap exited children. So when we get here, + * we want to reap any that have piled up. + * + * Note: on platforms that do not support waitpid with WNOHANG, when called with + * a zero argument, this function will hang until all children have exited. + * + * AJS, 2013-07-07: I do not see why we need to ignore signals during this + * function. This function just waits and updates the pid and status fields. + * I don't see why that should interfere with any signal handlers. But I am + * reluctant to remove this protection. So I changed to use sigprocmask to + * block signals instead to avoid interfering with installed signal handlers. + */ static int wait_any(int interesting) /* pid of interest, if any */ { - RETSIGTYPE (*hstat)(int), (*istat)(int), (*qstat)(int); int pid; int status = 0; struct redirect *redp; +#ifdef HAVE_SIGPROCMASK + sigset_t set, oldset; + + /* I have no idea why we are blocking signals during this function... */ + sigemptyset(& set); + sigaddset(& set, SIGINT); + sigaddset(& set, SIGHUP); + sigaddset(& set, SIGQUIT); + sigprocmask(SIG_BLOCK, & set, & oldset); +#else + void (*hstat)(int), (*istat)(int), (*qstat)(int); istat = signal(SIGINT, SIG_IGN); +#endif #ifdef __MINGW32__ if (interesting < 0) { status = -1; @@ -2243,11 +2357,22 @@ wait_any(int interesting) /* pid of interest, if any */ break; } } -#else +#else /* ! __MINGW32__ */ +#ifndef HAVE_SIGPROCMASK hstat = signal(SIGHUP, SIG_IGN); qstat = signal(SIGQUIT, SIG_IGN); +#endif for (;;) { -# ifdef HAVE_SYS_WAIT_H /* POSIX compatible sys/wait.h */ +# if defined(HAVE_WAITPID) && defined(WNOHANG) + /* + * N.B. If the caller wants status for a specific child process + * (i.e. interesting is non-zero), then we must hang until we + * get exit status for that child. + */ + if ((pid = waitpid(-1, & status, (interesting ? 0 : WNOHANG))) == 0) + /* No children have exited */ + break; +# elif defined(HAVE_SYS_WAIT_H) /* POSIX compatible sys/wait.h */ pid = wait(& status); # else pid = wait((union wait *) & status); @@ -2265,10 +2390,16 @@ wait_any(int interesting) /* pid of interest, if any */ if (pid == -1 && errno == ECHILD) break; } +#ifndef HAVE_SIGPROCMASK signal(SIGHUP, hstat); signal(SIGQUIT, qstat); #endif +#endif /* ! __MINGW32__ */ +#ifndef HAVE_SIGPROCMASK signal(SIGINT, istat); +#else + sigprocmask(SIG_SETMASK, & oldset, NULL); +#endif return status; } @@ -2313,10 +2444,10 @@ gawk_popen(const char *cmd, struct redirect *rp) fatal(_("moving pipe to stdout in child failed (dup: %s)"), strerror(errno)); } - + /* none of these handles must be inherited by the child process */ close(p[1]); /* close pipe input */ - + os_close_on_exec(p[0], cmd, "pipe", "from"); /* pipe output: input of the parent process */ os_close_on_exec(save_stdout, cmd, "pipe", "from"); /* saved stdout of the parent process */ @@ -2327,7 +2458,7 @@ gawk_popen(const char *cmd, struct redirect *rp) qcmd = quote_cmd(cmd), NULL); efree(qcmd); #endif - + /* restore stdout */ close(1); if (dup(save_stdout) != 1) { @@ -2390,7 +2521,7 @@ gawk_pclose(struct redirect *rp) /* process previously found, return stored status */ if (rp->pid == -1) return rp->status; - rp->status = wait_any(rp->pid); + rp->status = sanitize_exit_status(wait_any(rp->pid)); rp->pid = -1; return rp->status; } @@ -2479,7 +2610,7 @@ do_getline_redir(int into_variable, enum redirval redirtype) assert(redirtype != redirect_none); redir_exp = TOP(); - rp = redirect(redir_exp, redirtype, & redir_error); + rp = redirect(redir_exp, redirtype, & redir_error, false); DEREF(redir_exp); decr_sp(); if (rp == NULL) { @@ -2489,6 +2620,10 @@ do_getline_redir(int into_variable, enum redirval redirtype) } return make_number((AWKNUM) -1.0); } else if ((rp->flag & RED_TWOWAY) != 0 && rp->iop == NULL) { + if (is_non_fatal_redirect(redir_exp->stptr)) { + update_ERRNO_int(EBADF); + return make_number((AWKNUM) -1.0); + } (void) close_rp(rp, CLOSE_ALL); fatal(_("getline: attempt to read from closed read end of two-way pipe")); } @@ -2501,7 +2636,7 @@ do_getline_redir(int into_variable, enum redirval redirtype) if (errcode != 0) { if (! do_traditional && (errcode != -1)) update_ERRNO_int(errcode); - return make_number((AWKNUM) -1.0); + return make_number((AWKNUM) cnt); } if (cnt == EOF) { @@ -2551,7 +2686,7 @@ do_getline(int into_variable, IOBUF *iop) update_ERRNO_int(errcode); if (into_variable) (void) POP_ADDRESS(); - return make_number((AWKNUM) -1.0); + return make_number((AWKNUM) cnt); } if (cnt == EOF) @@ -2574,8 +2709,8 @@ do_getline(int into_variable, IOBUF *iop) typedef struct { const char *envname; char **dfltp; /* pointer to address of default path */ - char **awkpath; /* array containing library search paths */ - int max_pathlen; /* length of the longest item in awkpath */ + char **awkpath; /* array containing library search paths */ + int max_pathlen; /* length of the longest item in awkpath */ } path_info; static path_info pi_awkpath = { @@ -2659,7 +2794,7 @@ init_awkpath(path_info *pi) pi->awkpath[i] = NULL; } -/* do_find_source --- search $AWKPATH for file, return NULL if not found */ +/* do_find_source --- search $AWKPATH for file, return NULL if not found */ static char * do_find_source(const char *src, struct stat *stb, int *errcode, path_info *pi) @@ -2683,7 +2818,7 @@ do_find_source(const char *src, struct stat *stb, int *errcode, path_info *pi) if (pi->awkpath == NULL) init_awkpath(pi); - emalloc(path, char *, pi->max_pathlen + strlen(src) + 1, "do_find_source"); + emalloc(path, char *, pi->max_pathlen + strlen(src) + 1, "do_find_source"); for (i = 0; pi->awkpath[i] != NULL; i++) { if (strcmp(pi->awkpath[i], "./") == 0 || strcmp(pi->awkpath[i], ".") == 0) *path = '\0'; @@ -2700,7 +2835,7 @@ do_find_source(const char *src, struct stat *stb, int *errcode, path_info *pi) return NULL; } -/* find_source --- find source file with default file extension handling */ +/* find_source --- find source file with default file extension handling */ char * find_source(const char *src, struct stat *stb, int *errcode, int is_extlib) @@ -3085,7 +3220,7 @@ iop_finish(IOBUF *iop) lintwarn(_("data file `%s' is empty"), iop->public.name); iop->errcode = errno = 0; iop->count = iop->scanoff = 0; - emalloc(iop->buf, char *, iop->size += 2, "iop_finish"); + emalloc(iop->buf, char *, iop->size += 1, "iop_finish"); iop->off = iop->buf; iop->dataend = NULL; iop->end = iop->buf + iop->size; @@ -3117,10 +3252,10 @@ grow_iop_buffer(IOBUF *iop) size_t newsize; /* - * Lop off original extra two bytes, double the size, - * add them back. + * Lop off original extra byte, double the size, + * add it back. */ - newsize = ((iop->size - 2) * 2) + 2; + newsize = ((iop->size - 1) * 2) + 1; /* Check for overflow */ if (newsize <= iop->size) @@ -3128,7 +3263,7 @@ grow_iop_buffer(IOBUF *iop) /* Make sure there's room for a disk block */ if (newsize - valid < iop->readsize) - newsize += iop->readsize + 2; + newsize += iop->readsize + 1; /* Check for overflow, again */ if (newsize <= iop->size) @@ -3168,51 +3303,51 @@ rs1scan(IOBUF *iop, struct recmatch *recm, SCANSTATE *state) * Subject: Re: multibyte locales: any way to find if a character isn't multibyte? * Date: Mon, 23 Jun 2003 12:20:16 +0200 * Cc: isamu@yamato.ibm.com - * + * * Hi, - * + * * > Is there any way to make the following query to the current locale? * > * > Given an 8-bit value, can this value ever appear as part of * > a multibyte character? - * + * * There is no simple answer here. The easiest solution I see is to * get the current locale's codeset (via locale_charset() which is a * wrapper around nl_langinfo(CODESET)), and then perform a case-by-case * treatment of the known multibyte encodings, from GB2312 to EUC-JISX0213; * for the unibyte encodings, a single btowc() call will tell you. - * + * * > This is particularly critical for me for ASCII newline ('\n'). If I * > can be guaranteed that it never shows up as part of a multibyte character, * > I can speed up gawk considerably in mulitbyte locales. - * + * * This is much simpler to answer! * In all ASCII based multibyte encodings used for locales today (this * excludes EBCDIC based doublebyte encodings from IBM, and also excludes * ISO-2022-JP which is used for email exchange but not as a locale encoding) * ALL bytes in the range 0x00..0x2F occur only as a single character, not * as part of a multibyte character. - * + * * So it's safe to assume, but deserves a comment in the source. - * + * * Bruno *************************************************************** * From: Bruno Haible <bruno@clisp.org> * To: Aharon Robbins <arnold@skeeve.com> * Subject: Re: multibyte locales: any way to find if a character isn't multibyte? * Date: Mon, 23 Jun 2003 14:27:49 +0200 - * + * * On Monday 23 June 2003 14:11, you wrote: - * + * * > if (rs != '\n' && MB_CUR_MAX > 1) { - * + * * If you assume ASCII, you can even write - * + * * if (rs >= 0x30 && MB_CUR_MAX > 1) { - * + * * (this catches also the space character) but if portability to EBCDIC * systems is desired, your code is fine as is. - * + * * Bruno */ /* Thus, the check for \n here; big speedup ! */ @@ -3460,10 +3595,45 @@ find_longest_terminator: return REC_OK; } +/* retryable --- return true if PROCINFO[<filename>, "RETRY"] exists */ + +static inline int +retryable(IOBUF *iop) +{ + return PROCINFO_node && in_PROCINFO(iop->public.name, "RETRY", NULL); +} + +/* errno_io_retry --- Does the I/O error indicate that the operation should be retried later? */ + +static inline int +errno_io_retry(void) +{ + switch (errno) { +#ifdef EAGAIN + case EAGAIN: +#endif +#ifdef EWOULDBLOCK +#if !defined(EAGAIN) || (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: +#endif +#endif +#ifdef EINTR + case EINTR: +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: +#endif + return 1; + default: + return 0; + } +} + /* * get_a_record --- read a record from IOP into out, * return length of EOF, set RT. * Note that errcode is never NULL, and the caller initializes *errcode to 0. + * If I/O would block, return -2. */ static int @@ -3507,8 +3677,10 @@ get_a_record(char **out, /* pointer to pointer to data */ iop->flag |= IOP_AT_EOF; return EOF; } else if (iop->count == -1) { - iop->flag |= IOP_AT_EOF; *errcode = errno; + if (errno_io_retry() && retryable(iop)) + return -2; + iop->flag |= IOP_AT_EOF; return EOF; } else { iop->dataend = iop->buf + iop->count; @@ -3582,6 +3754,8 @@ get_a_record(char **out, /* pointer to pointer to data */ iop->count = iop->public.read_func(iop->public.fd, iop->dataend, amt_to_read); if (iop->count == -1) { *errcode = errno; + if (errno_io_retry() && retryable(iop)) + return -2; iop->flag |= IOP_AT_EOF; break; } else if (iop->count == 0) { @@ -3694,7 +3868,7 @@ set_RS() * in case of fatal error in make_regexp. */ refree(RS_re_yes_case); /* NULL argument is ok */ - refree(RS_re_no_case); + refree(RS_re_no_case); RS_re_yes_case = RS_re_no_case = RS_regexp = NULL; if (RS->stlen == 0) { @@ -3733,17 +3907,13 @@ pty_vs_pipe(const char *command) #ifdef HAVE_TERMIOS_H NODE *val; - if (PROCINFO_node == NULL) - return false; + /* + * N.B. No need to check for NULL PROCINFO_node, since the + * in_PROCINFO function now checks that for us. + */ val = in_PROCINFO(command, "pty", NULL); - if (val) { - if ((val->flags & MAYBE_NUM) != 0) - (void) force_number(val); - if ((val->flags & NUMBER) != 0) - return ! iszero(val); - else - return (val->stlen != 0); - } + if (val) + return boolval(val); #endif /* HAVE_TERMIOS_H */ return false; } @@ -3823,9 +3993,9 @@ inetfile(const char *str, struct inet_socket_info *isi) isi->localport.offset = cp-str; while (*cp != '/' && *cp != '\0') cp++; - /* + /* * Require a port, let them explicitly put 0 if - * they don't care. + * they don't care. */ if (*cp != '/' || ((isi->localport.len = (cp-str)-isi->localport.offset) == 0)) return false; @@ -3834,7 +4004,7 @@ inetfile(const char *str, struct inet_socket_info *isi) cp++; isi->remotehost.offset = cp-str; while (*cp != '/' && *cp != '\0') - cp++; + cp++; if (*cp != '/' || ((isi->remotehost.len = (cp-str)-isi->remotehost.offset) == 0)) return false; @@ -3850,7 +4020,7 @@ inetfile(const char *str, struct inet_socket_info *isi) */ isi->remoteport.offset = cp-str; while (*cp != '/' && *cp != '\0') - cp++; + cp++; if (*cp != '\0' || ((isi->remoteport.len = (cp-str)-isi->remoteport.offset) == 0)) return false; @@ -3866,22 +4036,31 @@ inetfile(const char *str, struct inet_socket_info *isi) /* * in_PROCINFO --- return value for a PROCINFO element with * SUBSEP seperated indices. - */ + */ static NODE * in_PROCINFO(const char *pidx1, const char *pidx2, NODE **full_idx) { char *str; size_t str_len; - NODE *r, *sub = NULL; + NODE *r, *sub = NULL; NODE *subsep = SUBSEP_node->var_value; + if (PROCINFO_node == NULL || (pidx1 == NULL && pidx2 == NULL)) + return NULL; + /* full_idx is in+out parameter */ if (full_idx) sub = *full_idx; - str_len = strlen(pidx1) + subsep->stlen + strlen(pidx2); + if (pidx1 != NULL && pidx2 == NULL) + str_len = strlen(pidx1); + else if (pidx1 == NULL && pidx2 != NULL) + str_len = strlen(pidx2); + else + str_len = strlen(pidx1) + subsep->stlen + strlen(pidx2); + if (sub == NULL) { emalloc(str, char *, str_len + 1, "in_PROCINFO"); sub = make_str_node(str, str_len, ALREADY_MALLOCED); @@ -3895,8 +4074,14 @@ in_PROCINFO(const char *pidx1, const char *pidx2, NODE **full_idx) sub->stlen = str_len; } - sprintf(sub->stptr, "%s%.*s%s", pidx1, (int)subsep->stlen, - subsep->stptr, pidx2); + if (pidx1 != NULL && pidx2 == NULL) + strcpy(sub->stptr, pidx1); + else if (pidx1 == NULL && pidx2 != NULL) + strcpy(sub->stptr, pidx2); + else + sprintf(sub->stptr, "%s%.*s%s", pidx1, (int)subsep->stlen, + subsep->stptr, pidx2); + r = in_array(PROCINFO_node, sub); if (! full_idx) unref(sub); |