diff options
Diffstat (limited to 'winpr/libwinpr/crt/unicode_android.c')
-rw-r--r-- | winpr/libwinpr/crt/unicode_android.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/winpr/libwinpr/crt/unicode_android.c b/winpr/libwinpr/crt/unicode_android.c new file mode 100644 index 0000000..2e9bac5 --- /dev/null +++ b/winpr/libwinpr/crt/unicode_android.c @@ -0,0 +1,183 @@ +/** + * WinPR: Windows Portable Runtime + * Unicode Conversion (CRT) + * + * Copyright 2022 Armin Novak <anovak@thincast.com> + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/config.h> +#include <winpr/assert.h> +#include <winpr/string.h> + +#include "../utils/android.h" + +#ifndef MIN +#define MIN(a, b) (a) < (b) ? (a) : (b) +#endif + +#include "../log.h" +#define TAG WINPR_TAG("unicode") + +static int convert_int(JNIEnv* env, const void* data, size_t size, void* buffer, size_t buffersize, + BOOL toUTF16) +{ + WINPR_ASSERT(env); + WINPR_ASSERT(data || (size == 0)); + WINPR_ASSERT(buffer || (buffersize == 0)); + + jstring utf8 = (*env)->NewStringUTF(env, "UTF-8"); + jstring utf16 = (*env)->NewStringUTF(env, "UTF-16LE"); + jclass stringClass = (*env)->FindClass(env, "java/lang/String"); + + if (!utf8 || !utf16 || !stringClass) + { + WLog_ERR(TAG, "utf8-%p, utf16=%p, stringClass=%p", utf8, utf16, stringClass); + return -1; + } + + jmethodID constructorID = + (*env)->GetMethodID(env, stringClass, "<init>", "([BLjava/lang/String;)V"); + jmethodID getBytesID = + (*env)->GetMethodID(env, stringClass, "getBytes", "(Ljava/lang/String;)[B"); + if (!constructorID || !getBytesID) + { + WLog_ERR(TAG, "constructorID=%p, getBytesID=%p", constructorID, getBytesID); + return -2; + } + + jbyteArray ret = (*env)->NewByteArray(env, size); + if (!ret) + { + WLog_ERR(TAG, "NewByteArray(%" PRIuz ") failed", size); + return -3; + } + + (*env)->SetByteArrayRegion(env, ret, 0, size, data); + + jobject obj = (*env)->NewObject(env, stringClass, constructorID, ret, toUTF16 ? utf8 : utf16); + if (!obj) + { + WLog_ERR(TAG, "NewObject(String, byteArray, UTF-%d) failed", toUTF16 ? 16 : 8); + return -4; + } + + jbyteArray res = (*env)->CallObjectMethod(env, obj, getBytesID, toUTF16 ? utf16 : utf8); + if (!res) + { + WLog_ERR(TAG, "CallObjectMethod(String, getBytes, UTF-%d) failed", toUTF16 ? 16 : 8); + return -4; + } + + jsize rlen = (*env)->GetArrayLength(env, res); + if (buffersize > 0) + { + if (rlen > buffersize) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + rlen = MIN(rlen, buffersize); + (*env)->GetByteArrayRegion(env, res, 0, rlen, buffer); + } + + if (toUTF16) + rlen /= sizeof(WCHAR); + + return rlen; +} + +static int convert(const void* data, size_t size, void* buffer, size_t buffersize, BOOL toUTF16) +{ + int rc; + JNIEnv* env = NULL; + jboolean attached = winpr_jni_attach_thread(&env); + rc = convert_int(env, data, size, buffer, buffersize, toUTF16); + if (attached) + winpr_jni_detach_thread(); + return rc; +} + +int int_MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, + LPWSTR lpWideCharStr, int cchWideChar) +{ + size_t cbCharLen = (size_t)cbMultiByte; + + WINPR_UNUSED(dwFlags); + + /* If cbMultiByte is 0, the function fails */ + if ((cbMultiByte == 0) || (cbMultiByte < -1)) + return 0; + + if (cchWideChar < 0) + return -1; + + if (cbMultiByte < 0) + { + const size_t len = strlen(lpMultiByteStr); + if (len >= INT32_MAX) + return 0; + cbCharLen = (int)len + 1; + } + else + cbCharLen = cbMultiByte; + + WINPR_ASSERT(lpMultiByteStr); + switch (CodePage) + { + case CP_ACP: + case CP_UTF8: + break; + + default: + WLog_ERR(TAG, "Unsupported encoding %u", CodePage); + return 0; + } + + return convert(lpMultiByteStr, cbCharLen, lpWideCharStr, cchWideChar * sizeof(WCHAR), TRUE); +} + +int int_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, + LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, + LPBOOL lpUsedDefaultChar) +{ + size_t cbCharLen = (size_t)cchWideChar; + + WINPR_UNUSED(dwFlags); + /* If cchWideChar is 0, the function fails */ + if ((cchWideChar == 0) || (cchWideChar < -1)) + return 0; + + if (cbMultiByte < 0) + return -1; + + WINPR_ASSERT(lpWideCharStr); + /* If cchWideChar is -1, the string is null-terminated */ + if (cchWideChar == -1) + { + const size_t len = _wcslen(lpWideCharStr); + if (len >= INT32_MAX) + return 0; + cbCharLen = (int)len + 1; + } + else + cbCharLen = cchWideChar; + + /* + * if cbMultiByte is 0, the function returns the required buffer size + * in bytes for lpMultiByteStr and makes no use of the output parameter itself. + */ + return convert(lpWideCharStr, cbCharLen * sizeof(WCHAR), lpMultiByteStr, cbMultiByte, FALSE); +} |