summaryrefslogtreecommitdiffstats
path: root/src/rcciconv.c
blob: 0fb440fa4a8ca3402af2005116e520a9ea37bb15 (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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#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, iconv_t icnv, const char *buf, size_t len) {
    char *in_buf, *out_buf, *res, err;
    int in_left, out_left, olen;
    int ub, utf_mode=0;
    int errors=0;
    
    if ((!buf)||(!ctx)||(icnv == (iconv_t)-1)) return (size_t)-1;
    
    len = STRNLEN(buf,len);
    
    if (iconv(icnv, NULL, NULL, NULL, NULL) == -1) return (size_t)-1;
    
loop_restart:
    errors = 0;
    in_buf = (char*)buf; /*DS*/
    in_left = len;
    out_buf = ctx->tmpbuffer;
    out_left = RCC_MAX_STRING_CHARS;

loop:
    err=iconv(icnv, &in_buf, &in_left, &out_buf, &out_left);
    if (err<0) {
        if (errno==E2BIG) {
    	    *(int*)(ctx->tmpbuffer+(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;
	}
    }
    
    ctx->tmpbuffer[RCC_MAX_STRING_CHARS - out_left] = 0;

    return RCC_MAX_STRING_CHARS - out_left;
}