/*
--------------------------------------------------------------------
The PHP License, version 3.01
Copyright (c) 1999 - 2008 The PHP Group. All rights reserved.
--------------------------------------------------------------------
"This product includes PHP software, freely available from
<http://www.php.net/software/>".
*/
#include <stdlib.h>
#include <string.h>
#include <iconv.h>
#include <errno.h>
//from PHP 4.4.9 ext/iconv/iconv.c
//为了便于在 blog 上表现,把返回简化成 3, 2, 1, 0, -1, -2, -3
//返回值非零表示转码失败,其中大于零则说明返回的 out 不需要 free
//这段代码只在 Linux 下测试
int _iconv_string(const char *in_p, size_t in_len, char **out, size_t *out_len, const char *in_charset, const char *out_charset)
{
iconv_t cd;
size_t in_left, out_size, out_left;
char *out_p, *out_buf, *tmp_buf;
size_t bsz, result = 0;
int retval = 0;
cd = iconv_open(out_charset, in_charset);
if (cd == (iconv_t)(-1)) {
if (errno == EINVAL) {
return 1; // WRONG_CHARSET
} else {
return 2;
}
}
in_left= in_len;
out_left = in_len + 32; /* Avoid realloc() most cases */
out_size = 0;
bsz = out_left;
out_buf = (char *)malloc(bsz+1);
out_p = out_buf;
while (in_left > 0) {
result = iconv(cd, (char **) &in_p, &in_left, (char **) &out_p, &out_left);
out_size = bsz - out_left;
if (result == (size_t)(-1)) {
if (errno == E2BIG && in_left > 0) {
/* converted string is longer than out buffer */
bsz += in_len;
tmp_buf = (char*)realloc(out_buf, bsz+1);
if (tmp_buf != NULL) {
out_p = out_buf = tmp_buf;
out_p += out_size;
out_left = bsz - out_size;
continue;
}
}
}
break;
}
if (result != (size_t)(-1)) {
/* flush the shift-out sequences */
for (;;) {
result = iconv(cd, NULL, NULL, (char **) &out_p, &out_left);
out_size = bsz - out_left;
if (result != (size_t)(-1)) {
break;
}
if (errno == E2BIG) {
bsz += 16;
tmp_buf = (char *)realloc(out_buf, bsz);
if (tmp_buf == NULL) {
break;
}
out_p = out_buf = tmp_buf;
out_p += out_size;
out_left = bsz - out_size;
} else {
break;
}
}
}
iconv_close(cd);
if (result == (size_t)(-1)) {
switch (errno) {
case EINVAL:
retval = -1;
break;
case EILSEQ:
retval = -2;
break;
case E2BIG:
retval = -3;
break;
default:
free(out_buf);
return 3;
}
}
*out_p = '\0';
*out = out_buf;
*out_len = out_size;
return retval;
}
//qyb wrap _iconv_string
//由调用者负责 free 返回值
char *iconv_string(const char *string, const char *in_charset, const char *out_charset)
{
char *result;
int retval;
size_t in_len, out_len;
in_len = strlen(string);
retval = _iconv_string(string, in_len, &result, &out_len, in_charset, out_charset);
if (retval > 0) {
return NULL;
}
return result;
}
GNU iconv
不在标准库里
没记错的话libiconv支持的平台不多……
也可以用mbtowc,wctomb,setlocale代替iconv
现在已经是最新的 POSIX (IEEE Std
现在已经是最新的 POSIX (IEEE Std 1003.1, 2004) 的一部分了