1 /*- 2 * Copyright (c) 2003 Ryuichiro Imura 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: src/lib/libkiconv/xlat16_iconv.c,v 1.4.8.1 2009/04/15 03:14:26 kensmith Exp $ 27 */ 28 29 /* 30 * kiconv(3) requires shared linked, and reduce module size 31 * when statically linked. 32 */ 33 #include <sys/types.h> 34 #include <sys/iconv.h> 35 #include <sys/sysctl.h> 36 37 #include <ctype.h> 38 #include <dlfcn.h> 39 #include <err.h> 40 #include <errno.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <iconv.h> 45 46 #include "quirks.h" 47 48 struct xlat16_table { 49 uint32_t * idx[0x200]; 50 void * data; 51 size_t size; 52 }; 53 54 static struct xlat16_table kiconv_xlat16_open(const char *, const char *, int); 55 56 static int my_iconv_init(void); 57 static iconv_t (*my_iconv_open)(const char *, const char *); 58 static size_t (*my_iconv)(iconv_t, const char **, size_t *, char **, size_t *); 59 static int (*my_iconv_close)(iconv_t); 60 static size_t my_iconv_char(iconv_t, const u_char **, size_t *, u_char **, size_t *); 61 62 int 63 kiconv_add_xlat16_cspair(const char *tocode, const char *fromcode, int flag) 64 { 65 int error; 66 size_t i, size, idxsize; 67 struct iconv_cspair_info *csi; 68 struct xlat16_table xt; 69 void *data; 70 char *p; 71 if (sysctlbyname("kern.iconv.cslist", NULL, &size, NULL, 0) == -1) 72 return (-1); 73 if (size > 0) { 74 csi = malloc(size); 75 if (csi == NULL) 76 return (-1); 77 if (sysctlbyname("kern.iconv.cslist", csi, &size, NULL, 0) == -1) { 78 free(csi); 79 return (-1); 80 } 81 for (i = 0; i < (size/sizeof(*csi)); i++, csi++){ 82 if (strcmp(csi->cs_to, tocode) == 0 && 83 strcmp(csi->cs_from, fromcode) == 0) 84 return (0); 85 } 86 } 87 88 xt = kiconv_xlat16_open(tocode, fromcode, flag); 89 if (xt.size == 0) 90 return (-1); 91 92 idxsize = sizeof(xt.idx); 93 94 if ((idxsize + xt.size) > ICONV_CSMAXDATALEN) { 95 errno = E2BIG; 96 return (-1); 97 } 98 99 if ((data = malloc(idxsize + xt.size)) != NULL) { 100 p = data; 101 memcpy(p, xt.idx, idxsize); 102 p += idxsize; 103 memcpy(p, xt.data, xt.size); 104 error = kiconv_add_xlat16_table(tocode, fromcode, data, 105 (int)(idxsize + xt.size)); 106 return (error); 107 } 108 return (-1); 109 } 110 111 int 112 kiconv_add_xlat16_cspairs(const char *foreigncode, const char *localcode) 113 { 114 int error; 115 116 error = kiconv_add_xlat16_cspair(foreigncode, localcode, 117 KICONV_FROM_LOWER | KICONV_FROM_UPPER); 118 if (error) 119 return (error); 120 error = kiconv_add_xlat16_cspair(localcode, foreigncode, 121 KICONV_LOWER | KICONV_UPPER); 122 if (error) 123 return (error); 124 return (0); 125 } 126 127 static struct xlat16_table 128 kiconv_xlat16_open(const char *tocode, const char *fromcode, int lcase) 129 { 130 u_char src[3], dst[4], *srcp, *dstp, ud, ld; 131 int us, ls, ret; 132 uint16_t c; 133 uint32_t table[0x80]; 134 size_t inbytesleft, outbytesleft, pre_q_size, post_q_size; 135 struct xlat16_table xt; 136 struct quirk_replace_list *pre_q_list, *post_q_list; 137 iconv_t cd; 138 char *p; 139 140 xt.data = NULL; 141 xt.size = 0; 142 143 src[2] = '\0'; 144 dst[3] = '\0'; 145 ret = my_iconv_init(); 146 if (ret) 147 return (xt); 148 cd = my_iconv_open(search_quirk(tocode, fromcode, &pre_q_list, &pre_q_size), 149 search_quirk(fromcode, tocode, &post_q_list, &post_q_size)); 150 if (cd == (iconv_t) (-1)) 151 return (xt); 152 153 if ((xt.data = malloc(0x200 * 0x80 * sizeof(uint32_t))) == NULL) 154 return (xt); 155 156 p = xt.data; 157 158 for (ls = 0 ; ls < 0x200 ; ls++) { 159 xt.idx[ls] = NULL; 160 for (us = 0 ; us < 0x80 ; us++) { 161 srcp = src; 162 dstp = dst; 163 164 inbytesleft = 2; 165 outbytesleft = 3; 166 bzero(dst, outbytesleft); 167 168 c = ((ls & 0x100 ? us | 0x80 : us) << 8) | (u_char)ls; 169 c = quirk_vendor2unix(c, pre_q_list, pre_q_size); 170 src[0] = (u_char)(c >> 8); 171 src[1] = (u_char)c; 172 173 ret = my_iconv_char(cd, (const u_char **)&srcp, 174 &inbytesleft, &dstp, &outbytesleft); 175 if (ret == -1) { 176 table[us] = 0; 177 continue; 178 } 179 180 ud = (u_char)dst[0]; 181 ld = (u_char)dst[1]; 182 183 switch(outbytesleft) { 184 case 0: 185 #ifdef XLAT16_ACCEPT_3BYTE_CHR 186 table[us] = (ud << 8) | ld; 187 table[us] |= (u_char)dst[2] << 16; 188 table[us] |= XLAT16_IS_3BYTE_CHR; 189 #else 190 table[us] = 0; 191 continue; 192 #endif 193 break; 194 case 1: 195 table[us] = quirk_unix2vendor((ud << 8) | ld, 196 post_q_list, post_q_size); 197 if ((table[us] >> 8) == 0) 198 table[us] |= XLAT16_ACCEPT_NULL_OUT; 199 break; 200 case 2: 201 table[us] = ud; 202 if (lcase & KICONV_LOWER && ud != tolower(ud)) { 203 table[us] |= (u_char)tolower(ud) << 16; 204 table[us] |= XLAT16_HAS_LOWER_CASE; 205 } 206 if (lcase & KICONV_UPPER && ud != toupper(ud)) { 207 table[us] |= (u_char)toupper(ud) << 16; 208 table[us] |= XLAT16_HAS_UPPER_CASE; 209 } 210 break; 211 } 212 213 switch(inbytesleft) { 214 case 0: 215 if ((ls & 0xff) == 0) 216 table[us] |= XLAT16_ACCEPT_NULL_IN; 217 break; 218 case 1: 219 c = ls > 0xff ? us | 0x80 : us; 220 if (lcase & KICONV_FROM_LOWER && c != tolower(c)) { 221 table[us] |= (u_char)tolower(c) << 16; 222 table[us] |= XLAT16_HAS_FROM_LOWER_CASE; 223 } 224 if (lcase & KICONV_FROM_UPPER && c != toupper(c)) { 225 table[us] |= (u_char)toupper(c) << 16; 226 table[us] |= XLAT16_HAS_FROM_UPPER_CASE; 227 } 228 break; 229 } 230 231 if (table[us] == 0) 232 continue; 233 234 /* 235 * store not NULL 236 */ 237 xt.idx[ls] = table; 238 } 239 if (xt.idx[ls]) { 240 memcpy(p, table, sizeof(table)); 241 p += sizeof(table); 242 } 243 } 244 my_iconv_close(cd); 245 246 xt.size = p - (char *)xt.data; 247 xt.data = realloc(xt.data, xt.size); 248 return (xt); 249 } 250 251 static int 252 my_iconv_init(void) 253 { 254 void *iconv_lib; 255 #ifdef __PIC__ 256 iconv_lib = dlopen("libc.so", RTLD_LAZY | RTLD_GLOBAL); 257 if (iconv_lib == NULL) { 258 warn("Unable to load iconv library: %s\n", dlerror()); 259 errno = ENOENT; 260 return (-1); 261 } 262 my_iconv_open = dlsym(iconv_lib, "iconv_open"); 263 my_iconv = dlsym(iconv_lib, "iconv"); 264 my_iconv_close = dlsym(iconv_lib, "iconv_close"); 265 #else 266 my_iconv_open = iconv_open; 267 my_iconv = iconv; 268 my_iconv_close = iconv_close; 269 #endif 270 271 return (0); 272 } 273 274 static size_t 275 my_iconv_char(iconv_t cd, const u_char **ibuf, size_t * ilen, u_char **obuf, 276 size_t * olen) 277 { 278 const u_char *sp; 279 u_char *dp, ilocal[3], olocal[3]; 280 u_char c1, c2; 281 int ret; 282 size_t ir, or; 283 284 sp = *ibuf; 285 dp = *obuf; 286 ir = *ilen; 287 288 bzero(*obuf, *olen); 289 ret = my_iconv(cd, (const char **)&sp, ilen, (char **)&dp, olen); 290 c1 = (*obuf)[0]; 291 c2 = (*obuf)[1]; 292 293 if (ret == -1) { 294 if (*ilen == ir - 1 && (*ibuf)[1] == '\0' && (c1 || c2)) 295 return (0); 296 else 297 return (-1); 298 } 299 300 /* 301 * We must judge if inbuf is a single byte char or double byte char. 302 * Here, to judge, try first byte(*sp) conversion and compare. 303 */ 304 ir = 1; 305 or = 3; 306 307 bzero(olocal, or); 308 memcpy(ilocal, *ibuf, sizeof(ilocal)); 309 sp = ilocal; 310 dp = olocal; 311 312 if ((my_iconv(cd,(const char **)&sp, &ir, (char **)&dp, &or)) != -1) { 313 if (olocal[0] != c1) 314 return (ret); 315 316 if (olocal[1] == c2 && (*ibuf)[1] == '\0') { 317 /* 318 * inbuf is a single byte char 319 */ 320 *ilen = 1; 321 *olen = or; 322 return (ret); 323 } 324 325 switch(or) { 326 case 0: 327 case 1: 328 if (olocal[1] == c2) { 329 /* 330 * inbuf is a single byte char, 331 * so return false here. 332 */ 333 return (-1); 334 } else { 335 /* 336 * inbuf is a double byte char 337 */ 338 return (ret); 339 } 340 break; 341 case 2: 342 /* 343 * should compare second byte of inbuf 344 */ 345 break; 346 } 347 } else { 348 /* 349 * inbuf clould not be splitted, so inbuf is 350 * a double byte char. 351 */ 352 return (ret); 353 } 354 355 /* 356 * try second byte(*(sp+1)) conversion, and compare 357 */ 358 ir = 1; 359 or = 3; 360 361 bzero(olocal, or); 362 363 sp = ilocal + 1; 364 dp = olocal; 365 366 if ((my_iconv(cd,(const char **)&sp, &ir, (char **)&dp, &or)) != -1) { 367 if (olocal[0] == c2) 368 /* 369 * inbuf is a single byte char 370 */ 371 return (-1); 372 } 373 374 return (ret); 375 } 376