blob: de954546a0fc0b81da1d94c6a7035556340428a4 [file] [log] [blame]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iconv.h>
#include <errno.h>
#include <stddef.h>
struct outbuf
{
struct outbuf *next;
char *outptr;
size_t outbytesleft;
char buf[256];
};
char *eazyiconv(const char *to, const char *from,
char *str, size_t str_blen, size_t str_elemsize, size_t out_tailzero_blen, size_t *out_size,
const char *replchr)
{
char *retstr = NULL;
struct outbuf *outhead = NULL;
struct outbuf *outtail = NULL;
struct outbuf *outiter = NULL;
iconv_t cd = NULL;
char *inptr = str;
size_t inbytesleft = str_blen;
int retval = 0;
int err = 0;
size_t blocksize = 0;
size_t totalsize = 0;
char *retiter = NULL;
unsigned int chrval = 0;
iconv_t cdreplchr = NULL;
char replchrfmtbuf[256] = "";
char replchrbuf[256] = "";
char *replchrfmtptr = replchrfmtbuf;
size_t replchrfmtleft = sizeof replchrfmtbuf;
char *replchrptr = replchrbuf;
size_t replchrleft = sizeof replchrbuf;
int replchr_blen = 0;
cd = iconv_open(to, from);
if (cd == (iconv_t)-1)
{
goto noclean;
}
outhead = outtail = calloc(1, sizeof(struct outbuf));
if (outtail == NULL)
{
goto clean_cd;
}
outtail->next = NULL;
outtail->outptr = outtail->buf;
outtail->outbytesleft = sizeof outtail->buf;
memset(outtail->buf, 0, sizeof outtail->buf);
while (1)
{
retval = iconv(cd, &inptr, &inbytesleft, &outtail->outptr, &outtail->outbytesleft);
if (retval == -1)
err = errno;
else
err = 0;
switch (err)
{
case 0:
outiter = calloc(1, sizeof(struct outbuf));
if (outiter == NULL)
{
goto clean_outbufs;
}
if (inptr == NULL) // succeeded cleanup iconv
{
goto succeeded;
}
else // fully succeeded iconv
{
inptr = NULL; // do cleanup iconv
inbytesleft = 0;
}
break;
case EINVAL: // incomplete tail sequence
case EILSEQ: // invalid sequence
chrval = 0;
memcpy(&chrval, inptr, str_elemsize > sizeof chrval ? sizeof chrval : str_elemsize);
snprintf(replchrfmtbuf, sizeof replchrfmtbuf, replchr, chrval);
inptr += str_elemsize;
inbytesleft -= str_elemsize;
cdreplchr = iconv_open(to, "UTF-8");
if (cdreplchr == (iconv_t)-1)
{
goto clean_outbufs;
}
replchrfmtptr = replchrfmtbuf;
replchrfmtleft = strlen(replchrfmtbuf);
replchrptr = replchrbuf;
replchrleft = sizeof replchrbuf;
iconv(cdreplchr, &replchrfmtptr, &replchrfmtleft, &replchrptr, &replchrleft);
iconv(cdreplchr, NULL, NULL, &replchrptr, &replchrleft);
iconv_close(cdreplchr);
replchr_blen = replchrptr - replchrbuf;
if (outtail->outbytesleft < replchr_blen)
{
outiter = calloc(1, sizeof(struct outbuf));
if (outiter == NULL)
{
goto clean_outbufs;
}
outtail->next = outiter;
outtail = outiter;
outtail->next = NULL;
outtail->outptr = outtail->buf;
outtail->outbytesleft = sizeof outtail->buf;
memset(outtail->buf, 0, sizeof outtail->buf);
}
memcpy(outtail->outptr, replchrbuf, replchr_blen);
outtail->outptr += replchr_blen;
outtail->outbytesleft -= replchr_blen;
break;
case E2BIG: // no enough space
outiter = calloc(1, sizeof(struct outbuf));
if (outiter == NULL)
{
goto clean_outbufs;
}
outtail->next = outiter;
outtail = outiter;
outtail->next = NULL;
outtail->outptr = outtail->buf;
outtail->outbytesleft = sizeof outtail->buf;
memset(outtail->buf, 0, sizeof outtail->buf);
break;
default:
break;
}
}
succeeded:
totalsize = 0;
for (outiter = outhead; outiter != NULL; outiter = outiter->next)
{
blocksize = outiter->outptr - outiter->buf;
totalsize += blocksize;
}
retstr = calloc(totalsize + out_tailzero_blen, 1);
if (retstr == NULL)
{
goto clean_outbufs;
}
retiter = retstr;
for (outiter = outhead; outiter != NULL; outiter = outiter->next)
{
blocksize = outiter->outptr - outiter->buf;
memcpy(retiter, outiter->buf, blocksize);
retiter += blocksize;
}
memset(retiter, 0, out_tailzero_blen);
*out_size = totalsize;
clean_outbufs:
while (outhead != NULL)
{
outiter = outhead;
outhead = outhead->next;
free(outiter);
}
outtail = NULL;
clean_cd:
iconv_close(cd);
noclean:
return retstr;
}
int main(int argc, char **argv)
{
if (argc < 7)
{
printf("usage: eiconv_test from_charset from_elemsize to_charset to_elemsize from_file to_file (no utf-16/32)\n");
return 0;
}
FILE *from_file = fopen(argv[5], "rb");
fseek(from_file, 0, SEEK_END);
off_t fsize = ftell(from_file);
fseek(from_file, 0, SEEK_SET);
char *from_str = malloc(fsize + 1);
fread(from_str, 1, fsize, from_file);
fclose(from_file);
size_t out_size = 0;
char *to_str = eazyiconv(argv[3], argv[1],
from_str, fsize, atoi(argv[2]), atoi(argv[4]), &out_size,
"<0x%02X>");
FILE *to_file = fopen(argv[6], "wb");
fwrite(to_str, 1, out_size, to_file);
free(to_str);
fclose(to_file);
return 0;
}