FreeRDP
unicode_icu.c
1 
22 #include <winpr/config.h>
23 #include <winpr/assert.h>
24 
25 #include <errno.h>
26 #include <wctype.h>
27 
28 #include <winpr/crt.h>
29 #include <winpr/error.h>
30 #include <winpr/print.h>
31 
32 #ifndef MIN
33 #define MIN(a, b) (a) < (b) ? (a) : (b)
34 #endif
35 
36 #include <unicode/ucnv.h>
37 #include <unicode/ustring.h>
38 
39 #include "unicode.h"
40 
41 #include "../log.h"
42 #define TAG WINPR_TAG("unicode")
43 
44 #define UCNV_CONVERT 1
45 
46 int int_MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte,
47  LPWSTR lpWideCharStr, int cchWideChar)
48 {
49  const BOOL isNullTerminated = cbMultiByte < 0;
50 
51  WINPR_UNUSED(dwFlags);
52 
53  /* If cbMultiByte is 0, the function fails */
54 
55  if ((cbMultiByte == 0) || (cbMultiByte < -1))
56  {
57  SetLastError(ERROR_INVALID_PARAMETER);
58  return 0;
59  }
60 
61  size_t len = 0;
62  if (isNullTerminated)
63  len = strlen(lpMultiByteStr) + 1;
64  else
65  len = cbMultiByte;
66 
67  if (len >= INT_MAX)
68  {
69  SetLastError(ERROR_INVALID_PARAMETER);
70  return 0;
71  }
72  cbMultiByte = (int)len;
73 
74  /*
75  * if cchWideChar is 0, the function returns the required buffer size
76  * in characters for lpWideCharStr and makes no use of the output parameter itself.
77  */
78  {
79  UErrorCode error = U_ZERO_ERROR;
80  int32_t targetLength = -1;
81 
82  switch (CodePage)
83  {
84  case CP_ACP:
85  case CP_UTF8:
86  break;
87 
88  default:
89  WLog_ERR(TAG, "Unsupported encoding %u", CodePage);
90  SetLastError(ERROR_INVALID_PARAMETER);
91  return 0;
92  }
93 
94  const int32_t targetCapacity = cchWideChar;
95 #if defined(UCNV_CONVERT)
96  char* targetStart = (char*)lpWideCharStr;
97  targetLength =
98  ucnv_convert("UTF-16LE", "UTF-8", targetStart, targetCapacity * (int32_t)sizeof(WCHAR),
99  lpMultiByteStr, cbMultiByte, &error);
100  if (targetLength > 0)
101  targetLength /= sizeof(WCHAR);
102 #else
103  WCHAR* targetStart = lpWideCharStr;
104  u_strFromUTF8(targetStart, targetCapacity, &targetLength, lpMultiByteStr, cbMultiByte,
105  &error);
106 #endif
107 
108  switch (error)
109  {
110  case U_BUFFER_OVERFLOW_ERROR:
111  if (targetCapacity > 0)
112  {
113  cchWideChar = 0;
114  WLog_ERR(TAG, "insufficient buffer supplied, got %d, required %d",
115  targetCapacity, targetLength);
116  SetLastError(ERROR_INSUFFICIENT_BUFFER);
117  }
118  else
119  cchWideChar = targetLength;
120  break;
121  case U_STRING_NOT_TERMINATED_WARNING:
122  cchWideChar = targetLength;
123  break;
124  case U_ZERO_ERROR:
125  cchWideChar = targetLength;
126  break;
127  default:
128  WLog_WARN(TAG, "unexpected ICU error code %s [0x%08" PRIx32 "]", u_errorName(error),
129  error);
130  if (U_FAILURE(error))
131  {
132  WLog_ERR(TAG, "unexpected ICU error code %s [0x%08" PRIx32 "] is fatal",
133  u_errorName(error), error);
134  cchWideChar = 0;
135  SetLastError(ERROR_NO_UNICODE_TRANSLATION);
136  }
137  else
138  cchWideChar = targetLength;
139  break;
140  }
141  }
142 
143  return cchWideChar;
144 }
145 
146 int int_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar,
147  LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar,
148  LPBOOL lpUsedDefaultChar)
149 {
150  /* If cchWideChar is 0, the function fails */
151 
152  if ((cchWideChar == 0) || (cchWideChar < -1))
153  {
154  SetLastError(ERROR_INVALID_PARAMETER);
155  return 0;
156  }
157 
158  /* If cchWideChar is -1, the string is null-terminated */
159 
160  size_t len = 0;
161  if (cchWideChar == -1)
162  len = _wcslen(lpWideCharStr) + 1;
163  else
164  len = cchWideChar;
165 
166  if (len >= INT32_MAX)
167  {
168  SetLastError(ERROR_INVALID_PARAMETER);
169  return 0;
170  }
171  cchWideChar = (int)len;
172 
173  /*
174  * if cbMultiByte is 0, the function returns the required buffer size
175  * in bytes for lpMultiByteStr and makes no use of the output parameter itself.
176  */
177  {
178  UErrorCode error = U_ZERO_ERROR;
179  int32_t targetLength = -1;
180 
181  switch (CodePage)
182  {
183  case CP_ACP:
184  case CP_UTF8:
185  break;
186 
187  default:
188  WLog_ERR(TAG, "Unsupported encoding %u", CodePage);
189  SetLastError(ERROR_INVALID_PARAMETER);
190  return 0;
191  }
192 
193  char* targetStart = lpMultiByteStr;
194  const int32_t targetCapacity = cbMultiByte;
195 #if defined(UCNV_CONVERT)
196  const char* str = (const char*)lpWideCharStr;
197  targetLength = ucnv_convert("UTF-8", "UTF-16LE", targetStart, targetCapacity, str,
198  cchWideChar * (int32_t)sizeof(WCHAR), &error);
199 #else
200  u_strToUTF8(targetStart, targetCapacity, &targetLength, lpWideCharStr, cchWideChar, &error);
201 #endif
202  switch (error)
203  {
204  case U_BUFFER_OVERFLOW_ERROR:
205  if (targetCapacity > 0)
206  {
207  WLog_ERR(TAG, "insufficient buffer supplied, got %d, required %d",
208  targetCapacity, targetLength);
209  cbMultiByte = 0;
210  SetLastError(ERROR_INSUFFICIENT_BUFFER);
211  }
212  else
213  cbMultiByte = targetLength;
214  break;
215  case U_STRING_NOT_TERMINATED_WARNING:
216  cbMultiByte = targetLength;
217  break;
218  case U_ZERO_ERROR:
219  cbMultiByte = targetLength;
220  break;
221  default:
222  WLog_WARN(TAG, "unexpected ICU error code %s [0x%08" PRIx32 "]", u_errorName(error),
223  error);
224  if (U_FAILURE(error))
225  {
226  WLog_ERR(TAG, "unexpected ICU error code %s [0x%08" PRIx32 "] is fatal",
227  u_errorName(error), error);
228  cbMultiByte = 0;
229  SetLastError(ERROR_NO_UNICODE_TRANSLATION);
230  }
231  else
232  cbMultiByte = targetLength;
233  break;
234  }
235  }
236  return cbMultiByte;
237 }