aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvi Halachmi (:avih) <avihpit@yahoo.com>2023-06-27 13:58:46 +0300
committerAvi Halachmi (:avih) <avihpit@yahoo.com>2023-06-28 18:08:06 +0300
commitec99f03ae8623be40a13b45b646745d0c8a4b06d (patch)
treece7649403a6b54e777512663c4d6e9d00ac4e200
parent1602a45b797908025dc71e6a07149a39fdb12a48 (diff)
downloadbusybox-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.c20
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
1227static void maybeEatUpto2ndHalfUp(HANDLE h, DWORD *ph1) 1227static 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}