diff --git a/bin/sh/miscbltin.c b/bin/sh/miscbltin.c --- a/bin/sh/miscbltin.c +++ b/bin/sh/miscbltin.c @@ -40,11 +40,14 @@ #include #include #include -#include + #include +#include +#include #include #include #include +#include #include "shell.h" #include "options.h" @@ -162,17 +165,18 @@ int is_ifs; int saveall = 0; ptrdiff_t lastnonifs, lastnonifsws; - struct timeval tv; - char *tvptr; - fd_set ifds; + sigset_t set, oset; + struct timespec timeout, tnow, tend; + struct pollfd pfd; + char *endptr; ssize_t nread; int sig; struct fdctx fdctx; rflag = 0; prompt = NULL; - tv.tv_sec = -1; - tv.tv_usec = 0; + timeout.tv_sec = -1; + timeout.tv_nsec = 0; while ((i = nextopt("erp:t:")) != '\0') { switch(i) { case 'p': @@ -184,22 +188,23 @@ rflag = 1; break; case 't': - tv.tv_sec = strtol(shoptarg, &tvptr, 0); - if (tvptr == shoptarg) + timeout.tv_sec = strtol(shoptarg, &endptr, 0); + if (endptr == shoptarg) error("timeout value"); - switch(*tvptr) { - case 0: + switch (*endptr) { case 's': + endptr++; break; case 'h': - tv.tv_sec *= 60; + timeout.tv_sec *= 60; /* FALLTHROUGH */ case 'm': - tv.tv_sec *= 60; + timeout.tv_sec *= 60; + endptr++; break; - default: - error("timeout unit"); } + if (*endptr != '\0') + error("timeout unit"); break; } } @@ -212,13 +217,33 @@ if ((ifs = bltinlookup("IFS", 1)) == NULL) ifs = " \t\n"; - if (tv.tv_sec >= 0) { + if (timeout.tv_sec >= 0) { /* * Wait for something to become available. */ - FD_ZERO(&ifds); - FD_SET(0, &ifds); - status = select(1, &ifds, NULL, NULL, &tv); + pfd.fd = STDIN_FILENO; + pfd.events = POLLIN; + status = sig = 0; + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, &oset); + if (pendingsig) { + /* caught a signal already */ + status = -1; + } else if (timeout.tv_sec == 0) { + status = poll(&pfd, 1, 0); + } else { + clock_gettime(CLOCK_UPTIME, &tnow); + tend = tnow; + tend.tv_sec += timeout.tv_sec; + do { + timespecsub(&tend, &tnow, &timeout); + status = ppoll(&pfd, 1, &timeout, &oset); + if (status >= 0 || pendingsig != 0) + break; + clock_gettime(CLOCK_UPTIME, &tnow); + } while (timespeccmp(&tnow, &tend, <)); + } + sigprocmask(SIG_SETMASK, &oset, NULL); /* * If there's nothing ready, return an error. */ diff --git a/bin/sh/tests/builtins/Makefile b/bin/sh/tests/builtins/Makefile --- a/bin/sh/tests/builtins/Makefile +++ b/bin/sh/tests/builtins/Makefile @@ -143,6 +143,7 @@ ${PACKAGE}FILES+= read9.0 ${PACKAGE}FILES+= read10.0 ${PACKAGE}FILES+= read11.0 +${PACKAGE}FILES+= read12.0 ${PACKAGE}FILES+= return1.0 ${PACKAGE}FILES+= return2.1 ${PACKAGE}FILES+= return3.1 diff --git a/bin/sh/tests/builtins/read12.0 b/bin/sh/tests/builtins/read12.0 new file mode 100644 --- /dev/null +++ b/bin/sh/tests/builtins/read12.0 @@ -0,0 +1,13 @@ + +set -e + +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) +trap 'rm -rf "$T"' 0 +cd $T +echo new_value >file1 +exec 3