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:
parent
82dcc3bff9
commit
eae12688c9
@ -145,6 +145,13 @@ config FEATURE_SH_NOFORK
|
|||||||
This feature is relatively new. Use with care. Report bugs
|
This feature is relatively new. Use with care. Report bugs
|
||||||
to project mailing list.
|
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
|
config FEATURE_SH_HISTFILESIZE
|
||||||
bool "Use $HISTFILESIZE"
|
bool "Use $HISTFILESIZE"
|
||||||
default y
|
default y
|
||||||
|
3
shell/ash_test/ash-read/read_t0.right
Normal file
3
shell/ash_test/ash-read/read_t0.right
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
><[0]
|
||||||
|
><[0]
|
||||||
|
><[1]
|
14
shell/ash_test/ash-read/read_t0.tests
Executable file
14
shell/ash_test/ash-read/read_t0.tests
Executable 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<[$?]"; }
|
3
shell/hush_test/hush-read/read_t0.right
Normal file
3
shell/hush_test/hush-read/read_t0.right
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
><[0]
|
||||||
|
><[0]
|
||||||
|
><[1]
|
14
shell/hush_test/hush-read/read_t0.tests
Executable file
14
shell/hush_test/hush-read/read_t0.tests
Executable 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<[$?]"; }
|
@ -57,9 +57,10 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
|
|||||||
const char *opt_u
|
const char *opt_u
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
struct pollfd pfd[1];
|
||||||
|
#define fd (pfd[0].fd) /* -u FD */
|
||||||
unsigned err;
|
unsigned err;
|
||||||
unsigned end_ms; /* -t TIMEOUT */
|
unsigned end_ms; /* -t TIMEOUT */
|
||||||
int fd; /* -u FD */
|
|
||||||
int nchars; /* -n NUM */
|
int nchars; /* -n NUM */
|
||||||
char **pp;
|
char **pp;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
@ -88,38 +89,43 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
|
|||||||
return "invalid count";
|
return "invalid count";
|
||||||
/* note: "-n 0": off (bash 3.2 does this too) */
|
/* note: "-n 0": off (bash 3.2 does this too) */
|
||||||
}
|
}
|
||||||
|
|
||||||
end_ms = 0;
|
end_ms = 0;
|
||||||
if (opt_t) {
|
if (opt_t && !ENABLE_FEATURE_SH_READ_FRAC) {
|
||||||
end_ms = bb_strtou(opt_t, NULL, 10);
|
end_ms = bb_strtou(opt_t, NULL, 10);
|
||||||
if (errno || end_ms > UINT_MAX / 2048)
|
if (errno)
|
||||||
return "invalid timeout";
|
return "invalid timeout";
|
||||||
|
if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */
|
||||||
|
end_ms = UINT_MAX / 2048;
|
||||||
end_ms *= 1000;
|
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;
|
|
||||||
}
|
|
||||||
} else if (ts.tv_sec < 0 || errno) {
|
|
||||||
return "invalid timeout";
|
|
||||||
}
|
|
||||||
if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
|
|
||||||
return "invalid timeout";
|
|
||||||
}
|
|
||||||
#endif /* if 0 */
|
|
||||||
}
|
}
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (--frac_digits > 0) {
|
||||||
|
end_ms *= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fd = STDIN_FILENO;
|
fd = STDIN_FILENO;
|
||||||
if (opt_u) {
|
if (opt_u) {
|
||||||
fd = bb_strtou(opt_u, NULL, 10);
|
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";
|
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)) {
|
if (opt_p && isatty(fd)) {
|
||||||
fputs(opt_p, stderr);
|
fputs(opt_p, stderr);
|
||||||
fflush_all();
|
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;
|
retval = (const char *)(uintptr_t)0;
|
||||||
startword = 1;
|
startword = 1;
|
||||||
backslash = 0;
|
backslash = 0;
|
||||||
if (end_ms) /* NB: end_ms stays nonzero: */
|
if (opt_t)
|
||||||
end_ms = ((unsigned)monotonic_ms() + end_ms) | 1;
|
end_ms += (unsigned)monotonic_ms();
|
||||||
buffer = NULL;
|
buffer = NULL;
|
||||||
bufpos = 0;
|
bufpos = 0;
|
||||||
do {
|
do {
|
||||||
char c;
|
char c;
|
||||||
struct pollfd pfd[1];
|
|
||||||
int timeout;
|
int timeout;
|
||||||
|
|
||||||
if ((bufpos & 0xff) == 0)
|
if ((bufpos & 0xff) == 0)
|
||||||
buffer = xrealloc(buffer, bufpos + 0x101);
|
buffer = xrealloc(buffer, bufpos + 0x101);
|
||||||
|
|
||||||
timeout = -1;
|
timeout = -1;
|
||||||
if (end_ms) {
|
if (opt_t) {
|
||||||
timeout = end_ms - (unsigned)monotonic_ms();
|
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? */
|
if (timeout <= 0) { /* already late? */
|
||||||
retval = (const char *)(uintptr_t)1;
|
retval = (const char *)(uintptr_t)1;
|
||||||
goto ret;
|
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!
|
* regardless of SA_RESTART-ness of that signal!
|
||||||
*/
|
*/
|
||||||
errno = 0;
|
errno = 0;
|
||||||
pfd[0].fd = fd;
|
|
||||||
pfd[0].events = POLLIN;
|
pfd[0].events = POLLIN;
|
||||||
if (poll(pfd, 1, timeout) != 1) {
|
if (poll(pfd, 1, timeout) <= 0) {
|
||||||
/* timed out, or EINTR */
|
/* timed out, or EINTR */
|
||||||
err = errno;
|
err = errno;
|
||||||
retval = (const char *)(uintptr_t)1;
|
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;
|
errno = err;
|
||||||
return retval;
|
return retval;
|
||||||
|
#undef fd
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ulimit builtin */
|
/* ulimit builtin */
|
||||||
|
Loading…
Reference in New Issue
Block a user