diff options
author | Rob Mensching <rob@firegiant.com> | 2022-11-17 16:05:22 -0800 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2022-11-21 09:12:41 -0800 |
commit | 83a63bdc1943187e93749420a3408cd6248eedb9 (patch) | |
tree | 617e931eddbf3ed96839ad4e100421e627a1ced5 /src/libs | |
parent | 307e3f1bb587b93ad386bbfd9b2d4603a72d80c4 (diff) | |
download | wix-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.cpp | 374 | ||||
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/inc/conutil.h | 70 |
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 | ||
21 | LPCSTR NEWLINE = "\r\n"; | ||
22 | |||
21 | static HANDLE vhStdIn = INVALID_HANDLE_VALUE; | 23 | static HANDLE vhStdIn = INVALID_HANDLE_VALUE; |
22 | static HANDLE vhStdOut = INVALID_HANDLE_VALUE; | 24 | static HANDLE vhStdOut = INVALID_HANDLE_VALUE; |
23 | static BOOL vfConsoleIn = FALSE; | 25 | static BOOL vfStdInInteractive = FALSE; |
26 | static BOOL vfStdOutInteractive = FALSE; | ||
24 | static BOOL vfConsoleOut = FALSE; | 27 | static BOOL vfConsoleOut = FALSE; |
25 | static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo; | 28 | static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo; |
26 | 29 | ||
27 | 30 | ||
31 | static HRESULT DAPI ReadInteractiveStdIn( | ||
32 | __deref_out_z LPWSTR* ppwzBuffer | ||
33 | ); | ||
34 | static HRESULT ReadRedirectedStdIn( | ||
35 | __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer, | ||
36 | __out DWORD* pcchSize, | ||
37 | BOOL fReadLine, | ||
38 | DWORD dwMaxRead | ||
39 | ); | ||
40 | |||
41 | |||
28 | extern "C" HRESULT DAPI ConsoleInitialize() | 42 | extern "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 | ||
84 | LExit: | 75 | LExit: |
@@ -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 | ********************************************************************/ | ||
172 | extern "C" HRESULT DAPI ConsoleWrite( | 156 | extern "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 | /******************************************************************** | 207 | extern "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 | |||
248 | LExit: | ||
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 | ********************************************************************/ | ||
229 | extern "C" HRESULT DAPI ConsoleWriteLine( | 254 | extern "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 | ********************************************************************/ | ||
295 | HRESULT ConsoleWriteError( | 314 | HRESULT 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 | ********************************************************************/ | ||
331 | extern "C" HRESULT DAPI ConsoleReadW( | 345 | extern "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 | ||
377 | LExit: | 366 | LExit: |
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 | *********************************************************************/ | ||
390 | extern "C" HRESULT DAPI ConsoleReadNonBlockingW( | 371 | extern "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 | ||
518 | LExit: | 464 | LExit: |
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 | *********************************************************************/ | ||
529 | extern "C" HRESULT DAPI ConsoleReadStringA( | 471 | extern "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 | *********************************************************************/ | ||
586 | extern "C" HRESULT DAPI ConsoleReadStringW( | 528 | extern "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 | *********************************************************************/ | ||
643 | extern "C" HRESULT DAPI ConsoleSetReadHidden(void) | 586 | extern "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 | *********************************************************************/ | ||
661 | extern "C" HRESULT DAPI ConsoleSetReadNormal(void) | 602 | extern "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 | |||
617 | static 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 | |||
670 | LExit: | ||
671 | ReleaseStr(pwz); | ||
672 | return hr; | ||
673 | } | ||
674 | |||
675 | |||
676 | static 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 | |||
733 | LExit: | ||
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(); | |||
33 | void DAPI ConsoleYellow(); | 33 | void DAPI ConsoleYellow(); |
34 | void DAPI ConsoleNormal(); | 34 | void 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 | ********************************************************************/ | ||
36 | HRESULT DAPI ConsoleWrite( | 42 | HRESULT 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 | ********************************************************************/ | ||
53 | HRESULT 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 | ********************************************************************/ | ||
41 | HRESULT DAPI ConsoleWriteLine( | 64 | HRESULT 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 | ********************************************************************/ | ||
46 | HRESULT DAPI ConsoleWriteError( | 76 | HRESULT 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 | ********************************************************************/ | ||
53 | HRESULT DAPI ConsoleReadW( | 87 | HRESULT 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 | *********************************************************************/ | ||
98 | HRESULT 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 | *********************************************************************/ | ||
57 | HRESULT DAPI ConsoleReadStringA( | 109 | HRESULT 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 | *********************************************************************/ | ||
62 | HRESULT DAPI ConsoleReadStringW( | 119 | HRESULT 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 | ||
68 | HRESULT 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 | *********************************************************************/ | ||
74 | HRESULT DAPI ConsoleSetReadHidden(void); | 129 | HRESULT DAPI ConsoleSetReadHidden(void); |
130 | |||
131 | /******************************************************************** | ||
132 | ConsoleSetReadNormal - reset to echo | ||
133 | |||
134 | *********************************************************************/ | ||
75 | HRESULT DAPI ConsoleSetReadNormal(void); | 135 | HRESULT DAPI ConsoleSetReadNormal(void); |
76 | 136 | ||
77 | #ifdef __cplusplus | 137 | #ifdef __cplusplus |