diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-20 16:09:31 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-20 16:09:31 +0200 |
commit | eae12688c9bb056b21e4e62fc295be5ebdcb8b7b (patch) | |
tree | 785eeede5f3c288a510bbd74fe52e1c216155284 | |
parent | 82dcc3bff97b08db227610596a54574783f69631 (diff) | |
download | busybox-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.src | 7 | ||||
-rw-r--r-- | shell/ash_test/ash-read/read_t0.right | 3 | ||||
-rwxr-xr-x | shell/ash_test/ash-read/read_t0.tests | 14 | ||||
-rw-r--r-- | shell/hush_test/hush-read/read_t0.right | 3 | ||||
-rwxr-xr-x | shell/hush_test/hush-read/read_t0.tests | 14 | ||||
-rw-r--r-- | shell/shell_common.c | 82 |
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 | ||
148 | config 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 | |||
148 | config FEATURE_SH_HISTFILESIZE | 155 | config 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] | ||
2 | echo 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] | ||
11 | true | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; } | ||
12 | |||
13 | # ><[1] | ||
14 | sleep 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] | ||
2 | echo 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] | ||
11 | true | { sleep 0.1; read -t 0 reply; echo ">$reply<[$?]"; } | ||
12 | |||
13 | # ><[1] | ||
14 | sleep 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 */ |