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