1
1
s-lang/modules/iconv-module.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)
{
}