blob: 23922de75960530b6532b4ec490ddc28ec533a83 [file] [log] [blame]
/* OS/2 iconv() implementation through OS/2 Unicode API
Copyright (C) 2001-2002 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
/*
This file implements an iconv wrapper based on OS/2 Unicode API.
*/
#include <uconv.h>
typedef struct _iconv_t
{
UconvObject from; /* "From" conversion handle */
UconvObject to; /* "To" conversion handle */
} *iconv_t;
/* Tell "iconv.h" to not define iconv_t by itself. */
#define _ICONV_T
#include "iconv.h"
#include <string.h>
#include <malloc.h>
#include <errno.h>
#include <alloca.h>
/* Convert an encoding name to te form understood by UniCreateUconvObject. */
static inline void
cp_convert (const char *cp, UniChar *ucp)
{
size_t sl = 0;
if (!stricmp (cp, "EUC-JP"))
memcpy (ucp, L"IBM-954", 8*2);
else if (!stricmp (cp, "EUC-KR"))
memcpy (ucp, L"IBM-970", 8*2);
else if (!stricmp (cp, "EUC-TW"))
memcpy (ucp, L"IBM-964", 8*2);
else if (!stricmp (cp, "EUC-CN"))
memcpy (ucp, L"IBM-1383", 9*2);
else if (!stricmp (cp, "BIG5"))
memcpy (ucp, L"IBM-950", 8*2);
else
{
/* Transform CPXXX naming style to IBM-XXX style */
if ((cp[0] == 'C' || cp[0] == 'c') && (cp[1] == 'P' || cp[1] == 'p'))
{
ucp[sl++] = 'I';
ucp[sl++] = 'B';
ucp[sl++] = 'M';
ucp[sl++] = '-';
cp += 2;
}
while (*cp != '\0')
ucp[sl++] = *cp++;
ucp[sl] = 0;
}
}
iconv_t
iconv_open (const char *cp_to, const char *cp_from)
{
UniChar *ucp;
iconv_t conv;
uconv_attribute_t attr;
conv = (iconv_t) malloc (sizeof (struct _iconv_t));
if (conv == NULL)
{
errno = ENOMEM;
return (iconv_t)(-1);
}
ucp = (UniChar *) alloca ((strlen (cp_from) + 2 + 1) * sizeof (UniChar));
cp_convert (cp_from, ucp);
if (UniCreateUconvObject (ucp, &conv->from))
{
free (conv);
errno = EINVAL;
return (iconv_t)(-1);
}
ucp = (UniChar *) alloca ((strlen (cp_to) + 2 + 1) * sizeof (UniChar));
cp_convert (cp_to, ucp);
if (UniCreateUconvObject (ucp, &conv->to))
{
UniFreeUconvObject (conv->from);
free (conv);
errno = EINVAL;
return (iconv_t)(-1);
}
UniQueryUconvObject (conv->from, &attr, sizeof (attr), NULL, NULL, NULL);
/* Do not treat 0x7f as a control character
(don't understand what it exactly means but without it MBCS prefix
character detection sometimes could fail (when 0x7f is a prefix)).
And don't treat the string as a path (the docs also don't explain
what it exactly means, but I'm pretty sure converted texts will
mostly not be paths). */
attr.converttype &= ~(CVTTYPE_CTRL7F | CVTTYPE_PATH);
UniSetUconvObject (conv->from, &attr);
return conv;
}
size_t
iconv (iconv_t conv,
const char **in, size_t *in_left,
char **out, size_t *out_left)
{
int rc;
size_t sl = *in_left, nonid;
UniChar *ucs = (UniChar *) alloca (sl * sizeof (UniChar));
UniChar *orig_ucs = ucs;
size_t retval = 0;
rc = UniUconvToUcs (conv->from, (void **)in, in_left, &ucs, &sl, &retval);
if (rc)
goto error;
sl = ucs - orig_ucs;
ucs = orig_ucs;
/* UniUconvFromUcs will stop at first nul byte (huh? indeed?)
while we want ALL the bytes converted. */
#if 1
rc = UniUconvFromUcs (conv->to, &ucs, &sl, (void **)out, out_left, &nonid);
if (rc)
goto error;
retval += nonid;
#else
while (sl)
{
size_t usl = 0;
while (sl && (ucs[usl] != 0))
usl++, sl--;
rc = UniUconvFromUcs (conv->to, &ucs, &usl, (void **)out, out_left, &nonid);
if (rc)
goto error;
retval += nonid;
if (sl && *out_left)
{
*(*out)++ = 0;
(*out_left)--;
ucs++; sl--;
}
}
#endif
return 0;
error:
/* Convert OS/2 error code to errno. */
switch (rc)
{
case ULS_ILLEGALSEQUENCE:
errno = EILSEQ;
break;
case ULS_INVALID:
errno = EINVAL;
break;
case ULS_BUFFERFULL:
errno = E2BIG;
break;
default:
errno = EBADF;
break;
}
return (size_t)(-1);
}
int
iconv_close (iconv_t conv)
{
if (conv != (iconv_t)(-1))
{
UniFreeUconvObject (conv->to);
UniFreeUconvObject (conv->from);
free (conv);
}
return 0;
}