/* * STFL - The Structured Terminal Forms Language/Library * Copyright (C) 2007 Clifford Wolf * * This library 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 3 of the License, or (at your option) any later version. * * This library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA * * iconv.c: Helper functions for widechar conversion */ #include "stfl.h" #include #include #include #include #include #include struct stfl_ipool_entry { void *data; struct stfl_ipool_entry *next; }; struct stfl_ipool { iconv_t to_wc_desc; iconv_t from_wc_desc; char *code; struct stfl_ipool_entry *list; pthread_mutex_t mtx; }; struct stfl_ipool *stfl_ipool_create(const char *code) { struct stfl_ipool *pool = malloc(sizeof(struct stfl_ipool)); pthread_mutex_init(&pool->mtx, NULL); pool->to_wc_desc = (iconv_t)(-1); pool->from_wc_desc = (iconv_t)(-1); pool->code = strdup(code); pool->list = 0; return pool; } void *stfl_ipool_add(struct stfl_ipool *pool, void *data) { struct stfl_ipool_entry *entry = malloc(sizeof(struct stfl_ipool_entry)); pthread_mutex_lock(&pool->mtx); entry->data = data; entry->next = pool->list; pool->list = entry; pthread_mutex_unlock(&pool->mtx); return data; } const wchar_t *stfl_ipool_towc(struct stfl_ipool *pool, const char *buf) { if (!pool || !buf) return 0; pthread_mutex_lock(&pool->mtx); if (!strcmp("WCHAR_T", pool->code)) { pthread_mutex_unlock(&pool->mtx); return (wchar_t*)buf; } if (pool->to_wc_desc == (iconv_t)(-1)) pool->to_wc_desc = iconv_open("WCHAR_T", pool->code); if (pool->to_wc_desc == (iconv_t)(-1)) { pthread_mutex_unlock(&pool->mtx); return 0; } char *inbuf = (char*)buf; size_t inbytesleft = strlen(buf); int buffer_size = inbytesleft * 2 + 16; int buffer_pos = 0; char *buffer = NULL; grow_buffer_retry:; buffer_size += inbytesleft * 2; buffer = realloc(buffer, buffer_size); retry_without_growing:; char *outbuf = buffer + buffer_pos; size_t outbytesleft = buffer_size - buffer_pos; iconv(pool->to_wc_desc, NULL, NULL, NULL, NULL); int rc = iconv(pool->to_wc_desc, &inbuf, &inbytesleft, &outbuf, &outbytesleft); buffer_pos = outbuf - buffer; if (rc == -1 && errno == E2BIG) goto grow_buffer_retry; if (rc == -1 && (errno == EILSEQ || errno == EINVAL)) { // just copy this char as it is (e.g. when input is broken utf-8 with some latin1 chars) if (outbytesleft < sizeof(wchar_t)) goto grow_buffer_retry; *((wchar_t*)outbuf) = *(unsigned char*)inbuf; buffer_pos += sizeof(wchar_t); inbuf++; inbytesleft--; goto retry_without_growing; } if (rc == -1) { free(buffer); pthread_mutex_unlock(&pool->mtx); return 0; } if (outbytesleft < sizeof(wchar_t)) buffer = realloc(buffer, buffer_size+sizeof(wchar_t)); *((wchar_t*)outbuf) = 0; pthread_mutex_unlock(&pool->mtx); return stfl_ipool_add(pool, buffer); } const char *stfl_ipool_fromwc(struct stfl_ipool *pool, const wchar_t *buf) { if (!pool || !buf) return 0; pthread_mutex_lock(&pool->mtx); if (!strcmp("WCHAR_T", pool->code)) { pthread_mutex_unlock(&pool->mtx); return (char*)buf; } if (pool->from_wc_desc == (iconv_t)(-1)) pool->from_wc_desc = iconv_open(pool->code, "WCHAR_T"); if (pool->from_wc_desc == (iconv_t)(-1)) { pthread_mutex_unlock(&pool->mtx); return 0; } char *inbuf = (char*)buf; size_t inbytesleft = wcslen(buf)*sizeof(wchar_t); int buffer_size = inbytesleft + 16; int buffer_pos = 0; char *buffer = NULL; grow_buffer_retry:; buffer_size += inbytesleft; buffer = realloc(buffer, buffer_size); retry_without_growing:; char *outbuf = buffer + buffer_pos; size_t outbytesleft = buffer_size - buffer_pos; iconv(pool->from_wc_desc, NULL, NULL, NULL, NULL); int rc = iconv(pool->from_wc_desc, &inbuf, &inbytesleft, &outbuf, &outbytesleft); buffer_pos = outbuf - buffer; if (rc == -1 && errno == E2BIG) goto grow_buffer_retry; if (rc == -1 && (errno == EILSEQ || errno == EINVAL)) { // just copy a '?' to the output stream if (outbytesleft < 1) goto grow_buffer_retry; *outbuf = '?'; buffer_pos++; inbuf += sizeof(wchar_t); inbytesleft -= sizeof(wchar_t); goto retry_without_growing; } if (rc == -1) { free(buffer); pthread_mutex_unlock(&pool->mtx); return 0; } if (outbytesleft < 1) buffer = realloc(buffer, buffer_size+1); *outbuf = 0; pthread_mutex_unlock(&pool->mtx); return stfl_ipool_add(pool, buffer); } void stfl_ipool_flush(struct stfl_ipool *pool) { if (!pool) return; pthread_mutex_lock(&pool->mtx); while (pool->list) { struct stfl_ipool_entry *l = pool->list; pool->list = l->next; free(l->data); free(l); } pthread_mutex_unlock(&pool->mtx); } void stfl_ipool_destroy(struct stfl_ipool *pool) { if (!pool) return; stfl_ipool_flush(pool); free(pool->code); if (pool->to_wc_desc != (iconv_t)(-1)) iconv_close(pool->to_wc_desc); if (pool->from_wc_desc != (iconv_t)(-1)) iconv_close(pool->from_wc_desc); free(pool); }