diff options
author | Avi Halachmi (:avih) <avihpit@yahoo.com> | 2023-06-27 13:58:46 +0300 |
---|---|---|
committer | Avi Halachmi (:avih) <avihpit@yahoo.com> | 2023-06-28 18:08:06 +0300 |
commit | ec99f03ae8623be40a13b45b646745d0c8a4b06d (patch) | |
tree | ce7649403a6b54e777512663c4d6e9d00ac4e200 | |
parent | 1602a45b797908025dc71e6a07149a39fdb12a48 (diff) | |
download | busybox-w32-ec99f03ae8623be40a13b45b646745d0c8a4b06d.tar.gz busybox-w32-ec99f03ae8623be40a13b45b646745d0c8a4b06d.tar.bz2 busybox-w32-ec99f03ae8623be40a13b45b646745d0c8a4b06d.zip |
win32: UTF8 input: avoid timeout when delivering UTF8 bytes
When windows_read_key - which is the sole consumer of
readConsoleInput_utf8 - is called with a timeout value, it uses
WaitForSingleObject to test whether the console has pending input.
Previously, readConsoleInput_utf8 consumed the input record before
it delivered the UTF8 bytes which are generated from it.
It's not an issue with ASCII-7 input - because indeed there are no
buffered bytes once it's delivered, and, except for console bugs
(when only key-up record exists) also not an issue with 2 or 3 bytes
UTF8 codepoints - because these are generated from a single wchar_t
input record on key-down, and the key-up event is not yet dequeued
while delivering the key-down UTF8 bytes.
But with a surrogate pair, which consumes two wchar_t records to
realize the UTF8 sequence, we previously consumed the records
up to and including the key-up event of the 2nd surrogate half.
This could result in a timeout if there are no further records at
the queue - eventhough some UTF8 bytes are still buffered/pending.
Such timeout can result in the shell aborting - windows_read_key
returns -1, which is later interpreted as EOF of the shell input,
and quits the shell.
Now readConsoleInput_utf8 dequeues an input record only once the
last byte which was generated from this record is delivered,
which we do using PeekConsoleInputW instead of ReadConsoleInputW.
This avoid a timeout as long as there are input bytes to deliver.
-rw-r--r-- | win32/winansi.c | 20 |
1 files changed, 14 insertions, 6 deletions
diff --git a/win32/winansi.c b/win32/winansi.c index f823d14be..d85b6de23 100644 --- a/win32/winansi.c +++ b/win32/winansi.c | |||
@@ -1223,7 +1223,7 @@ static int toutf8(DWORD cp, unsigned char *buf) { | |||
1223 | 1223 | ||
1224 | // peek into the console input queue and try to find a key-up event of | 1224 | // peek into the console input queue and try to find a key-up event of |
1225 | // a surrugate-2nd-half, at which case eat the console events up to this | 1225 | // a surrugate-2nd-half, at which case eat the console events up to this |
1226 | // one, and combine the pair values into *ph1 | 1226 | // one (excluding), and combine the pair values into *ph1 |
1227 | static void maybeEatUpto2ndHalfUp(HANDLE h, DWORD *ph1) | 1227 | static void maybeEatUpto2ndHalfUp(HANDLE h, DWORD *ph1) |
1228 | { | 1228 | { |
1229 | // Peek into the queue arbitrary 16 records deep | 1229 | // Peek into the queue arbitrary 16 records deep |
@@ -1236,7 +1236,8 @@ static void maybeEatUpto2ndHalfUp(HANDLE h, DWORD *ph1) | |||
1236 | 1236 | ||
1237 | // we're conservative, and abort the search on anything which | 1237 | // we're conservative, and abort the search on anything which |
1238 | // seems out of place, like non-key event, non-2nd-half, etc. | 1238 | // seems out of place, like non-key event, non-2nd-half, etc. |
1239 | for (i = 0; i < got; ++i) { | 1239 | // search from 1 because i==0 is still the 1st half down record. |
1240 | for (i = 1; i < got; ++i) { | ||
1240 | DWORD h2; | 1241 | DWORD h2; |
1241 | int is2nd, isdown; | 1242 | int is2nd, isdown; |
1242 | 1243 | ||
@@ -1255,7 +1256,7 @@ static void maybeEatUpto2ndHalfUp(HANDLE h, DWORD *ph1) | |||
1255 | return; | 1256 | return; |
1256 | 1257 | ||
1257 | // got 2nd-half-up. eat the events up to this, combine the values | 1258 | // got 2nd-half-up. eat the events up to this, combine the values |
1258 | ReadConsoleInputW(h, r, i + 1, &got); | 1259 | ReadConsoleInputW(h, r, i, &got); |
1259 | *ph1 = 0x10000 | ((*ph1 & ~0xD800) << 10) | (h2 & ~0xDC00); | 1260 | *ph1 = 0x10000 | ((*ph1 & ~0xD800) << 10) | (h2 & ~0xDC00); |
1260 | return; | 1261 | return; |
1261 | } | 1262 | } |
@@ -1313,10 +1314,15 @@ BOOL readConsoleInput_utf8(HANDLE h, INPUT_RECORD *r, DWORD len, DWORD *got) | |||
1313 | if (u8pos == u8len) { | 1314 | if (u8pos == u8len) { |
1314 | DWORD codepoint; | 1315 | DWORD codepoint; |
1315 | 1316 | ||
1316 | if (!ReadConsoleInputW(h, r, 1, got)) | 1317 | // peek rather than read to keep the last processed record at |
1318 | // the console queue until we deliver all of its products, so | ||
1319 | // that WaitForSingleObject(handle) shows there's data ready. | ||
1320 | if (!PeekConsoleInputW(h, r, 1, got)) | ||
1317 | return FALSE; | 1321 | return FALSE; |
1318 | if (*got == 0 || r->EventType != KEY_EVENT) | 1322 | if (*got == 0) |
1319 | return TRUE; | 1323 | return TRUE; |
1324 | if (r->EventType != KEY_EVENT) | ||
1325 | return ReadConsoleInput(h, r, 1, got); | ||
1320 | 1326 | ||
1321 | srec = *r; | 1327 | srec = *r; |
1322 | codepoint = srec.Event.KeyEvent.uChar.UnicodeChar; | 1328 | codepoint = srec.Event.KeyEvent.uChar.UnicodeChar; |
@@ -1334,7 +1340,7 @@ BOOL readConsoleInput_utf8(HANDLE h, INPUT_RECORD *r, DWORD len, DWORD *got) | |||
1334 | } | 1340 | } |
1335 | 1341 | ||
1336 | // if it's a 1st (high) surrogate pair half, try to eat upto and | 1342 | // if it's a 1st (high) surrogate pair half, try to eat upto and |
1337 | // including the 2nd (low) half, and combine them into codepoint. | 1343 | // excluding the 2nd (low) half, and combine them into codepoint. |
1338 | if (codepoint >= 0xD800 && codepoint <= 0xDBFF) | 1344 | if (codepoint >= 0xD800 && codepoint <= 0xDBFF) |
1339 | maybeEatUpto2ndHalfUp(h, &codepoint); | 1345 | maybeEatUpto2ndHalfUp(h, &codepoint); |
1340 | 1346 | ||
@@ -1344,6 +1350,8 @@ BOOL readConsoleInput_utf8(HANDLE h, INPUT_RECORD *r, DWORD len, DWORD *got) | |||
1344 | 1350 | ||
1345 | *r = srec; | 1351 | *r = srec; |
1346 | r->Event.KeyEvent.uChar.AsciiChar = (char)u8buf[u8pos++]; | 1352 | r->Event.KeyEvent.uChar.AsciiChar = (char)u8buf[u8pos++]; |
1353 | if (u8pos == u8len) // consume the record which generated this buffer | ||
1354 | ReadConsoleInputW(h, &srec, 1, got); | ||
1347 | *got = 1; | 1355 | *got = 1; |
1348 | return TRUE; | 1356 | return TRUE; |
1349 | } | 1357 | } |