| /* |
| Simple DirectMedia Layer |
| Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org> |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely, subject to the following restrictions: |
| |
| 1. The origin of this software must not be misrepresented; you must not |
| claim that you wrote the original software. If you use this software |
| in a product, an acknowledgment in the product documentation would be |
| appreciated but is not required. |
| 2. Altered source versions must be plainly marked as such, and must not be |
| misrepresented as being the original software. |
| 3. This notice may not be removed or altered from any source distribution. |
| */ |
| |
| /* |
| Implementation iconv via OS/2 conversion objects API. |
| |
| Andrey Vasilkin. |
| */ |
| |
| #define ICONV_THREAD_SAFE 1 |
| |
| #include "geniconv.h" |
| #define _ULS_CALLCONV_ |
| #define CALLCONV _System |
| #include <uconv.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #ifdef ICONV_THREAD_SAFE |
| #define INCL_DOSSEMAPHORES |
| #define INCL_DOSERRORS |
| #include <os2.h> |
| #endif |
| #include "os2cp.h" |
| |
| #if !defined(min) |
| #define min(a, b) (((a) < (b)) ? (a) : (b)) |
| #endif |
| |
| #define MAX_CP_NAME_LEN 64 |
| |
| typedef struct iuconv_obj { |
| UconvObject uo_tocode; |
| UconvObject uo_fromcode; |
| int buf_len; |
| UniChar *buf; |
| #ifdef ICONV_THREAD_SAFE |
| HMTX hMtx; |
| #endif |
| } iuconv_obj; |
| |
| |
| static int _createUconvObj(const char *code, UconvObject *uobj) |
| { |
| UniChar uc_code[MAX_CP_NAME_LEN]; |
| int i; |
| const char *ch = code; |
| |
| if (code == NULL) |
| uc_code[0] = 0; |
| else { |
| for (i = 0; i < MAX_CP_NAME_LEN; i++) { |
| uc_code[i] = (unsigned short)*ch; |
| if (! (*ch)) |
| break; |
| ch++; |
| } |
| } |
| |
| return UniCreateUconvObject(uc_code, uobj); |
| } |
| |
| static int uconv_open(const char *code, UconvObject *uobj) |
| { |
| int rc; |
| |
| if (!stricmp(code, "UTF-16")) { |
| *uobj = NULL; |
| return ULS_SUCCESS; |
| } |
| |
| rc = _createUconvObj(code, uobj); |
| if (rc != ULS_SUCCESS) { |
| unsigned long cp = os2cpFromName((char *)code); |
| char cp_name[16]; |
| |
| if (cp != 0 && _snprintf(cp_name, sizeof(cp_name), "IBM-%u", cp) > 0) |
| rc = _createUconvObj(cp_name, uobj); |
| } |
| |
| return rc; |
| } |
| |
| |
| extern iconv_t _System os2_iconv_open(const char* tocode, const char* fromcode) |
| { |
| UconvObject uo_tocode; |
| UconvObject uo_fromcode; |
| int rc; |
| iuconv_obj *iuobj; |
| |
| if (tocode == NULL) |
| tocode = ""; |
| |
| if (fromcode == NULL) |
| fromcode = ""; |
| |
| if (stricmp(tocode, fromcode) != 0) { |
| rc = uconv_open(fromcode, &uo_fromcode); |
| if (rc != ULS_SUCCESS) { |
| errno = EINVAL; |
| return (iconv_t)(-1); |
| } |
| |
| rc = uconv_open(tocode, &uo_tocode); |
| if (rc != ULS_SUCCESS) { |
| UniFreeUconvObject(uo_fromcode); |
| errno = EINVAL; |
| return (iconv_t)(-1); |
| } |
| } else { |
| uo_tocode = NULL; |
| uo_fromcode = NULL; |
| } |
| |
| iuobj = (iuconv_obj *) malloc(sizeof(iuconv_obj)); |
| iuobj->uo_tocode = uo_tocode; |
| iuobj->uo_fromcode = uo_fromcode; |
| iuobj->buf_len = 0; |
| iuobj->buf = NULL; |
| #ifdef ICONV_THREAD_SAFE |
| DosCreateMutexSem(NULL, &iuobj->hMtx, 0, FALSE); |
| #endif |
| |
| return iuobj; |
| } |
| |
| extern size_t _System os2_iconv(iconv_t cd, char* * inbuf, |
| size_t *inbytesleft, |
| char* * outbuf, size_t *outbytesleft) |
| { |
| UconvObject uo_tocode = ((iuconv_obj *)(cd))->uo_tocode; |
| UconvObject uo_fromcode = ((iuconv_obj *)(cd))->uo_fromcode; |
| size_t nonIdenticalConv = 0; |
| UniChar *uc_buf; |
| size_t uc_buf_len; |
| UniChar **uc_str; |
| size_t *uc_str_len; |
| int rc; |
| size_t ret = (size_t)(-1); |
| |
| if (uo_tocode == NULL && uo_fromcode == NULL) { |
| uc_buf_len = min(*inbytesleft, *outbytesleft); |
| memcpy(*outbuf, *inbuf, uc_buf_len); |
| *inbytesleft -= uc_buf_len; |
| *outbytesleft -= uc_buf_len; |
| outbuf += uc_buf_len; |
| inbuf += uc_buf_len; |
| return uc_buf_len; |
| } |
| |
| #ifdef ICONV_THREAD_SAFE |
| DosRequestMutexSem(((iuconv_obj *)(cd))->hMtx, SEM_INDEFINITE_WAIT); |
| #endif |
| |
| if (uo_tocode && uo_fromcode && |
| (((iuconv_obj *)cd)->buf_len >> 1) < *inbytesleft) { |
| if (((iuconv_obj *)cd)->buf != NULL) |
| free(((iuconv_obj *)cd)->buf); |
| ((iuconv_obj *)cd)->buf_len = *inbytesleft << 1; |
| ((iuconv_obj *)cd)->buf = (UniChar *)malloc(((iuconv_obj *)cd)->buf_len); |
| } |
| |
| if (uo_fromcode) { |
| if (uo_tocode) { |
| uc_buf = ((iuconv_obj *)cd)->buf; |
| uc_buf_len = ((iuconv_obj *)cd)->buf_len; |
| uc_str = &uc_buf; |
| } else { |
| uc_str = (UniChar **)outbuf; |
| uc_buf_len = *outbytesleft; |
| } |
| uc_buf_len = uc_buf_len >> 1; |
| uc_str_len = &uc_buf_len; |
| rc = UniUconvToUcs(uo_fromcode, (void **)inbuf, inbytesleft, |
| uc_str, uc_str_len, &nonIdenticalConv); |
| uc_buf_len = uc_buf_len << 1; |
| if (!uo_tocode) |
| *outbytesleft = uc_buf_len; |
| |
| if (rc != ULS_SUCCESS) { |
| errno = EILSEQ; |
| goto done; |
| } else if (*inbytesleft && !*uc_str_len) { |
| errno = E2BIG; |
| goto done; |
| } |
| |
| if (!uo_tocode) |
| return nonIdenticalConv; |
| |
| uc_buf = ((iuconv_obj *)cd)->buf; |
| uc_buf_len = ((iuconv_obj *)cd)->buf_len - uc_buf_len; |
| uc_str = &uc_buf; |
| uc_str_len = &uc_buf_len; |
| } else { |
| uc_str = (UniChar **)inbuf; |
| uc_str_len = inbytesleft; |
| } |
| |
| *uc_str_len = *uc_str_len>>1; |
| rc = UniUconvFromUcs(uo_tocode, uc_str, uc_str_len, (void **)outbuf, |
| outbytesleft, &nonIdenticalConv); |
| if (rc != ULS_SUCCESS) { |
| switch (rc) { |
| case ULS_BUFFERFULL: |
| errno = E2BIG; |
| break; |
| case ULS_ILLEGALSEQUENCE: |
| errno = EILSEQ; |
| break; |
| case ULS_INVALID: |
| errno = EINVAL; |
| break; |
| } |
| goto done; |
| } else if (*uc_str_len && !*outbytesleft) { |
| errno = E2BIG; |
| goto done; |
| } |
| |
| ret = nonIdenticalConv; |
| |
| done: |
| |
| #ifdef ICONV_THREAD_SAFE |
| DosReleaseMutexSem(((iuconv_obj *)cd)->hMtx); |
| #endif |
| return ret; |
| } |
| |
| int _System os2_iconv_close(iconv_t cd) |
| { |
| if (!cd) return 0; |
| |
| #ifdef ICONV_THREAD_SAFE |
| DosCloseMutexSem(((iuconv_obj *)cd)->hMtx); |
| #endif |
| if (((iuconv_obj *)cd)->uo_tocode != NULL) |
| UniFreeUconvObject(((iuconv_obj *)cd)->uo_tocode); |
| if (((iuconv_obj *)cd)->uo_fromcode != NULL) |
| UniFreeUconvObject(((iuconv_obj *)cd)->uo_fromcode); |
| |
| if (((iuconv_obj *)cd)->buf != NULL) |
| free(((iuconv_obj *)cd)->buf); |
| |
| free(cd); |
| |
| return 0; |
| } |
| |
| /* vi: set ts=4 sw=4 expandtab: */ |