master
cpp 106 lines 2.42 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 InputChannel.cpp
8
9 Abstract:
10
11 Implementation of InputChannel.
12
13 --*/
14 #include "precomp.h"
15 #include "InputChannel.h"
16
17 #include <wil/resource.h>
18
19 namespace wsl::windows::wslc {
20
21 InputChannel::InputChannel(HANDLE consoleHandle, FILE* readFile) : m_file(readFile)
22 {
23 DWORD mode = 0;
24 if (consoleHandle != INVALID_HANDLE_VALUE && consoleHandle != nullptr && GetConsoleMode(consoleHandle, &mode))
25 {
26 m_consoleHandle = consoleHandle;
27 }
28 }
29
30 InputChannel::InputChannel(FILE* readFile, bool interactiveOverride) :
31 m_file(readFile), m_interactiveOverride(interactiveOverride)
32 {
33 }
34
35 bool InputChannel::IsInteractive() const noexcept
36 {
37 if (m_consoleHandle != nullptr)
38 {
39 DWORD mode = 0;
40 return GetConsoleMode(m_consoleHandle, &mode) != FALSE;
41 }
42
43 return m_interactiveOverride;
44 }
45
46 std::optional<std::wstring> InputChannel::ReadLine(bool mask) const
47 {
48 if (m_file == nullptr)
49 {
50 return std::nullopt;
51 }
52
53 // Disable console echo while reading when masking is requested and input is a
54 // real console. Armed only after echo is actually disabled so the restore is a
55 // no-op otherwise; runs on every exit path including exceptions.
56 DWORD previousMode = 0;
57 bool echoDisabled = false;
58 auto restoreEcho = wil::scope_exit([&]() {
59 if (echoDisabled)
60 {
61 LOG_IF_WIN32_BOOL_FALSE(SetConsoleMode(m_consoleHandle, previousMode));
62 }
63 });
64
65 if (mask && m_consoleHandle != nullptr && GetConsoleMode(m_consoleHandle, &previousMode))
66 {
67 // Fail rather than echo a secret if the mode cannot be changed.
68 THROW_IF_WIN32_BOOL_FALSE(SetConsoleMode(m_consoleHandle, previousMode & ~ENABLE_ECHO_INPUT));
69 echoDisabled = true;
70 }
71
72 std::wstring line;
73 bool anyRead = false;
74 for (;;)
75 {
76 const wint_t ch = fgetwc(m_file);
77 if (ch == WEOF)
78 {
79 break;
80 }
81
82 anyRead = true;
83 if (ch == L'\n')
84 {
85 break;
86 }
87
88 line.push_back(static_cast<wchar_t>(ch));
89 }
90
91 if (!anyRead)
92 {
93 return std::nullopt;
94 }
95
96 // The read stops at LF; strip a paired CR so callers get a bare line regardless
97 // of the input's line-ending convention.
98 if (!line.empty() && line.back() == L'\r')
99 {
100 line.pop_back();
101 }
102
103 return line;
104 }
105
106 } // namespace wsl::windows::wslc