[CONUTILS] Split stream.c into input and output stream modules.
[reactos.git] / sdk / lib / conutils / utils.c
1 /*
2 * PROJECT: ReactOS Console Utilities Library
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Base set of functions for loading string resources
5 * and message strings, and handle type identification.
6 * COPYRIGHT: Copyright 2017-2018 ReactOS Team
7 * Copyright 2017-2018 Hermes Belusca-Maito
8 */
9
10 /* FIXME: Temporary HACK before we cleanly support UNICODE functions */
11 #define UNICODE
12 #define _UNICODE
13
14 #include <windef.h>
15 #include <winbase.h>
16 #include <winnls.h>
17 #include <winuser.h> // MAKEINTRESOURCEW, RT_STRING
18 #include <wincon.h> // Console APIs (only if kernel32 support included)
19 #include <strsafe.h>
20
21 /* PSEH for SEH Support */
22 #include <pseh/pseh2.h>
23
24 // #include "conutils.h"
25 #include "utils.h"
26
27 /*
28 * General-purpose utility functions (wrappers around,
29 * or reimplementations of, Win32 APIs).
30 */
31
32 #if 0 // The following function may be useful in the future...
33
34 // Performs MultiByteToWideChar then WideCharToMultiByte .
35 // See https://github.com/pcman-bbs/pcman-windows/blob/master/Lite/StrUtils.h#l33
36 // and http://www.openfoundry.org/svn/pcman/branches/OpenPCMan_2009/Lite/StrUtils.cpp
37 // for the idea.
38 int
39 MultiByteToMultiByte(
40 // IN WORD wTranslations,
41 IN DWORD dwFlags,
42 IN UINT SrcCodePage,
43 IN LPCSTR lpSrcString,
44 IN int cbSrcChar,
45 IN UINT DestCodePage,
46 OUT LPSTR wDestString OPTIONAL,
47 IN int cbDestChar
48 );
49
50 #endif
51
52 /*
53 * 'LoadStringW' API ripped from user32.dll to remove
54 * any dependency of this library from user32.dll
55 */
56 INT
57 WINAPI
58 K32LoadStringW(
59 IN HINSTANCE hInstance OPTIONAL,
60 IN UINT uID,
61 OUT LPWSTR lpBuffer,
62 IN INT nBufferMax)
63 {
64 HRSRC hrsrc;
65 HGLOBAL hmem;
66 WCHAR *p;
67 UINT i;
68
69 if (!lpBuffer)
70 return 0;
71
72 /* Use LOWORD (incremented by 1) as ResourceID */
73 /* There are always blocks of 16 strings */
74 // FindResourceExW(hInstance, RT_STRING, name, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
75 // NOTE: Instead of using LANG_NEUTRAL, one might use LANG_USER_DEFAULT...
76 hrsrc = FindResourceW(hInstance,
77 MAKEINTRESOURCEW((LOWORD(uID) >> 4) + 1),
78 (LPWSTR)RT_STRING);
79 if (!hrsrc) return 0;
80
81 hmem = LoadResource(hInstance, hrsrc);
82 if (!hmem) return 0;
83
84 p = LockResource(hmem);
85 // FreeResource(hmem);
86
87 /* Find the string we're looking for */
88 uID &= 0x000F; /* Position in the block, same as % 16 */
89 for (i = 0; i < uID; i++)
90 p += *p + 1;
91
92 /*
93 * If nBufferMax == 0, then return a read-only pointer
94 * to the resource itself in lpBuffer it is assumed that
95 * lpBuffer is actually a (LPWSTR *).
96 */
97 if (nBufferMax == 0)
98 {
99 *((LPWSTR*)lpBuffer) = p + 1;
100 return *p;
101 }
102
103 i = min(nBufferMax - 1, *p);
104 if (i > 0)
105 {
106 memcpy(lpBuffer, p + 1, i * sizeof(WCHAR));
107 lpBuffer[i] = L'\0';
108 }
109 else
110 {
111 if (nBufferMax > 1)
112 {
113 lpBuffer[0] = L'\0';
114 return 0;
115 }
116 }
117
118 return i;
119 }
120
121 /*
122 * "Safe" version of FormatMessageW, that does not crash if a malformed
123 * source string is retrieved and then being used for formatting.
124 * It basically wraps calls to FormatMessageW within SEH.
125 */
126 DWORD
127 WINAPI
128 FormatMessageSafeW(
129 IN DWORD dwFlags,
130 IN LPCVOID lpSource OPTIONAL,
131 IN DWORD dwMessageId,
132 IN DWORD dwLanguageId,
133 OUT LPWSTR lpBuffer,
134 IN DWORD nSize,
135 IN va_list *Arguments OPTIONAL)
136 {
137 DWORD dwLength = 0;
138
139 _SEH2_TRY
140 {
141 /*
142 * Retrieve the message string. Wrap in SEH
143 * to protect from invalid string parameters.
144 */
145 _SEH2_TRY
146 {
147 dwLength = FormatMessageW(dwFlags,
148 lpSource,
149 dwMessageId,
150 dwLanguageId,
151 lpBuffer,
152 nSize,
153 Arguments);
154 }
155 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
156 {
157 dwLength = 0;
158
159 /*
160 * An exception occurred while calling FormatMessage, this is usually
161 * the sign that a parameter was invalid, either 'lpBuffer' was NULL
162 * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
163 * array pointer 'Arguments' was NULL or did not contain enough elements,
164 * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
165 * message string expected too many inserts.
166 * In this last case only, we can call again FormatMessage but ignore
167 * explicitely the inserts. The string that we will return to the user
168 * will not be pre-formatted.
169 */
170 if (((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) || lpBuffer) &&
171 !(dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS))
172 {
173 /* Remove any possible harmful flags and always ignore inserts */
174 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
175 dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
176
177 /* If this call also throws an exception, we are really dead */
178 dwLength = FormatMessageW(dwFlags,
179 lpSource,
180 dwMessageId,
181 dwLanguageId,
182 lpBuffer,
183 nSize,
184 NULL /* Arguments */);
185 }
186 }
187 _SEH2_END;
188 }
189 _SEH2_FINALLY
190 {
191 }
192 _SEH2_END;
193
194 return dwLength;
195 }
196
197 BOOL
198 IsTTYHandle(IN HANDLE hHandle)
199 {
200 /*
201 * More general test than IsConsoleHandle. Consoles, as well as
202 * serial ports, etc... verify this test, but only consoles verify
203 * the IsConsoleHandle test: indeed the latter checks whether
204 * the handle is really handled by the console subsystem.
205 */
206 return ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) == FILE_TYPE_CHAR);
207 }
208
209 BOOL
210 IsConsoleHandle(IN HANDLE hHandle)
211 {
212 DWORD dwMode;
213
214 /* Check whether the handle may be that of a console... */
215 if ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR)
216 return FALSE;
217
218 /*
219 * It may be. Perform another test. The idea comes from the
220 * MSDN description of the WriteConsole API:
221 *
222 * "WriteConsole fails if it is used with a standard handle
223 * that is redirected to a file. If an application processes
224 * multilingual output that can be redirected, determine whether
225 * the output handle is a console handle (one method is to call
226 * the GetConsoleMode function and check whether it succeeds).
227 * If the handle is a console handle, call WriteConsole. If the
228 * handle is not a console handle, the output is redirected and
229 * you should call WriteFile to perform the I/O."
230 */
231 return GetConsoleMode(hHandle, &dwMode);
232 }
233
234 /* EOF */