#include "xh_config.h"
#include "xh_core.h"
#ifdef XH_HAVE_ENCODER
#ifdef XH_HAVE_ICU
static void
xh_encoder_uconv_destroy(UConverter *uconv)
{
if (uconv != NULL) {
ucnv_close(uconv);
}
}
static UConverter *
xh_encoder_uconv_create(xh_char_t *encoding, xh_bool_t toUnicode)
{
UConverter *uconv;
UErrorCode status = U_ZERO_ERROR;
uconv = ucnv_open((char *) encoding, &status);
if ( U_FAILURE(status) ) {
return NULL;
}
if (toUnicode) {
ucnv_setToUCallBack(uconv, UCNV_TO_U_CALLBACK_STOP,
NULL, NULL, NULL, &status);
}
else {
ucnv_setFromUCallBack(uconv, UCNV_FROM_U_CALLBACK_STOP,
NULL, NULL, NULL, &status);
}
return uconv;
}
#endif
void
xh_encoder_destroy(xh_encoder_t *encoder)
{
if (encoder != NULL) {
#ifdef XH_HAVE_ICONV
if (encoder->iconv != NULL) {
xh_log_debug0("destroy iconv encoder");
iconv_close(encoder->iconv);
}
#endif
#ifdef XH_HAVE_ICU
if (encoder->uconv_from != NULL) {
xh_log_debug0("destroy icu encoder");
xh_encoder_uconv_destroy(encoder->uconv_from);
xh_encoder_uconv_destroy(encoder->uconv_to);
}
#endif
free(encoder);
}
}
xh_encoder_t *
xh_encoder_create(xh_char_t *tocode, xh_char_t *fromcode)
{
xh_encoder_t *encoder;
encoder = malloc(sizeof(xh_encoder_t));
if (encoder == NULL) {
return NULL;
}
memset(encoder, 0, sizeof(xh_encoder_t));
xh_str_copy(encoder->tocode, tocode, XH_PARAM_LEN);
xh_str_copy(encoder->fromcode, fromcode, XH_PARAM_LEN);
#ifdef XH_HAVE_ICONV
xh_log_debug2("create iconv encoder from: '%s' to: '%s'", fromcode, tocode);
encoder->iconv = iconv_open((char *) tocode, (char *) fromcode);
if (encoder->iconv != (iconv_t) -1) {
encoder->type = XH_ENC_ICONV;
return encoder;
}
iconv_close(encoder->iconv);
encoder->iconv = NULL;
#endif
#ifdef XH_HAVE_ICU
xh_log_debug2("create icu encoder from: '%s' to: '%s'", fromcode, tocode);
encoder->uconv_to = xh_encoder_uconv_create(tocode, 1);
if (encoder->uconv_to != NULL) {
encoder->uconv_from = xh_encoder_uconv_create(fromcode, 0);
if (encoder->uconv_from != NULL) {
encoder->type = XH_ENC_ICU;
encoder->pivotSource = encoder->pivotTarget = encoder->pivotStart = encoder->pivotBuffer;
encoder->pivotLimit = encoder->pivotBuffer + sizeof(encoder->pivotBuffer) / sizeof(encoder->pivotBuffer[0]);
return encoder;
}
}
#endif
xh_encoder_destroy(encoder);
return NULL;
}
void
xh_encoder_encode_perl_buffer(xh_encoder_t *encoder, xh_perl_buffer_t *main_buf, xh_perl_buffer_t *enc_buf)
{
xh_char_t *src = main_buf->start;
#ifdef XH_HAVE_ICONV
if (encoder->type == XH_ENC_ICONV) {
size_t in_left = main_buf->cur - main_buf->start;
size_t out_left = enc_buf->end - enc_buf->cur;
size_t converted = iconv(encoder->iconv, (char **) &src, &in_left, (char **) &enc_buf->cur, &out_left);
if (converted == (size_t) -1) {
croak("Encoding error");
}
return;
}
#endif
#ifdef XH_HAVE_ICU
UErrorCode err = U_ZERO_ERROR;
ucnv_convertEx(encoder->uconv_to, encoder->uconv_from, (char **) &enc_buf->cur, (char *) enc_buf->end,
(const char **) &src, (char *) main_buf->cur, NULL, NULL, NULL, NULL,
FALSE, TRUE, &err);
if ( U_FAILURE(err) ) {
croak("Encoding error: %d", err);
}
#endif
}
void
xh_encoder_encode_string(xh_encoder_t *encoder, xh_char_t **src, size_t *src_left, xh_char_t **dst, size_t *dst_left)
{
#ifdef XH_HAVE_ICONV
if (encoder->type == XH_ENC_ICONV) {
size_t converted = iconv(encoder->iconv, (char **) src, src_left, (char **) dst, dst_left);
if (converted == (size_t) -1) {
switch (errno) {
case EILSEQ:
croak("Encoding error: invalid char found");
case E2BIG:
encoder->state = XH_ENC_BUFFER_OVERFLOW;
break;
case EINVAL:
encoder->state = XH_ENC_TRUNCATED_CHAR_FOUND;
break;
default:
croak("Encoding error");
}
}
else {
encoder->state = XH_ENC_OK;
}
return;
}
#endif
#ifdef XH_HAVE_ICU
UErrorCode err = U_ZERO_ERROR;
xh_char_t *old_src = *src;
xh_char_t *old_dst = *dst;
ucnv_convertEx(encoder->uconv_to, encoder->uconv_from, (char **) dst, (char *) (*dst + *dst_left),
(const char **) src, (char *) (*src + *src_left), encoder->pivotStart, &encoder->pivotSource, &encoder->pivotTarget, encoder->pivotLimit,
FALSE, FALSE, &err);
*src_left -= *src - old_src;
*dst_left -= *dst - old_dst;
if ( U_FAILURE(err) ) {
switch (err) {
case U_INVALID_CHAR_FOUND:
croak("Encoding error: invalid char found");
case U_BUFFER_OVERFLOW_ERROR:
encoder->state = XH_ENC_BUFFER_OVERFLOW;
break;
case U_TRUNCATED_CHAR_FOUND:
encoder->state = XH_ENC_TRUNCATED_CHAR_FOUND;
break;
default:
croak("Encoding error: %d", err);
}
}
else {
encoder->state = XH_ENC_OK;
}
#endif
}
#endif /* XH_HAVE_ENCODER */