@@ -304,16 +304,15 @@ static inline int fb_isdigit(const char c)
304
304
return isdigit((int)(UCHAR)c);
305
305
}
306
306
307
+ typedef Array<char> CharBuffer;
308
+ GlobalPtr<CharBuffer> charBuffer;
307
309
308
310
#ifdef WIN_NT
309
- // This function is highly based on code written by https://github.com/xenu
310
- // He permitted our usage here: https://github.com/Perl/perl5/pull/18702#issuecomment-1156050577
311
- static int win32ReadConsole(FILE* file, char* buffer, size_t bufferSize)
311
+
312
+ GlobalPtr<Array<WCHAR>> wideBuffer;
313
+
314
+ static int win32ReadConsole(FILE* file, CharBuffer& mbBuffer)
312
315
{
313
- // This function is a workaround for a bug in Windows:
314
- // https://github.com/microsoft/terminal/issues/4551
315
- // tl;dr: ReadFile() and ReadConsoleA() return garbage when reading
316
- // non-ASCII characters from the console with the 65001 codepage.
317
316
auto handle = (HANDLE) _get_osfhandle(fileno(file));
318
317
319
318
if (handle == INVALID_HANDLE_VALUE)
@@ -322,107 +321,49 @@ static int win32ReadConsole(FILE* file, char* buffer, size_t bufferSize)
322
321
return -1;
323
322
}
324
323
325
- DWORD mode;
326
- if (!GetConsoleMode(handle, &mode))
324
+ DWORD oldMode, newMode;
325
+ if (GetConsoleMode(handle, &oldMode))
326
+ {
327
+ newMode = oldMode | (ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
328
+ if (newMode != oldMode)
329
+ SetConsoleMode(handle, oldMode);
330
+ }
331
+ else
327
332
{
328
333
fb_assert(false);
329
- return -1 ;
334
+ newMode = oldMode = 0 ;
330
335
}
331
336
332
- const size_t MAX_LINE_LENGTH = 32765;
333
- static WCHAR wideBuf[MAX_LINE_LENGTH +1];
337
+ Cleanup consoleMode([&] {
338
+ if (oldMode != newMode)
339
+ SetConsoleMode(handle, oldMode);
340
+ });
334
341
335
- WCHAR* pWide;
336
- DWORD charsRead = 0;
337
- size_t leftToRead = bufferSize;
342
+ const size_t MAX_LINE_LENGTH = MAX_USHORT;
343
+ WCHAR* wideBuf = wideBuffer->getBuffer(MAX_LINE_LENGTH, false);
338
344
339
- while (leftToRead)
345
+ DWORD charsRead;
346
+ if (!ReadConsoleW(handle, wideBuf, MAX_LINE_LENGTH, &charsRead, NULL))
340
347
{
341
- // The purpose of convertedBuf is to preserve partial UTF-8 (or of any
342
- // other multibyte encoding) code points between read() calls. Since
343
- // there's only one console, the buffer is global. It's needed because
344
- // ReadConsoleW() returns a string of UTF-16 code units and its result,
345
- // after conversion to the current console codepage, may not fit in the
346
- // return buffer.
347
- //
348
- // The buffer's size is 8 because it will contain at most two UTF-8 code
349
- // points.
350
- static char convertedBuf[8];
351
- static size_t convertedBufLen = 0;
352
-
353
- if (convertedBufLen)
354
- {
355
- bool newline = false;
356
- const size_t toWrite = MIN(convertedBufLen, leftToRead);
357
-
358
- // Don't read anything if the *first* character is ^Z and
359
- // ENABLE_PROCESSED_INPUT is enabled. On some versions of Windows,
360
- // ReadFile() ignores ENABLE_PROCESSED_INPUT, but apparently it's a
361
- // bug: https://github.com/microsoft/terminal/issues/4958
362
- if (leftToRead == bufferSize && (mode & ENABLE_PROCESSED_INPUT) && convertedBuf[0] == 0x1A)
363
- break;
364
-
365
- // Are we returning a newline?
366
- if (memchr(convertedBuf, '\n', toWrite) != 0)
367
- newline = true;
368
-
369
- memcpy(buffer, convertedBuf, toWrite);
370
- buffer += toWrite;
371
-
372
- // If there's anything left in convertedBuf, move it to the beginning of the buffer.
373
- convertedBufLen -= toWrite;
374
-
375
- if (convertedBufLen)
376
- memmove(convertedBuf, convertedBuf + toWrite, convertedBufLen);
377
-
378
- leftToRead -= toWrite;
379
-
380
- // With ENABLE_LINE_INPUT enabled, we stop reading after the first
381
- // newline, otherwise we stop reading after the first character.
382
- if (!leftToRead || newline || (mode & ENABLE_LINE_INPUT) == 0)
383
- break;
384
- }
385
-
386
- if (!charsRead)
387
- {
388
- if (!ReadConsoleW(handle, wideBuf, MAX_LINE_LENGTH, &charsRead, NULL))
389
- return -1;
390
-
391
- if (!charsRead)
392
- break;
393
-
394
- pWide = wideBuf;
395
- }
396
-
397
- DWORD wideBufLen = 1;
398
- charsRead--;
399
-
400
- if (IS_HIGH_SURROGATE(*pWide))
401
- {
402
- // High surrogate, read one more code unit.
403
- if (charsRead)
404
- charsRead--;
405
- else
406
- {
407
- DWORD read2;
408
- if (!ReadConsoleW(handle, pWide + 1, 1, &read2, NULL))
409
- return -1;
410
- }
348
+ fb_assert(false);
349
+ return -1;
350
+ }
411
351
412
- ++wideBufLen;
413
- }
352
+ if (!charsRead)
353
+ return 0;
414
354
415
- convertedBufLen = WideCharToMultiByte(GetConsoleCP(), 0, pWide, wideBufLen ,
416
- convertedBuf, sizeof(convertedBuf) , NULL, NULL);
355
+ int mbLength = WideCharToMultiByte(GetConsoleCP(), 0, wideBuf, charsRead ,
356
+ NULL, 0 , NULL, NULL);
417
357
418
- if (!convertedBufLen )
419
- return -1;
358
+ if (!mbLength )
359
+ return -1;
420
360
421
- pWide += wideBufLen;
422
- }
361
+ mbLength = WideCharToMultiByte(GetConsoleCP(), 0, wideBuf, charsRead,
362
+ mbBuffer.getBuffer(mbLength, false), mbLength, NULL, NULL);
423
363
424
- return bufferSize - leftToRead ;
364
+ return mbLength ;
425
365
}
366
+
426
367
#endif
427
368
428
369
@@ -1108,16 +1049,20 @@ static void readNextInputLine(const char* prompt)
1108
1049
do
1109
1050
{
1110
1051
// Read the line
1111
- char buffer[MAX_USHORT] ;
1052
+ char* buffer;
1112
1053
int lineSize;
1113
1054
1114
1055
#ifdef WIN_NT
1115
1056
if (!Input_file && isatty(fileno(Filelist->Ifp().indev_fpointer)))
1116
- lineSize = win32ReadConsole(Filelist->Ifp().indev_fpointer, buffer, sizeof(buffer));
1057
+ {
1058
+ lineSize = win32ReadConsole(Filelist->Ifp().indev_fpointer, charBuffer);
1059
+ buffer = charBuffer->begin();
1060
+ }
1117
1061
else
1118
1062
#endif
1119
1063
{
1120
- if (fgets(buffer, sizeof(buffer), Filelist->Ifp().indev_fpointer) != NULL)
1064
+ buffer = charBuffer->getBuffer(MAX_USHORT);
1065
+ if (fgets(buffer, charBuffer->getCapacity(), Filelist->Ifp().indev_fpointer) != NULL)
1121
1066
lineSize = strlen(buffer);
1122
1067
else
1123
1068
lineSize = -1;
0 commit comments