1 //===-- MICmnStreamStdinWindows.cpp -----------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
11 // File: MIUtilStreamStdin.cpp
13 // Overview: CMICmnStreamStdinWindows implementation.
15 // Environment: Compilers: Visual C++ 12.
16 // gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1
17 // Libraries: See MIReadmetxt.
22 // Third Party Headers:
28 #endif // defined( _MSC_VER )
32 #include "MICmnStreamStdinWindows.h"
34 #include "MICmnResources.h"
35 #include "MIUtilSystemWindows.h"
36 #include "MIUtilSingletonHelper.h"
38 //++ ------------------------------------------------------------------------------------
39 // Details: CMICmnStreamStdinWindows constructor.
45 CMICmnStreamStdinWindows::CMICmnStreamStdinWindows(void)
46 : m_constBufferSize(1024)
48 , m_pCmdBuffer(nullptr)
49 , m_pStdinBuffer(nullptr)
51 , m_bRunningInConsoleWin(false)
55 //++ ------------------------------------------------------------------------------------
56 // Details: CMICmnStreamStdinWindows destructor.
62 CMICmnStreamStdinWindows::~CMICmnStreamStdinWindows(void)
67 //++ ------------------------------------------------------------------------------------
68 // Details: Initialize resources for *this Stdin stream.
71 // Return: MIstatus::success - Functional succeeded.
72 // MIstatus::failure - Functional failed.
76 CMICmnStreamStdinWindows::Initialize(void)
79 return MIstatus::success;
81 bool bOk = MIstatus::success;
84 // Note initialisation order is important here as some resources depend on previous
85 MI::ModuleInit<CMICmnLog>(IDS_MI_INIT_ERR_LOG, bOk, errMsg);
86 MI::ModuleInit<CMICmnResources>(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg);
88 // Other resources required
91 m_pCmdBuffer = new MIchar[m_constBufferSize];
94 #if MICONFIG_CREATE_OWN_STDIN_BUFFER
95 // Give stdinput a user defined buffer
96 m_pStdinBuffer = new char[1024];
97 ::setbuf(stdin, m_pStdinBuffer);
98 #endif // MICONFIG_CREATE_OWN_STDIN_BUFFER
100 // Clear error indicators for std input
103 #if defined(_MSC_VER)
104 m_bRunningInConsoleWin = ::_isatty(::fileno(stdin));
105 #endif // #if defined( _MSC_VER )
108 m_bInitialized = bOk;
112 CMIUtilString strInitError(CMIUtilString::Format(MIRSRC(IDS_MI_INIT_ERR_OS_STDIN_HANDLER), errMsg.c_str()));
113 SetErrorDescription(strInitError);
114 return MIstatus::failure;
117 return MIstatus::success;
120 //++ ------------------------------------------------------------------------------------
121 // Details: Release resources for *this Stdin stream.
124 // Return: MIstatus::success - Functional succeeded.
125 // MIstatus::failure - Functional failed.
129 CMICmnStreamStdinWindows::Shutdown(void)
132 return MIstatus::success;
134 m_bInitialized = false;
136 ClrErrorDescription();
138 bool bOk = MIstatus::success;
139 CMIUtilString errMsg;
142 if (m_pCmdBuffer != nullptr)
144 delete[] m_pCmdBuffer;
145 m_pCmdBuffer = nullptr;
149 #if MICONFIG_CREATE_OWN_STDIN_BUFFER
151 delete[] m_pStdinBuffer;
152 m_pStdinBuffer = nullptr;
153 #endif // MICONFIG_CREATE_OWN_STDIN_BUFFER
155 // Note shutdown order is important here
156 MI::ModuleShutdown<CMICmnResources>(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg);
157 MI::ModuleShutdown<CMICmnLog>(IDS_MI_INIT_ERR_LOG, bOk, errMsg);
161 SetErrorDescriptionn(MIRSRC(IDS_MI_SHTDWN_ERR_OS_STDIN_HANDLER), errMsg.c_str());
164 return MIstatus::success;
167 //++ ------------------------------------------------------------------------------------
168 // Details: Determine if stdin has any characters present in its buffer.
170 // Args: vwbAvail - (W) True = There is chars available, false = nothing there.
171 // Return: MIstatus::success - Functional succeeded.
172 // MIstatus::failure - Functional failed.
176 CMICmnStreamStdinWindows::InputAvailable(bool &vwbAvail)
178 return m_bRunningInConsoleWin ? InputAvailableConsoleWin(vwbAvail) : InputAvailableApplication(vwbAvail);
181 //++ ------------------------------------------------------------------------------------
182 // Details: Determine if stdin has any characters present in its buffer. If running in a
183 // terminal use _kbhit().
185 // Args: vwbAvail - (W) True = There is chars available, false = nothing there.
186 // Return: MIstatus::success - Functional succeeded.
187 // MIstatus::failure - Functional failed.
191 CMICmnStreamStdinWindows::InputAvailableConsoleWin(bool &vwbAvail)
193 #if defined(_MSC_VER)
194 if (m_nBytesToBeRead == 0)
196 // Get a windows handle to std input stream
197 HANDLE handle = ::GetStdHandle(STD_INPUT_HANDLE);
198 DWORD nBytesWaiting = ::_kbhit();
200 // Save the number of bytes to be read so that we can check if input is available to be read
201 m_nBytesToBeRead = nBytesWaiting;
203 // Return state of whether bytes are waiting or not
204 vwbAvail = (nBytesWaiting > 0);
206 #endif // #if defined( _MSC_VER )
208 return MIstatus::success;
211 //++ ------------------------------------------------------------------------------------
212 // Details: Determine if stdin has any characters present in its buffer.
214 // Args: vwbAvail - (W) True = There is chars available, false = nothing there.
215 // Return: MIstatus::success - Functional succeeded.
216 // MIstatus::failure - Functional failed.
220 CMICmnStreamStdinWindows::InputAvailableApplication(bool &vwbAvail)
222 #if defined(_MSC_VER)
223 if (m_nBytesToBeRead == 0)
225 // Get a windows handle to std input stream
226 HANDLE handle = ::GetStdHandle(STD_INPUT_HANDLE);
227 DWORD nBytesWaiting = 0;
229 // Ask how many bytes are available
230 if (::PeekNamedPipe(handle, nullptr, 0, nullptr, &nBytesWaiting, nullptr) == FALSE)
232 // This can occur when the client i.e. Eclipse closes the stdin stream 'cause it deems its work is finished
233 // for that debug session. May be we should be handling SIGKILL somehow?
234 const CMIUtilString osErrMsg(CMIUtilSystemWindows().GetOSLastError().StripCRAll());
235 SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_STDIN_ERR_CHKING_BYTE_AVAILABLE), osErrMsg.c_str()));
236 return MIstatus::failure;
239 // Save the number of bytes to be read so that we can check if input is available to be read
240 m_nBytesToBeRead = nBytesWaiting;
242 // Return state of whether bytes are waiting or not
243 vwbAvail = (nBytesWaiting > 0);
245 #endif // #if defined( _MSC_VER )
247 return MIstatus::success;
250 //++ ------------------------------------------------------------------------------------
251 // Details: Wait on new line of data from stdin stream (completed by '\n' or '\r').
253 // Args: vwErrMsg - (W) Empty string ok or error description.
254 // Return: MIchar * - text buffer pointer or NULL on failure.
258 CMICmnStreamStdinWindows::ReadLine(CMIUtilString &vwErrMsg)
263 const MIchar *pText = ::fgets(&m_pCmdBuffer[0], m_constBufferSize, stdin);
264 if (pText == nullptr)
266 if (::ferror(m_pStdin) != 0)
267 vwErrMsg = ::strerror(errno);
271 // Subtract the number of bytes read so that we can check if input is available to be read
272 m_nBytesToBeRead = m_nBytesToBeRead - ::strlen(pText);
274 // Strip off new line characters
275 for (MIchar *pI = m_pCmdBuffer; *pI != '\0'; pI++)
277 if ((*pI == '\n') || (*pI == '\r'))