/* This code was written by Dino Leonardo Sangoi */ #include "config.h" #include #include #include #include #include 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) { }