aboutsummaryrefslogtreecommitdiff
path: root/src/libs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-11-17 16:05:22 -0800
committerRob Mensching <rob@firegiant.com>2022-11-21 09:12:41 -0800
commit83a63bdc1943187e93749420a3408cd6248eedb9 (patch)
tree617e931eddbf3ed96839ad4e100421e627a1ced5 /src/libs
parent307e3f1bb587b93ad386bbfd9b2d4603a72d80c4 (diff)
downloadwix-83a63bdc1943187e93749420a3408cd6248eedb9.tar.gz
wix-83a63bdc1943187e93749420a3408cd6248eedb9.tar.bz2
wix-83a63bdc1943187e93749420a3408cd6248eedb9.zip
Add support for UTF-8 console and use it for passing smartcab paths
Fixes 7024
Diffstat (limited to 'src/libs')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/conutil.cpp374
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/conutil.h70
2 files changed, 283 insertions, 161 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/conutil.cpp b/src/libs/dutil/WixToolset.DUtil/conutil.cpp
index 7ca17439..2aec36e5 100644
--- a/src/libs/dutil/WixToolset.DUtil/conutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/conutil.cpp
@@ -18,18 +18,33 @@
18#define ConExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CONUTIL, g, x, s, __VA_ARGS__) 18#define ConExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CONUTIL, g, x, s, __VA_ARGS__)
19 19
20 20
21LPCSTR NEWLINE = "\r\n";
22
21static HANDLE vhStdIn = INVALID_HANDLE_VALUE; 23static HANDLE vhStdIn = INVALID_HANDLE_VALUE;
22static HANDLE vhStdOut = INVALID_HANDLE_VALUE; 24static HANDLE vhStdOut = INVALID_HANDLE_VALUE;
23static BOOL vfConsoleIn = FALSE; 25static BOOL vfStdInInteractive = FALSE;
26static BOOL vfStdOutInteractive = FALSE;
24static BOOL vfConsoleOut = FALSE; 27static BOOL vfConsoleOut = FALSE;
25static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo; 28static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo;
26 29
27 30
31static HRESULT DAPI ReadInteractiveStdIn(
32 __deref_out_z LPWSTR* ppwzBuffer
33);
34static HRESULT ReadRedirectedStdIn(
35 __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
36 __out DWORD* pcchSize,
37 BOOL fReadLine,
38 DWORD dwMaxRead
39);
40
41
28extern "C" HRESULT DAPI ConsoleInitialize() 42extern "C" HRESULT DAPI ConsoleInitialize()
29{ 43{
30 Assert(INVALID_HANDLE_VALUE == vhStdOut); 44 Assert(INVALID_HANDLE_VALUE == vhStdOut);
31 HRESULT hr = S_OK; 45 HRESULT hr = S_OK;
32 UINT er; 46 DWORD dwStdInMode = 0;
47 DWORD dwStdOutMode = 0;
33 48
34 vhStdIn = ::GetStdHandle(STD_INPUT_HANDLE); 49 vhStdIn = ::GetStdHandle(STD_INPUT_HANDLE);
35 if (INVALID_HANDLE_VALUE == vhStdIn) 50 if (INVALID_HANDLE_VALUE == vhStdIn)
@@ -37,48 +52,24 @@ extern "C" HRESULT DAPI ConsoleInitialize()
37 ConExitOnLastError(hr, "failed to open stdin"); 52 ConExitOnLastError(hr, "failed to open stdin");
38 } 53 }
39 54
55 vfStdInInteractive = ::GetFileType(vhStdIn) == FILE_TYPE_CHAR && ::GetConsoleMode(vhStdIn, &dwStdInMode);
56
40 vhStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); 57 vhStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
41 if (INVALID_HANDLE_VALUE == vhStdOut) 58 if (INVALID_HANDLE_VALUE == vhStdOut)
42 { 59 {
43 ConExitOnLastError(hr, "failed to open stdout"); 60 ConExitOnLastError(hr, "failed to open stdout");
44 } 61 }
45 62
46 // check if we have a std in on the console 63 vfStdOutInteractive = ::GetFileType(vhStdOut) == FILE_TYPE_CHAR && ::GetConsoleMode(vhStdOut, &dwStdOutMode);
47 if (::GetConsoleScreenBufferInfo(vhStdIn, &vcsbiInfo))
48 {
49 vfConsoleIn = TRUE;
50 }
51 else
52 {
53 er = ::GetLastError();
54 if (ERROR_INVALID_HANDLE == er)
55 {
56 vfConsoleIn= FALSE;
57 hr = S_OK;
58 }
59 else
60 {
61 ConExitOnWin32Error(er, hr, "failed to get input console screen buffer info");
62 }
63 }
64 64
65 if (::GetConsoleScreenBufferInfo(vhStdOut, &vcsbiInfo)) 65 if (::GetConsoleScreenBufferInfo(vhStdOut, &vcsbiInfo))
66 { 66 {
67 vfConsoleOut = TRUE; 67 vfConsoleOut = TRUE;
68 } 68 }
69 else // no console 69
70 if (!::SetConsoleCP(CP_UTF8) || !::SetConsoleOutputCP(CP_UTF8))
70 { 71 {
71 memset(&vcsbiInfo, 0, sizeof(vcsbiInfo)); 72 ConExitWithLastError(hr, "failed to set console codepage to UTF-8");
72 er = ::GetLastError();
73 if (ERROR_INVALID_HANDLE == er)
74 {
75 vfConsoleOut = FALSE;
76 hr = S_OK;
77 }
78 else
79 {
80 ConExitOnWin32Error(er, hr, "failed to get output console screen buffer info");
81 }
82 } 73 }
83 74
84LExit: 75LExit:
@@ -162,13 +153,6 @@ extern "C" void DAPI ConsoleNormal()
162 } 153 }
163} 154}
164 155
165
166/********************************************************************
167 ConsoleWrite - full color printfA without libc
168
169 NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d")
170 assumes already in normal color and resets the screen to normal color
171********************************************************************/
172extern "C" HRESULT DAPI ConsoleWrite( 156extern "C" HRESULT DAPI ConsoleWrite(
173 CONSOLE_COLOR cc, 157 CONSOLE_COLOR cc,
174 __in_z __format_string LPCSTR szFormat, 158 __in_z __format_string LPCSTR szFormat,
@@ -202,7 +186,7 @@ extern "C" HRESULT DAPI ConsoleWrite(
202 { 186 {
203 if (!::WriteFile(vhStdOut, reinterpret_cast<BYTE*>(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL)) 187 if (!::WriteFile(vhStdOut, reinterpret_cast<BYTE*>(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL))
204 { 188 {
205 ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput); 189 ConExitOnLastError(hr, "failed to write output to console with format: %s", szFormat);
206 } 190 }
207 191
208 cbTotal += cbWrote; 192 cbTotal += cbWrote;
@@ -220,12 +204,53 @@ LExit:
220} 204}
221 205
222 206
223/******************************************************************** 207extern "C" HRESULT DAPI ConsoleWriteW(
224 ConsoleWriteLine - full color printfA plus newline without libc 208 __in CONSOLE_COLOR cc,
209 __in_z LPCWSTR wzData
210)
211{
212 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
213 HRESULT hr = S_OK;
214 LPSTR pszOutput = NULL;
215 DWORD cchOutput = 0;
216 DWORD cbWrote = 0;
217 DWORD cbTotal = 0;
218
219 // set the color
220 switch (cc)
221 {
222 case CONSOLE_COLOR_NORMAL: break; // do nothing
223 case CONSOLE_COLOR_RED: ConsoleRed(); break;
224 case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break;
225 case CONSOLE_COLOR_GREEN: ConsoleGreen(); break;
226 }
227
228 hr = StrAnsiAllocString(&pszOutput, wzData, 0, CP_UTF8);
229 ExitOnFailure(hr, "failed to convert console output to utf-8: %ls", wzData);
230
231 cchOutput = lstrlenA(pszOutput);
232 while (cbTotal < (sizeof(*pszOutput) * cchOutput))
233 {
234 if (!::WriteFile(vhStdOut, reinterpret_cast<BYTE*>(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL))
235 {
236 ConExitOnLastError(hr, "failed to write output to console with format: %ls", wzData);
237 }
238
239 cbTotal += cbWrote;
240 }
241
242 // reset the color to normal
243 if (CONSOLE_COLOR_NORMAL != cc)
244 {
245 ConsoleNormal();
246 }
247
248LExit:
249 ReleaseStr(pszOutput);
250 return hr;
251}
252
225 253
226 NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d")
227 assumes already in normal color and resets the screen to normal color
228********************************************************************/
229extern "C" HRESULT DAPI ConsoleWriteLine( 254extern "C" HRESULT DAPI ConsoleWriteLine(
230 CONSOLE_COLOR cc, 255 CONSOLE_COLOR cc,
231 __in_z __format_string LPCSTR szFormat, 256 __in_z __format_string LPCSTR szFormat,
@@ -238,7 +263,6 @@ extern "C" HRESULT DAPI ConsoleWriteLine(
238 DWORD cchOutput = 0; 263 DWORD cchOutput = 0;
239 DWORD cbWrote = 0; 264 DWORD cbWrote = 0;
240 DWORD cbTotal = 0; 265 DWORD cbTotal = 0;
241 LPCSTR szNewLine = "\r\n";
242 266
243 // set the color 267 // set the color
244 switch (cc) 268 switch (cc)
@@ -270,7 +294,7 @@ extern "C" HRESULT DAPI ConsoleWriteLine(
270 // 294 //
271 // write the newline 295 // write the newline
272 // 296 //
273 if (!::WriteFile(vhStdOut, reinterpret_cast<const BYTE*>(szNewLine), 2, &cbWrote, NULL)) 297 if (!::WriteFile(vhStdOut, reinterpret_cast<const BYTE*>(NEWLINE), 2, &cbWrote, NULL))
274 { 298 {
275 ConExitOnLastError(hr, "failed to write newline to console"); 299 ConExitOnLastError(hr, "failed to write newline to console");
276 } 300 }
@@ -287,11 +311,6 @@ LExit:
287} 311}
288 312
289 313
290/********************************************************************
291 ConsoleWriteError - display an error to the screen
292
293 NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%s" or "%d")
294********************************************************************/
295HRESULT ConsoleWriteError( 314HRESULT ConsoleWriteError(
296 HRESULT hrError, 315 HRESULT hrError,
297 CONSOLE_COLOR cc, 316 CONSOLE_COLOR cc,
@@ -323,11 +342,6 @@ LExit:
323} 342}
324 343
325 344
326/********************************************************************
327 ConsoleReadW - get console input without libc
328
329 NOTE: only supports reading ANSI characters
330********************************************************************/
331extern "C" HRESULT DAPI ConsoleReadW( 345extern "C" HRESULT DAPI ConsoleReadW(
332 __deref_out_z LPWSTR* ppwzBuffer 346 __deref_out_z LPWSTR* ppwzBuffer
333 ) 347 )
@@ -336,57 +350,24 @@ extern "C" HRESULT DAPI ConsoleReadW(
336 Assert(ppwzBuffer); 350 Assert(ppwzBuffer);
337 351
338 HRESULT hr = S_OK; 352 HRESULT hr = S_OK;
339 LPSTR psz = NULL; 353 DWORD cchSize = 0;
340 DWORD cch = 0;
341 DWORD cchRead = 0;
342 DWORD cchTotalRead = 0;
343 354
344 cch = 64; 355 if (vfStdInInteractive)
345 hr = StrAnsiAlloc(&psz, cch);
346 ConExitOnFailure(hr, "failed to allocate memory to read from console");
347
348 // loop until we read the \r\n from the console
349 for (;;)
350 { 356 {
351 // read one character at a time, since that seems to be the only way to make this work 357 hr = ReadInteractiveStdIn(ppwzBuffer);
352 if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL)) 358 ExitOnFailure(hr, "failed to read from interactive console");
353 ConExitOnLastError(hr, "failed to read string from console"); 359 }
354 360 else
355 cchTotalRead += cchRead; 361 {
356 if (1 < cchTotalRead && '\r' == psz[cchTotalRead - 2] && '\n' == psz[cchTotalRead - 1]) 362 hr = ReadRedirectedStdIn(ppwzBuffer, &cchSize, TRUE, 0);
357 { 363 ExitOnFailure(hr, "failed to read from redirected console");
358 psz[cchTotalRead - 2] = '\0'; // chop off the \r\n
359 break;
360 }
361 else if (0 == cchRead) // nothing more was read
362 {
363 psz[cchTotalRead] = '\0'; // null termintate and bail
364 break;
365 }
366
367 if (cchTotalRead == cch)
368 {
369 cch *= 2; // double everytime we run out of space
370 hr = StrAnsiAlloc(&psz, cch);
371 ConExitOnFailure(hr, "failed to allocate memory to read from console");
372 }
373 } 364 }
374
375 hr = StrAllocStringAnsi(ppwzBuffer, psz, 0, CP_ACP);
376 365
377LExit: 366LExit:
378 ReleaseStr(psz);
379 return hr; 367 return hr;
380} 368}
381 369
382 370
383/********************************************************************
384 ConsoleReadNonBlockingW - Read from the console without blocking
385 Won't work for redirected files (exe < txtfile), but will work for stdin redirected to
386 an anonymous or named pipe
387
388 if (fReadLine), stop reading immediately when \r\n is found
389*********************************************************************/
390extern "C" HRESULT DAPI ConsoleReadNonBlockingW( 371extern "C" HRESULT DAPI ConsoleReadNonBlockingW(
391 __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, 372 __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
392 __out DWORD* pcchSize, 373 __out DWORD* pcchSize,
@@ -406,9 +387,6 @@ extern "C" HRESULT DAPI ConsoleReadNonBlockingW(
406 DWORD cchTotal = 0; 387 DWORD cchTotal = 0;
407 DWORD cch = 8; 388 DWORD cch = 8;
408 389
409 DWORD cchRead = 0;
410 DWORD cchTotalRead = 0;
411
412 DWORD dwIndex = 0; 390 DWORD dwIndex = 0;
413 DWORD er; 391 DWORD er;
414 392
@@ -476,45 +454,13 @@ extern "C" HRESULT DAPI ConsoleReadNonBlockingW(
476 { 454 {
477 // otherwise, the peek worked, and we have the end of a pipe 455 // otherwise, the peek worked, and we have the end of a pipe
478 if (0 == dwRead) 456 if (0 == dwRead)
479 ExitFunction1(hr = S_FALSE);
480
481 cch = 8;
482 hr = StrAnsiAlloc(&psz, cch);
483 ConExitOnFailure(hr, "failed to allocate memory to read from console");
484
485 for (/*dwRead from PeekNamedPipe*/; dwRead > 0; dwRead--)
486 { 457 {
487 // read one character at a time, since that seems to be the only way to make this work 458 ExitFunction1(hr = S_FALSE);
488 if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL))
489 {
490 ConExitOnLastError(hr, "failed to read string from console");
491 }
492
493 cchTotalRead += cchRead;
494 if (fReadLine && '\r' == psz[cchTotalRead - 1] && '\n' == psz[cchTotalRead])
495 {
496 psz[cchTotalRead - 1] = '\0'; // chop off the \r\n
497 cchTotalRead -= 1;
498 break;
499 }
500 else if (0 == cchRead) // nothing more was read
501 {
502 psz[cchTotalRead] = '\0'; // null termintate and bail
503 break;
504 }
505
506 if (cchTotalRead == cch)
507 {
508 cch *= 2; // double everytime we run out of space
509 hr = StrAnsiAlloc(&psz, cch);
510 ConExitOnFailure(hr, "failed to allocate memory to read from console");
511 }
512 } 459 }
513 460
514 *pcchSize = cchTotalRead; 461 hr = ReadRedirectedStdIn(ppwzBuffer, pcchSize, fReadLine, dwRead);
515 hr = StrAllocStringAnsi(ppwzBuffer, psz, cchTotalRead, CP_ACP);
516 } 462 }
517 463
518LExit: 464LExit:
519 ReleaseStr(psz); 465 ReleaseStr(psz);
520 466
@@ -522,10 +468,6 @@ LExit:
522} 468}
523 469
524 470
525/********************************************************************
526 ConsoleReadStringA - get console input without libc
527
528*********************************************************************/
529extern "C" HRESULT DAPI ConsoleReadStringA( 471extern "C" HRESULT DAPI ConsoleReadStringA(
530 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer, 472 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer,
531 CONST DWORD cchCharBuffer, 473 CONST DWORD cchCharBuffer,
@@ -543,16 +485,18 @@ extern "C" HRESULT DAPI ConsoleReadStringA(
543 do 485 do
544 { 486 {
545 hr = StrAnsiAlloc(ppszCharBuffer, cchCharBuffer * iRead); 487 hr = StrAnsiAlloc(ppszCharBuffer, cchCharBuffer * iRead);
546 ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); 488 ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringA");
489
547 // ReadConsoleW will not return until <Return>, the last two chars are 13 and 10. 490 // ReadConsoleW will not return until <Return>, the last two chars are 13 and 10.
548 if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) 491 if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0)
549 { 492 {
550 ConExitOnLastError(hr, "failed to read string from console"); 493 ConExitOnLastError(hr, "failed to read string from console");
551 } 494 }
495
552 iReadCharTotal += *pcchNumCharReturn; 496 iReadCharTotal += *pcchNumCharReturn;
553 iRead += 1; 497 iRead += 1;
554 } 498 } while((*ppszCharBuffer)[iReadCharTotal - 1] != 10 || (*ppszCharBuffer)[iReadCharTotal - 2] != 13);
555 while((*ppszCharBuffer)[iReadCharTotal - 1] != 10 || (*ppszCharBuffer)[iReadCharTotal - 2] != 13); 499
556 *pcchNumCharReturn = iReadCharTotal; 500 *pcchNumCharReturn = iReadCharTotal;
557 } 501 }
558 else 502 else
@@ -562,6 +506,7 @@ extern "C" HRESULT DAPI ConsoleReadStringA(
562 { 506 {
563 ConExitOnLastError(hr, "failed to read string from console"); 507 ConExitOnLastError(hr, "failed to read string from console");
564 } 508 }
509
565 if ((*ppszCharBuffer)[*pcchNumCharReturn - 1] != 10 || 510 if ((*ppszCharBuffer)[*pcchNumCharReturn - 1] != 10 ||
566 (*ppszCharBuffer)[*pcchNumCharReturn - 2] != 13) 511 (*ppszCharBuffer)[*pcchNumCharReturn - 2] != 13)
567 { 512 {
@@ -579,18 +524,16 @@ LExit:
579 return hr; 524 return hr;
580} 525}
581 526
582/********************************************************************
583 ConsoleReadStringW - get console input without libc
584 527
585*********************************************************************/
586extern "C" HRESULT DAPI ConsoleReadStringW( 528extern "C" HRESULT DAPI ConsoleReadStringW(
587 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer, 529 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer,
588 const DWORD cchCharBuffer, 530 CONST DWORD cchCharBuffer,
589 __out DWORD* pcchNumCharReturn 531 __out DWORD* pcchNumCharReturn
590 ) 532 )
591{ 533{
592 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); 534 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
593 HRESULT hr = S_OK; 535 HRESULT hr = S_OK;
536
594 if (ppwzCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2)) 537 if (ppwzCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2))
595 { 538 {
596 DWORD iRead = 1; 539 DWORD iRead = 1;
@@ -601,15 +544,17 @@ extern "C" HRESULT DAPI ConsoleReadStringW(
601 { 544 {
602 hr = StrAlloc(ppwzCharBuffer, cchCharBuffer * iRead); 545 hr = StrAlloc(ppwzCharBuffer, cchCharBuffer * iRead);
603 ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW"); 546 ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW");
547
604 // ReadConsoleW will not return until <Return>, the last two chars are 13 and 10. 548 // ReadConsoleW will not return until <Return>, the last two chars are 13 and 10.
605 if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0) 549 if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0)
606 { 550 {
607 ConExitOnLastError(hr, "failed to read string from console"); 551 ConExitOnLastError(hr, "failed to read string from console");
608 } 552 }
553
609 iReadCharTotal += *pcchNumCharReturn; 554 iReadCharTotal += *pcchNumCharReturn;
610 iRead += 1; 555 iRead += 1;
611 } 556 } while((*ppwzCharBuffer)[iReadCharTotal - 1] != 10 || (*ppwzCharBuffer)[iReadCharTotal - 2] != 13);
612 while((*ppwzCharBuffer)[iReadCharTotal - 1] != 10 || (*ppwzCharBuffer)[iReadCharTotal - 2] != 13); 557
613 *pcchNumCharReturn = iReadCharTotal; 558 *pcchNumCharReturn = iReadCharTotal;
614 } 559 }
615 else 560 else
@@ -619,6 +564,7 @@ extern "C" HRESULT DAPI ConsoleReadStringW(
619 { 564 {
620 ConExitOnLastError(hr, "failed to read string from console"); 565 ConExitOnLastError(hr, "failed to read string from console");
621 } 566 }
567
622 if ((*ppwzCharBuffer)[*pcchNumCharReturn - 1] != 10 || 568 if ((*ppwzCharBuffer)[*pcchNumCharReturn - 1] != 10 ||
623 (*ppwzCharBuffer)[*pcchNumCharReturn - 2] != 13) 569 (*ppwzCharBuffer)[*pcchNumCharReturn - 2] != 13)
624 { 570 {
@@ -636,14 +582,12 @@ LExit:
636 return hr; 582 return hr;
637} 583}
638 584
639/********************************************************************
640 ConsoleSetReadHidden - set console input no echo
641 585
642*********************************************************************/
643extern "C" HRESULT DAPI ConsoleSetReadHidden(void) 586extern "C" HRESULT DAPI ConsoleSetReadHidden(void)
644{ 587{
645 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); 588 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
646 HRESULT hr = S_OK; 589 HRESULT hr = S_OK;
590
647 ::FlushConsoleInputBuffer(vhStdIn); 591 ::FlushConsoleInputBuffer(vhStdIn);
648 if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT)) 592 if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT))
649 { 593 {
@@ -654,14 +598,12 @@ LExit:
654 return hr; 598 return hr;
655} 599}
656 600
657/********************************************************************
658 ConsoleSetReadNormal - reset to echo
659 601
660*********************************************************************/
661extern "C" HRESULT DAPI ConsoleSetReadNormal(void) 602extern "C" HRESULT DAPI ConsoleSetReadNormal(void)
662{ 603{
663 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called"); 604 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
664 HRESULT hr = S_OK; 605 HRESULT hr = S_OK;
606
665 if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT)) 607 if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT))
666 { 608 {
667 ConExitOnLastError(hr, "failed to set console input mode to be normal"); 609 ConExitOnLastError(hr, "failed to set console input mode to be normal");
@@ -671,3 +613,123 @@ LExit:
671 return hr; 613 return hr;
672} 614}
673 615
616
617static HRESULT DAPI ReadInteractiveStdIn(
618 __deref_out_z LPWSTR* ppwzBuffer
619)
620{
621 HRESULT hr = S_OK;
622 LPWSTR pwz = NULL;
623 DWORD cch = 8;
624 DWORD cchRead = 0;
625 DWORD cchTotalRead = 0;
626
627 hr = StrAlloc(&pwz, cch);
628 ConExitOnFailure(hr, "failed to allocate memory to read from console");
629
630 // loop until we read the \r\n from the console
631 for (;;)
632 {
633 // read one character at a time, since that seems to be the only way to make this work
634 if (!::ReadConsoleW(vhStdIn, pwz + cchTotalRead, 1, &cchRead, NULL))
635 {
636 ConExitOnLastError(hr, "failed to read string from console");
637 }
638
639 cchTotalRead += cchRead;
640 if (0 == cchRead) // nothing more was read
641 {
642 pwz[cchTotalRead] = L'\0'; // null terminate and bail
643 break;
644 }
645 else if (0 < cchTotalRead && L'\n' == pwz[cchTotalRead - 1])
646 {
647 if (1 < cchTotalRead && L'\r' == pwz[cchTotalRead - 2])
648 {
649 pwz[cchTotalRead - 2] = L'\0'; // chop off the \r\n
650 }
651 else
652 {
653 pwz[cchTotalRead - 1] = L'\0'; // chop off the \n
654 }
655
656 break;
657 }
658
659 if (cchTotalRead == cch)
660 {
661 cch *= 2; // double everytime we run out of space
662 hr = StrAlloc(&pwz, cch);
663 ConExitOnFailure(hr, "failed to allocate memory to read from console");
664 }
665 }
666
667 hr = StrAllocString(ppwzBuffer, pwz, 0);
668 ConExitOnFailure(hr, "failed to copy stdin buffer to return buffer");
669
670LExit:
671 ReleaseStr(pwz);
672 return hr;
673}
674
675
676static HRESULT ReadRedirectedStdIn(
677 __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
678 __out DWORD* pcchSize,
679 BOOL fReadLine,
680 DWORD dwMaxRead
681)
682{
683 HRESULT hr = S_OK;
684 LPSTR psz = NULL;
685 DWORD cch = 8;
686 DWORD cchRead = 0;
687 DWORD cchTotalRead = 0;
688
689 hr = StrAnsiAlloc(&psz, cch);
690 ConExitOnFailure(hr, "failed to allocate memory to read from console");
691
692 while (dwMaxRead == 0 || cchTotalRead < dwMaxRead)
693 {
694 // read one character at a time, since that seems to be the only way to make this work
695 if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL))
696 {
697 ConExitOnLastError(hr, "failed to read string from console");
698 }
699
700 cchTotalRead += cchRead;
701 if (0 == cchRead) // nothing more was read
702 {
703 psz[cchTotalRead] = '\0'; // null terminate and bail
704 break;
705 }
706 else if (fReadLine && 0 < cchTotalRead && '\n' == psz[cchTotalRead - 1])
707 {
708 if (1 < cchTotalRead && '\r' == psz[cchTotalRead - 2])
709 {
710 psz[cchTotalRead - 2] = '\0'; // chop off the \r\n
711 }
712 else
713 {
714 psz[cchTotalRead - 1] = '\0'; // chop off the \n
715 }
716
717 break;
718 }
719
720 if (cchTotalRead == cch)
721 {
722 cch *= 2; // double everytime we run out of space
723 hr = StrAnsiAlloc(&psz, cch);
724 ConExitOnFailure(hr, "failed to allocate memory to read from console");
725 }
726 }
727
728 *pcchSize = cchTotalRead;
729
730 hr = StrAllocStringAnsi(ppwzBuffer, psz, 0, CP_UTF8);
731 ConExitOnFailure(hr, "failed to convert console data");
732
733LExit:
734 return hr;
735}
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/conutil.h b/src/libs/dutil/WixToolset.DUtil/inc/conutil.h
index 38aaea84..92e7f600 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/conutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/conutil.h
@@ -33,16 +33,46 @@ void DAPI ConsoleRed();
33void DAPI ConsoleYellow(); 33void DAPI ConsoleYellow();
34void DAPI ConsoleNormal(); 34void DAPI ConsoleNormal();
35 35
36/********************************************************************
37 ConsoleWrite - full color printfA without libc
38
39 NOTE: only supports ANSI characters
40 assumes already in normal color and resets the screen to normal color
41********************************************************************/
36HRESULT DAPI ConsoleWrite( 42HRESULT DAPI ConsoleWrite(
37 CONSOLE_COLOR cc, 43 CONSOLE_COLOR cc,
38 __in_z __format_string LPCSTR szFormat, 44 __in_z __format_string LPCSTR szFormat,
39 ... 45 ...
40 ); 46 );
47
48/********************************************************************
49 ConsoleWriteW - sends UTF-8 characters to console out in color.
50
51 NOTE: assumes already in normal color and resets the screen to normal color
52********************************************************************/
53HRESULT DAPI ConsoleWriteW(
54 __in CONSOLE_COLOR cc,
55 __in_z LPCWSTR wzData
56 );
57
58/********************************************************************
59 ConsoleWriteLine - full color printfA plus newline without libc
60
61 NOTE: only supports ANSI characters
62 assumes already in normal color and resets the screen to normal color
63********************************************************************/
41HRESULT DAPI ConsoleWriteLine( 64HRESULT DAPI ConsoleWriteLine(
42 CONSOLE_COLOR cc, 65 CONSOLE_COLOR cc,
43 __in_z __format_string LPCSTR szFormat, 66 __in_z __format_string LPCSTR szFormat,
44 ... 67 ...
45 ); 68 );
69
70/********************************************************************
71 ConsoleWriteError - display an error to the console out
72
73 NOTE: only supports ANSI characters
74 does not write to stderr
75********************************************************************/
46HRESULT DAPI ConsoleWriteError( 76HRESULT DAPI ConsoleWriteError(
47 HRESULT hrError, 77 HRESULT hrError,
48 CONSOLE_COLOR cc, 78 CONSOLE_COLOR cc,
@@ -50,28 +80,58 @@ HRESULT DAPI ConsoleWriteError(
50 ... 80 ...
51 ); 81 );
52 82
83/********************************************************************
84 ConsoleReadW - reads a line from console in as UTF-8 to populate Unicode buffer
85
86********************************************************************/
53HRESULT DAPI ConsoleReadW( 87HRESULT DAPI ConsoleReadW(
54 __deref_out_z LPWSTR* ppwzBuffer 88 __deref_out_z LPWSTR* ppwzBuffer
55 ); 89 );
56 90
91/********************************************************************
92 ConsoleReadNonBlockingW - Read from the console without blocking
93 Won't work for redirected files (exe < txtfile), but will work for stdin redirected to
94 an anonymous or named pipe
95
96 if (fReadLine), stop reading immediately when \r\n is found
97*********************************************************************/
98HRESULT DAPI ConsoleReadNonBlockingW(
99 __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
100 __out DWORD* pcchSize,
101 BOOL fReadLine
102 );
103
104/********************************************************************
105 ConsoleReadStringA - get console input without libc
106
107 NOTE: only supports ANSI characters
108*********************************************************************/
57HRESULT DAPI ConsoleReadStringA( 109HRESULT DAPI ConsoleReadStringA(
58 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* szCharBuffer, 110 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* szCharBuffer,
59 CONST DWORD cchCharBuffer, 111 CONST DWORD cchCharBuffer,
60 __out DWORD* pcchNumCharReturn 112 __out DWORD* pcchNumCharReturn
61 ); 113 );
114
115/********************************************************************
116 ConsoleReadStringW - get console input without libc
117
118*********************************************************************/
62HRESULT DAPI ConsoleReadStringW( 119HRESULT DAPI ConsoleReadStringW(
63 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* szCharBuffer, 120 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* szCharBuffer,
64 CONST DWORD cchCharBuffer, 121 CONST DWORD cchCharBuffer,
65 __out DWORD* pcchNumCharReturn 122 __out DWORD* pcchNumCharReturn
66 ); 123 );
67 124
68HRESULT DAPI ConsoleReadNonBlockingW( 125/********************************************************************
69 __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, 126 ConsoleSetReadHidden - set console input no echo
70 __out DWORD* pcchSize,
71 BOOL fReadLine
72 );
73 127
128*********************************************************************/
74HRESULT DAPI ConsoleSetReadHidden(void); 129HRESULT DAPI ConsoleSetReadHidden(void);
130
131/********************************************************************
132 ConsoleSetReadNormal - reset to echo
133
134*********************************************************************/
75HRESULT DAPI ConsoleSetReadNormal(void); 135HRESULT DAPI ConsoleSetReadNormal(void);
76 136
77#ifdef __cplusplus 137#ifdef __cplusplus