shell: optional support for read -t N.NNN, closes 10101

function                                             old     new   delta
shell_builtin_read                                  1097    1277    +180
dump_procs                                           353     359      +6

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-07-20 16:09:31 +02:00
parent 82dcc3bff9
commit eae12688c9
6 changed files with 97 additions and 34 deletions

View File

@ -145,6 +145,13 @@ config FEATURE_SH_NOFORK
This feature is relatively new. Use with care. Report bugs
to project mailing list.
config FEATURE_SH_READ_FRAC
bool "read -t N.NNN support (+110 bytes)"
default y
depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH
help
Enable support for fractional second timeout in read builtin.
config FEATURE_SH_HISTFILESIZE
bool "Use $HISTFILESIZE"
default y

View File

@ -0,0 +1,3 @@
><[0]
><[0]
><[1]

View File

@ -0,0 +1,14 @@
# ><[0]
echo Ok | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; }
# This would not be deterministic: returns 0 "data exists" if EOF is seen
# (true terminated) - because EOF is considered to be data (read will not block),
# else returns 1 "no data".
## ><[????]
#true | { read -t 0 reply; echo ">$reply<[$?]"; }
# ><[0]
true | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; }
# ><[1]
sleep 0.2 | { read -p IGNORED_PROMPT -t 0 reply; echo ">$reply<[$?]"; }

View File

@ -0,0 +1,3 @@
><[0]
><[0]
><[1]

View File

@ -0,0 +1,14 @@
# ><[0]
echo Ok | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; }
# This would not be deterministic: returns 0 "data exists" if EOF is seen
# (true terminated) - because EOF is considered to be data (read will not block),
# else returns 1 "no data".
## ><[????]
#true | { read -t 0 reply; echo ">$reply<[$?]"; }
# ><[0]
true | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; }
# ><[1]
sleep 0.2 | { read -p IGNORED_PROMPT -t 0 reply; echo ">$reply<[$?]"; }

View File

@ -57,9 +57,10 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
const char *opt_u
)
{
struct pollfd pfd[1];
#define fd (pfd[0].fd) /* -u FD */
unsigned err;
unsigned end_ms; /* -t TIMEOUT */
int fd; /* -u FD */
int nchars; /* -n NUM */
char **pp;
char *buffer;
@ -88,38 +89,43 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
return "invalid count";
/* note: "-n 0": off (bash 3.2 does this too) */
}
end_ms = 0;
if (opt_t) {
if (opt_t && !ENABLE_FEATURE_SH_READ_FRAC) {
end_ms = bb_strtou(opt_t, NULL, 10);
if (errno || end_ms > UINT_MAX / 2048)
return "invalid timeout";
end_ms *= 1000;
#if 0 /* even bash has no -t N.NNN support */
ts.tv_sec = bb_strtou(opt_t, &p, 10);
ts.tv_usec = 0;
/* EINVAL means number is ok, but not terminated by NUL */
if (*p == '.' && errno == EINVAL) {
char *p2;
if (*++p) {
int scale;
ts.tv_usec = bb_strtou(p, &p2, 10);
if (errno)
return "invalid timeout";
scale = p2 - p;
/* normalize to usec */
if (scale > 6)
return "invalid timeout";
while (scale++ < 6)
ts.tv_usec *= 10;
if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */
end_ms = UINT_MAX / 2048;
end_ms *= 1000;
}
} else if (ts.tv_sec < 0 || errno) {
if (opt_t && ENABLE_FEATURE_SH_READ_FRAC) {
/* bash 4.3 (maybe earlier) supports -t N.NNNNNN */
char *p;
/* Eat up to three fractional digits */
int frac_digits = 3 + 1;
end_ms = bb_strtou(opt_t, &p, 10);
if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */
end_ms = UINT_MAX / 2048;
if (errno) {
/* EINVAL = number is ok, but not NUL terminated */
if (errno != EINVAL || *p != '.')
return "invalid timeout";
/* Do not check the rest: bash allows "0.123456xyz" */
while (*++p && --frac_digits) {
end_ms *= 10;
end_ms += (*p - '0');
if ((unsigned char)(*p - '0') > 9)
return "invalid timeout";
}
if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
return "invalid timeout";
}
#endif /* if 0 */
while (--frac_digits > 0) {
end_ms *= 10;
}
}
fd = STDIN_FILENO;
if (opt_u) {
fd = bb_strtou(opt_u, NULL, 10);
@ -127,6 +133,19 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
return "invalid file descriptor";
}
if (opt_t && end_ms == 0) {
/* "If timeout is 0, read returns immediately, without trying
* to read any data. The exit status is 0 if input is available
* on the specified file descriptor, non-zero otherwise."
* bash seems to ignore -p PROMPT for this use case.
*/
int r;
pfd[0].events = POLLIN;
r = poll(pfd, 1, /*timeout:*/ 0);
/* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */
return (const char *)(uintptr_t)(r <= 0);
}
if (opt_p && isatty(fd)) {
fputs(opt_p, stderr);
fflush_all();
@ -161,21 +180,24 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
retval = (const char *)(uintptr_t)0;
startword = 1;
backslash = 0;
if (end_ms) /* NB: end_ms stays nonzero: */
end_ms = ((unsigned)monotonic_ms() + end_ms) | 1;
if (opt_t)
end_ms += (unsigned)monotonic_ms();
buffer = NULL;
bufpos = 0;
do {
char c;
struct pollfd pfd[1];
int timeout;
if ((bufpos & 0xff) == 0)
buffer = xrealloc(buffer, bufpos + 0x101);
timeout = -1;
if (end_ms) {
if (opt_t) {
timeout = end_ms - (unsigned)monotonic_ms();
/* ^^^^^^^^^^^^^ all values are unsigned,
* wrapping math is used here, good even if
* 32-bit unix time wrapped (year 2038+).
*/
if (timeout <= 0) { /* already late? */
retval = (const char *)(uintptr_t)1;
goto ret;
@ -187,9 +209,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
* regardless of SA_RESTART-ness of that signal!
*/
errno = 0;
pfd[0].fd = fd;
pfd[0].events = POLLIN;
if (poll(pfd, 1, timeout) != 1) {
if (poll(pfd, 1, timeout) <= 0) {
/* timed out, or EINTR */
err = errno;
retval = (const char *)(uintptr_t)1;
@ -272,6 +293,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
errno = err;
return retval;
#undef fd
}
/* ulimit builtin */