267 строки
5.5 KiB
C
267 строки
5.5 KiB
C
/* This code was written by Dino Leonardo Sangoi <SANGOID@lloydadriatico.it> */
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <slang.h>
|
|
#include <string.h>
|
|
#include <iconv.h>
|
|
#include <errno.h>
|
|
|
|
SLANG_MODULE(iconv);
|
|
|
|
#ifndef ICONV_CONST
|
|
# define ICONV_CONST
|
|
#endif
|
|
|
|
static int ICONV_Type_Id = 0;
|
|
|
|
typedef struct
|
|
{
|
|
iconv_t cd;
|
|
}
|
|
ICONV_Type;
|
|
|
|
static void free_iconv_type (ICONV_Type *it)
|
|
{
|
|
SLfree ((char *) it);
|
|
}
|
|
|
|
static SLang_MMT_Type *allocate_iconv_type (iconv_t cd)
|
|
{
|
|
ICONV_Type *it;
|
|
SLang_MMT_Type *mmt;
|
|
|
|
it = (ICONV_Type *) SLmalloc (sizeof (ICONV_Type));
|
|
if (it == NULL)
|
|
return NULL;
|
|
|
|
it->cd = cd;
|
|
|
|
if (NULL == (mmt = SLang_create_mmt (ICONV_Type_Id, (VOID_STAR) it)))
|
|
{
|
|
free_iconv_type (it);
|
|
return NULL;
|
|
}
|
|
return mmt;
|
|
}
|
|
|
|
static void _iconv_open(char *tocode, char *fromcode)
|
|
{
|
|
iconv_t cd;
|
|
SLang_MMT_Type *mmt;
|
|
|
|
cd = iconv_open(tocode, fromcode);
|
|
if (cd == (iconv_t)(-1))
|
|
{
|
|
SLang_verror (SL_INTRINSIC_ERROR, "Error preparing iconv to convert from '%s' to '%s'.", fromcode, tocode);
|
|
return;
|
|
}
|
|
|
|
if (NULL == (mmt = allocate_iconv_type (cd)))
|
|
{
|
|
iconv_close(cd);
|
|
return;
|
|
}
|
|
|
|
if (-1 == SLang_push_mmt (mmt))
|
|
{
|
|
SLang_free_mmt (mmt);
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void _iconv_close(ICONV_Type *it)
|
|
{
|
|
if (it->cd != (iconv_t)-1)
|
|
{
|
|
iconv_close(it->cd);
|
|
it->cd = (iconv_t)-1;
|
|
}
|
|
}
|
|
|
|
static void destroy_iconv (SLtype type, VOID_STAR f)
|
|
{
|
|
ICONV_Type *it;
|
|
(void) type;
|
|
|
|
it = (ICONV_Type *) f;
|
|
_iconv_close(it);
|
|
free_iconv_type (it);
|
|
}
|
|
|
|
static void _iconv_reset(ICONV_Type *it)
|
|
{
|
|
iconv(it->cd, NULL, NULL, NULL, NULL);
|
|
}
|
|
|
|
#define SHIFT_BUF_LEN 64U
|
|
static void _iconv_reset_shift(ICONV_Type *it)
|
|
{
|
|
size_t n = SHIFT_BUF_LEN;
|
|
char buf[SHIFT_BUF_LEN], *p = buf;
|
|
SLang_BString_Type *bstr;
|
|
size_t rc;
|
|
|
|
rc = iconv(it->cd, NULL, NULL, &p, &n);
|
|
if (rc == (size_t)(-1))
|
|
{
|
|
/* Since no input was provided, errno must be E2BIG */
|
|
SLang_verror (SL_Internal_Error, "Internal error: shift buffer too small in iconv_reset_shift!");
|
|
return;
|
|
}
|
|
buf[SHIFT_BUF_LEN-n] = '\0';
|
|
bstr = SLbstring_create((unsigned char *)buf, SHIFT_BUF_LEN-n);
|
|
if (bstr == NULL)
|
|
return;
|
|
|
|
(void) SLang_push_bstring(bstr);
|
|
SLbstring_free (bstr);
|
|
}
|
|
|
|
static void _iconv(ICONV_Type *it, SLang_BString_Type *bstr)
|
|
{
|
|
char *buf = NULL;
|
|
size_t rc;
|
|
char ICONV_CONST *instr;
|
|
char *outstr;
|
|
size_t inn, outn, bufn;
|
|
size_t fail = (size_t)-1;
|
|
SLstrlen_Type bstrlen;
|
|
|
|
if (NULL == (instr = (char ICONV_CONST *)SLbstring_get_pointer(bstr, &bstrlen)))
|
|
return;
|
|
inn = (size_t) bstrlen;
|
|
|
|
bufn = outn = 2*inn + 2;
|
|
if (NULL == (buf = (char *)SLmalloc(bufn)))
|
|
return;
|
|
|
|
outstr = buf;
|
|
|
|
while (1)
|
|
{
|
|
errno = 0;
|
|
rc = iconv(it->cd, &instr, &inn, &outstr, &outn);
|
|
if (rc != (size_t)-1)
|
|
break; /* ok! */
|
|
|
|
if (fail == inn)
|
|
{
|
|
SLang_verror (SL_Unknown_Error, "Unknown error in iconv");
|
|
goto error;
|
|
}
|
|
|
|
fail = inn;
|
|
switch (errno)
|
|
{
|
|
case EILSEQ:
|
|
SLang_verror (SL_InvalidUTF8_Error, "Invalid multibyte sequence or unable to convert to the target encoding");
|
|
goto error;
|
|
case EINVAL:
|
|
SLang_verror (SL_InvalidUTF8_Error, "Incomplete multibyte sequence");
|
|
goto error;
|
|
case 0:
|
|
/* drop */
|
|
/* grrrr
|
|
* At least on windows, libiconv returns with errno = 0
|
|
* (or unmodified?) when there's no more room on outstr
|
|
* if so, fallback
|
|
*/
|
|
case E2BIG:
|
|
{
|
|
char *p;
|
|
long outdelta;
|
|
|
|
outdelta = outstr - buf;
|
|
outn += bufn;
|
|
bufn += bufn;
|
|
p = (char *)SLrealloc(buf, bufn);
|
|
if (p == NULL)
|
|
goto error;
|
|
buf = p;
|
|
outstr = buf + outdelta;
|
|
}
|
|
break;
|
|
default:
|
|
SLang_verror (SL_Unknown_Error, "Unknown iconv error");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
bstr = SLbstring_create((unsigned char *) buf, outstr - buf);
|
|
if (bstr != NULL)
|
|
(void) SLang_push_bstring(bstr);
|
|
SLbstring_free (bstr);
|
|
/* drop */
|
|
error:
|
|
SLfree(buf);
|
|
}
|
|
|
|
#define DUMMY_ICONV_TYPE 255
|
|
#define P DUMMY_ICONV_TYPE
|
|
#define V SLANG_VOID_TYPE
|
|
#define S SLANG_STRING_TYPE
|
|
#define B SLANG_BSTRING_TYPE
|
|
static SLang_Intrin_Fun_Type ICONV_Intrinsics [] =
|
|
{
|
|
MAKE_INTRINSIC_2("iconv_open", _iconv_open, V, S, S),
|
|
MAKE_INTRINSIC_1("iconv_close", _iconv_close, V, P),
|
|
MAKE_INTRINSIC_1("iconv_reset", _iconv_reset, V, P),
|
|
MAKE_INTRINSIC_1("iconv_reset_shift", _iconv_reset_shift, V, P),
|
|
MAKE_INTRINSIC_2("iconv", _iconv, V, P, B),
|
|
SLANG_END_INTRIN_FUN_TABLE
|
|
};
|
|
|
|
#undef B
|
|
#undef S
|
|
#undef V
|
|
#undef P
|
|
|
|
static int register_iconv_type (void)
|
|
{
|
|
SLang_Class_Type *cl;
|
|
|
|
if (ICONV_Type_Id != 0)
|
|
return 0;
|
|
|
|
if (NULL == (cl = SLclass_allocate_class ("ICONV_Type")))
|
|
return -1;
|
|
|
|
if (-1 == SLclass_set_destroy_function (cl, destroy_iconv))
|
|
return -1;
|
|
|
|
/* By registering as SLANG_VOID_TYPE, slang will dynamically allocate a
|
|
* type.
|
|
*/
|
|
if (-1 == SLclass_register_class (cl, SLANG_VOID_TYPE, sizeof (ICONV_Type), SLANG_CLASS_TYPE_MMT))
|
|
return -1;
|
|
|
|
ICONV_Type_Id = SLclass_get_class_id (cl);
|
|
if (-1 == SLclass_patch_intrin_fun_table1 (ICONV_Intrinsics, DUMMY_ICONV_TYPE, ICONV_Type_Id))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int init_iconv_module_ns (char *ns_name)
|
|
{
|
|
SLang_NameSpace_Type *ns = SLns_create_namespace (ns_name);
|
|
if (ns == NULL)
|
|
return -1;
|
|
|
|
if (-1 == register_iconv_type ())
|
|
return -1;
|
|
|
|
if (-1 == SLns_add_intrin_fun_table (ns, ICONV_Intrinsics, "__ICONV__"))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This function is optional */
|
|
void deinit_iconv_module (void)
|
|
{
|
|
}
|