aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2017-07-20 16:09:31 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2017-07-20 16:09:31 +0200
commiteae12688c9bb056b21e4e62fc295be5ebdcb8b7b (patch)
tree785eeede5f3c288a510bbd74fe52e1c216155284
parent82dcc3bff97b08db227610596a54574783f69631 (diff)
downloadbusybox-w32-eae12688c9bb056b21e4e62fc295be5ebdcb8b7b.tar.gz
busybox-w32-eae12688c9bb056b21e4e62fc295be5ebdcb8b7b.tar.bz2
busybox-w32-eae12688c9bb056b21e4e62fc295be5ebdcb8b7b.zip
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>
-rw-r--r--shell/Config.src7
-rw-r--r--shell/ash_test/ash-read/read_t0.right3
-rwxr-xr-xshell/ash_test/ash-read/read_t0.tests14
-rw-r--r--shell/hush_test/hush-read/read_t0.right3
-rwxr-xr-xshell/hush_test/hush-read/read_t0.tests14
-rw-r--r--shell/shell_common.c82
6 files changed, 93 insertions, 30 deletions
diff --git a/shell/Config.src b/shell/Config.src
index ccb1b15fe..0dbf304ae 100644
--- a/shell/Config.src
+++ b/shell/Config.src
@@ -145,6 +145,13 @@ config FEATURE_SH_NOFORK
145 This feature is relatively new. Use with care. Report bugs 145 This feature is relatively new. Use with care. Report bugs
146 to project mailing list. 146 to project mailing list.
147 147
148config FEATURE_SH_READ_FRAC
149 bool "read -t N.NNN support (+110 bytes)"
150 default y
151 depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH
152 help
153 Enable support for fractional second timeout in read builtin.
154
148config FEATURE_SH_HISTFILESIZE 155config FEATURE_SH_HISTFILESIZE
149 bool "Use $HISTFILESIZE" 156 bool "Use $HISTFILESIZE"
150 default y 157 default y
diff --git a/shell/ash_test/ash-read/read_t0.right b/shell/ash_test/ash-read/read_t0.right
new file mode 100644
index 000000000..f02105961
--- /dev/null
+++ b/shell/ash_test/ash-read/read_t0.right
@@ -0,0 +1,3 @@
1><[0]
2><[0]
3><[1]
diff --git a/shell/ash_test/ash-read/read_t0.tests b/shell/ash_test/ash-read/read_t0.tests
new file mode 100755
index 000000000..6b7bc217b
--- /dev/null
+++ b/shell/ash_test/ash-read/read_t0.tests
@@ -0,0 +1,14 @@
1# ><[0]
2echo Ok | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; }
3
4# This would not be deterministic: returns 0 "data exists" if EOF is seen
5# (true terminated) - because EOF is considered to be data (read will not block),
6# else returns 1 "no data".
7## ><[????]
8#true | { read -t 0 reply; echo ">$reply<[$?]"; }
9
10# ><[0]
11true | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; }
12
13# ><[1]
14sleep 0.2 | { read -p IGNORED_PROMPT -t 0 reply; echo ">$reply<[$?]"; }
diff --git a/shell/hush_test/hush-read/read_t0.right b/shell/hush_test/hush-read/read_t0.right
new file mode 100644
index 000000000..f02105961
--- /dev/null
+++ b/shell/hush_test/hush-read/read_t0.right
@@ -0,0 +1,3 @@
1><[0]
2><[0]
3><[1]
diff --git a/shell/hush_test/hush-read/read_t0.tests b/shell/hush_test/hush-read/read_t0.tests
new file mode 100755
index 000000000..6b7bc217b
--- /dev/null
+++ b/shell/hush_test/hush-read/read_t0.tests
@@ -0,0 +1,14 @@
1# ><[0]
2echo Ok | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; }
3
4# This would not be deterministic: returns 0 "data exists" if EOF is seen
5# (true terminated) - because EOF is considered to be data (read will not block),
6# else returns 1 "no data".
7## ><[????]
8#true | { read -t 0 reply; echo ">$reply<[$?]"; }
9
10# ><[0]
11true | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; }
12
13# ><[1]
14sleep 0.2 | { read -p IGNORED_PROMPT -t 0 reply; echo ">$reply<[$?]"; }
diff --git a/shell/shell_common.c b/shell/shell_common.c
index bf56f3d78..a9f8d8413 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -57,9 +57,10 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
57 const char *opt_u 57 const char *opt_u
58) 58)
59{ 59{
60 struct pollfd pfd[1];
61#define fd (pfd[0].fd) /* -u FD */
60 unsigned err; 62 unsigned err;
61 unsigned end_ms; /* -t TIMEOUT */ 63 unsigned end_ms; /* -t TIMEOUT */
62 int fd; /* -u FD */
63 int nchars; /* -n NUM */ 64 int nchars; /* -n NUM */
64 char **pp; 65 char **pp;
65 char *buffer; 66 char *buffer;
@@ -88,38 +89,43 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
88 return "invalid count"; 89 return "invalid count";
89 /* note: "-n 0": off (bash 3.2 does this too) */ 90 /* note: "-n 0": off (bash 3.2 does this too) */
90 } 91 }
92
91 end_ms = 0; 93 end_ms = 0;
92 if (opt_t) { 94 if (opt_t && !ENABLE_FEATURE_SH_READ_FRAC) {
93 end_ms = bb_strtou(opt_t, NULL, 10); 95 end_ms = bb_strtou(opt_t, NULL, 10);
94 if (errno || end_ms > UINT_MAX / 2048) 96 if (errno)
95 return "invalid timeout"; 97 return "invalid timeout";
98 if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */
99 end_ms = UINT_MAX / 2048;
96 end_ms *= 1000; 100 end_ms *= 1000;
97#if 0 /* even bash has no -t N.NNN support */ 101 }
98 ts.tv_sec = bb_strtou(opt_t, &p, 10); 102 if (opt_t && ENABLE_FEATURE_SH_READ_FRAC) {
99 ts.tv_usec = 0; 103 /* bash 4.3 (maybe earlier) supports -t N.NNNNNN */
100 /* EINVAL means number is ok, but not terminated by NUL */ 104 char *p;
101 if (*p == '.' && errno == EINVAL) { 105 /* Eat up to three fractional digits */
102 char *p2; 106 int frac_digits = 3 + 1;
103 if (*++p) { 107
104 int scale; 108 end_ms = bb_strtou(opt_t, &p, 10);
105 ts.tv_usec = bb_strtou(p, &p2, 10); 109 if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */
106 if (errno) 110 end_ms = UINT_MAX / 2048;
107 return "invalid timeout"; 111
108 scale = p2 - p; 112 if (errno) {
109 /* normalize to usec */ 113 /* EINVAL = number is ok, but not NUL terminated */
110 if (scale > 6) 114 if (errno != EINVAL || *p != '.')
115 return "invalid timeout";
116 /* Do not check the rest: bash allows "0.123456xyz" */
117 while (*++p && --frac_digits) {
118 end_ms *= 10;
119 end_ms += (*p - '0');
120 if ((unsigned char)(*p - '0') > 9)
111 return "invalid timeout"; 121 return "invalid timeout";
112 while (scale++ < 6)
113 ts.tv_usec *= 10;
114 } 122 }
115 } else if (ts.tv_sec < 0 || errno) {
116 return "invalid timeout";
117 } 123 }
118 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */ 124 while (--frac_digits > 0) {
119 return "invalid timeout"; 125 end_ms *= 10;
120 } 126 }
121#endif /* if 0 */
122 } 127 }
128
123 fd = STDIN_FILENO; 129 fd = STDIN_FILENO;
124 if (opt_u) { 130 if (opt_u) {
125 fd = bb_strtou(opt_u, NULL, 10); 131 fd = bb_strtou(opt_u, NULL, 10);
@@ -127,6 +133,19 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
127 return "invalid file descriptor"; 133 return "invalid file descriptor";
128 } 134 }
129 135
136 if (opt_t && end_ms == 0) {
137 /* "If timeout is 0, read returns immediately, without trying
138 * to read any data. The exit status is 0 if input is available
139 * on the specified file descriptor, non-zero otherwise."
140 * bash seems to ignore -p PROMPT for this use case.
141 */
142 int r;
143 pfd[0].events = POLLIN;
144 r = poll(pfd, 1, /*timeout:*/ 0);
145 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */
146 return (const char *)(uintptr_t)(r <= 0);
147 }
148
130 if (opt_p && isatty(fd)) { 149 if (opt_p && isatty(fd)) {
131 fputs(opt_p, stderr); 150 fputs(opt_p, stderr);
132 fflush_all(); 151 fflush_all();
@@ -161,21 +180,24 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
161 retval = (const char *)(uintptr_t)0; 180 retval = (const char *)(uintptr_t)0;
162 startword = 1; 181 startword = 1;
163 backslash = 0; 182 backslash = 0;
164 if (end_ms) /* NB: end_ms stays nonzero: */ 183 if (opt_t)
165 end_ms = ((unsigned)monotonic_ms() + end_ms) | 1; 184 end_ms += (unsigned)monotonic_ms();
166 buffer = NULL; 185 buffer = NULL;
167 bufpos = 0; 186 bufpos = 0;
168 do { 187 do {
169 char c; 188 char c;
170 struct pollfd pfd[1];
171 int timeout; 189 int timeout;
172 190
173 if ((bufpos & 0xff) == 0) 191 if ((bufpos & 0xff) == 0)
174 buffer = xrealloc(buffer, bufpos + 0x101); 192 buffer = xrealloc(buffer, bufpos + 0x101);
175 193
176 timeout = -1; 194 timeout = -1;
177 if (end_ms) { 195 if (opt_t) {
178 timeout = end_ms - (unsigned)monotonic_ms(); 196 timeout = end_ms - (unsigned)monotonic_ms();
197 /* ^^^^^^^^^^^^^ all values are unsigned,
198 * wrapping math is used here, good even if
199 * 32-bit unix time wrapped (year 2038+).
200 */
179 if (timeout <= 0) { /* already late? */ 201 if (timeout <= 0) { /* already late? */
180 retval = (const char *)(uintptr_t)1; 202 retval = (const char *)(uintptr_t)1;
181 goto ret; 203 goto ret;
@@ -187,9 +209,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
187 * regardless of SA_RESTART-ness of that signal! 209 * regardless of SA_RESTART-ness of that signal!
188 */ 210 */
189 errno = 0; 211 errno = 0;
190 pfd[0].fd = fd;
191 pfd[0].events = POLLIN; 212 pfd[0].events = POLLIN;
192 if (poll(pfd, 1, timeout) != 1) { 213 if (poll(pfd, 1, timeout) <= 0) {
193 /* timed out, or EINTR */ 214 /* timed out, or EINTR */
194 err = errno; 215 err = errno;
195 retval = (const char *)(uintptr_t)1; 216 retval = (const char *)(uintptr_t)1;
@@ -272,6 +293,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
272 293
273 errno = err; 294 errno = err;
274 return retval; 295 return retval;
296#undef fd
275} 297}
276 298
277/* ulimit builtin */ 299/* ulimit builtin */