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