diff options
Diffstat (limited to 'io.c')
-rw-r--r-- | io.c | 118 |
1 files changed, 98 insertions, 20 deletions
@@ -588,7 +588,8 @@ inrec(IOBUF *iop, int *errcode) 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); @@ -719,10 +720,9 @@ redflags2str(int flags) /* redirect --- Redirection for printf and print commands */ struct redirect * -redirect(NODE *redir_exp, int redirtype, int *errflg) +redirect_string(char *str, size_t explen, int not_string, int redirtype, int *errflg) { struct redirect *rp; - char *str; int tflag = 0; int outflag = 0; const char *direction = "to"; @@ -771,18 +771,16 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) default: cant_happen(); } - if (do_lint && (redir_exp->flags & STRCUR) == 0) + if (do_lint && not_string) lintwarn(_("expression in `%s' redirection only has numeric value"), 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); @@ -820,8 +818,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))) { @@ -832,22 +830,24 @@ 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'; + emalloc(newstr, char *, explen + 1, "redirect"); + memcpy(newstr, str, explen); + newstr[explen] = '\0'; + str = newstr; rp->value = str; rp->flag = tflag; init_output_wrapper(& rp->output); @@ -1036,6 +1036,15 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) return rp; } +struct redirect * +redirect(NODE *redir_exp, int redirtype, int *errflg) +{ + int not_string = ((redir_exp->flags & STRCUR) == 0); + redir_exp = force_string(redir_exp); + return redirect_string(redir_exp->stptr, redir_exp->stlen, not_string, + redirtype, errflg); +} + /* getredirect --- find the struct redirect for this file or pipe */ struct redirect * @@ -2160,17 +2169,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 + RETSIGTYPE (*hstat)(int), (*istat)(int), (*qstat)(int); istat = signal(SIGINT, SIG_IGN); +#endif #ifdef __MINGW32__ if (interesting < 0) { status = -1; @@ -2187,10 +2222,16 @@ wait_any(int interesting) /* pid of interest, if any */ } } #else +#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) + if ((pid = waitpid(-1, & status, 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); @@ -2208,10 +2249,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 +#ifndef HAVE_SIGPROCMASK signal(SIGINT, istat); +#else + sigprocmask(SIG_SETMASK, & oldset, NULL); +#endif return status; } @@ -2432,7 +2479,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) { @@ -2482,7 +2529,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) @@ -3378,10 +3425,37 @@ find_longest_terminator: return REC_OK; } +/* 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 @@ -3425,8 +3499,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()) + return -2; + iop->flag |= IOP_AT_EOF; return EOF; } else { iop->dataend = iop->buf + iop->count; @@ -3500,6 +3576,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()) + return -2; iop->flag |= IOP_AT_EOF; break; } else if (iop->count == 0) { |