summaryrefslogtreecommitdiffstats
path: root/src/rcciconv.c
blob: 1add9d9123cd6ecfeeb314e6a57e222c1bf5bed7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <iconv.h>

#include "internal.h"
#include "rcciconv.h"

static void rccIConvCopySymbol(char **in_buf, int *in_left, char **out_buf, int *out_left) {
    if ((out_left>0)&&(in_left>0)) {
	(**out_buf)=(**in_buf);
	(*out_buf)++;
	(*in_buf)++;
	(*in_left)--;
	(*out_left)--;
    }
}

static int rccIConvUTFBytes(unsigned char c) {
    int j;
    if (c<128) return 1;

    for (j=6;j>=0;j--)
	if ((c&(1<<j))==0) break;
	    
    if ((j==0)||(j==6)) return 1;
    return 6-j;
}


rcc_iconv rccIConvOpen(const char *to, const char *from) {
    rcc_iconv icnv;
    
    if ((!from)||(!to)||(!strcasecmp(from,to))) return NULL;
    
    icnv = (rcc_iconv)malloc(sizeof(rcc_iconv_s));
    if (!icnv) return icnv;
    
    icnv->icnv = iconv_open(to, from);
    return icnv;
}

void rccIConvClose(rcc_iconv icnv) {
    if (icnv) {
	if (icnv->icnv != (iconv_t)-1) iconv_close(icnv->icnv);
	free(icnv);
    }
}

size_t rccIConvRecode(rcc_iconv icnv, char *outbuf, size_t outsize, const char *buf, size_t size) {
    char *in_buf, *out_buf, *res, err;
    int in_left, out_left, olen;
    int ub, utf_mode=0;
    int errors=0;
    
    if ((!buf)||(!outbuf)||(!outsize)||(!icnv)||(icnv->icnv == (iconv_t)-1)) return (size_t)-1;
    if (iconv(icnv->icnv, NULL, NULL, NULL, NULL) == -1) return (size_t)-1;

    size = STRNLEN(buf,size);
    
loop_restart:
    errors = 0;
    in_buf = (char*)buf; /*DS*/
    in_left = size;
    out_buf = outbuf;
    out_left = outsize;

loop:
    err=iconv(icnv->icnv, &in_buf, &in_left, &out_buf, &out_left);
    if (err<0) {
        if (errno==E2BIG) {
    	    *(int*)(outbuf+(RCC_MAX_STRING_CHARS-sizeof(int)))=0;
	} else if (errno==EILSEQ) {
	    if (errors++<RCC_MAX_ERRORS) {
		for (ub=utf_mode?rccIConvUTFBytes(*in_buf):1;ub>0;ub--)
		    rccIConvCopySymbol(&in_buf, &in_left, &out_buf, &out_left);
		if (in_left>0) goto loop;
	    } else if (!utf_mode) {
		utf_mode = 1;
		goto loop_restart;
	    } else {
	        return (size_t)-1;
	    }
	} else {
	    return (size_t)-1;
	}
    }
    
    outbuf[outsize - out_left] = 0;

    return outsize - out_left;
}

size_t rccIConv(rcc_context ctx, rcc_iconv icnv, const char *buf, size_t len) {
    if (!ctx) return (size_t)-1;
    return rccIConvRecode(icnv, ctx->tmpbuffer, RCC_MAX_STRING_CHARS, buf, len);
}