1 /*
2   LibRCC - interface to iconv library
3 
4   Copyright (C) 2005-2008 Suren A. Chilingaryan <csa@dside.dyndns.org>
5 
6   This library is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License version 2.1 or later
8   as published by the Free Software Foundation.
9 
10   This library is distributed in the hope that it will be useful, but WITHOUT
11   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
13   for more details.
14 
15   You should have received a copy of the GNU Lesser General Public License
16   along with this program; if not, write to the Free Software Foundation, Inc.,
17   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <iconv.h>
24 
25 #include <string.h>
26 #ifdef HAVE_STRINGS_H
27 # include <strings.h>
28 #endif /* HAVE_STRINGS_H */
29 
30 #include "internal.h"
31 #include "rcciconv.h"
32 
33 #define RCC_MAX_ERRORS 3
34 
rccIConvCopySymbol(char ** in_buf,size_t * in_left,char ** out_buf,size_t * out_left)35 static void rccIConvCopySymbol(char **in_buf, size_t *in_left, char **out_buf, size_t *out_left) {
36     if ((out_left>0)&&(in_left>0)) {
37 /*	(**out_buf)=(**in_buf);
38 	(*out_buf)++;
39 	(*out_left)--;*/
40 	(*in_buf)++;
41 	(*in_left)--;
42     }
43 }
44 
rccIConvUTFBytes(unsigned char c)45 static int rccIConvUTFBytes(unsigned char c) {
46     int j;
47     if (c<128) return 1;
48 
49     for (j=6;j>=0;j--)
50 	if ((c&(1<<j))==0) break;
51 
52     if ((j==0)||(j==6)) return 1;
53     return 6-j;
54 }
55 
56 
rccIConvOpen(const char * to,const char * from)57 rcc_iconv rccIConvOpen(const char *to, const char *from) {
58     rcc_iconv icnv;
59 
60     if ((!from)||(!to)||(!strcasecmp(from,to))) return NULL;
61 
62     icnv = (rcc_iconv)malloc(sizeof(rcc_iconv_s));
63     if (!icnv) return icnv;
64 
65     icnv->icnv = iconv_open(to, from);
66     return icnv;
67 }
68 
rccIConvClose(rcc_iconv icnv)69 void rccIConvClose(rcc_iconv icnv) {
70     if (icnv) {
71 	if (icnv->icnv != (iconv_t)-1) iconv_close(icnv->icnv);
72 	free(icnv);
73     }
74 }
75 
rccIConvGetError(rcc_iconv icnv)76 int rccIConvGetError(rcc_iconv icnv) {
77     if ((!icnv)||(icnv->icnv == (iconv_t)-1)) return -1;
78     return 0;
79 }
80 
rccIConvRecode(rcc_iconv icnv,char * outbuf,size_t outsize,const char * buf,size_t size)81 size_t rccIConvRecode(rcc_iconv icnv, char *outbuf, size_t outsize, const char *buf, size_t size) {
82     char *in_buf, *out_buf, err;
83     size_t in_left, out_left;
84     int ub, utf_mode=0;
85     int errors=0;
86 
87     if ((!buf)||(!outbuf)||(!outsize)||(!icnv)||(icnv->icnv == (iconv_t)-1)) return (size_t)-1;
88     if (iconv(icnv->icnv, NULL, NULL, NULL, NULL) == -1) return (size_t)-1;
89 
90     if (!size) size = strlen(buf);
91 
92 loop_restart:
93     errors = 0;
94     in_buf = (char*)buf; /*DS*/
95     in_left = size;
96     out_buf = outbuf;
97     out_left = outsize;
98 
99 loop:
100     err=iconv(icnv->icnv, (char**)&in_buf, &in_left, &out_buf, &out_left);
101     if (err<0) {
102         if (errno==E2BIG) {
103     	    *(int*)(outbuf+(RCC_MAX_STRING_CHARS-sizeof(int)))=0;
104 	} else if (errno==EILSEQ) {
105 	    if (errors++<RCC_MAX_ERRORS) {
106 		for (ub=utf_mode?rccIConvUTFBytes(*in_buf):1;ub>0;ub--)
107 		    rccIConvCopySymbol(&in_buf, &in_left, &out_buf, &out_left);
108 		if (in_left>0) goto loop;
109 	    } else if (!utf_mode) {
110 		utf_mode = 1;
111 		goto loop_restart;
112 	    } else {
113 	        return (size_t)-1;
114 	    }
115 	} else {
116 	    return (size_t)-1;
117 	}
118     }
119 
120     outbuf[outsize - out_left] = 0;
121 
122     return outsize - out_left;
123 }
124 
rccIConv(rcc_iconv icnv,const char * buf,size_t len,size_t * rlen)125 char *rccIConv(rcc_iconv icnv, const char *buf, size_t len, size_t *rlen) {
126     char *res;
127     size_t size;
128     char tmpbuffer[RCC_MAX_STRING_CHARS+1];
129 
130     size = rccIConvRecode(icnv, tmpbuffer, RCC_MAX_STRING_CHARS, buf, len);
131     if (size != (size_t)-1) {
132 	res = (char*)malloc((size+1)*sizeof(char));
133 	if (!res) return res;
134 
135 	if (rlen) *rlen = size;
136 	memcpy(res, tmpbuffer, size);
137 	res[size] = 0;
138 
139 	return res;
140     }
141 
142     return NULL;
143 }
144 
rccIConvInternal(rcc_context ctx,rcc_iconv icnv,const char * buf,size_t len)145 size_t rccIConvInternal(rcc_context ctx, rcc_iconv icnv, const char *buf, size_t len) {
146     if (!ctx) return (size_t)-1;
147     return rccIConvRecode(icnv, ctx->tmpbuffer, RCC_MAX_STRING_CHARS, buf, len);
148 }
149 
150