1 /*- 2 * Copyright (c) 2003, 2005 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$ 27 */ 28 29 /* 30 * kiconv(3) requires shared linked, and reduce module size 31 * when statically linked. 32 */ 33 34 #ifdef PIC 35 36 #include <sys/types.h> 37 #include <sys/iconv.h> 38 #include <sys/sysctl.h> 39 40 #include <ctype.h> 41 #include <dlfcn.h> 42 #include <err.h> 43 #include <errno.h> 44 #include <locale.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <wctype.h> 49 50 #include "quirks.h" 51 52 typedef void *iconv_t; 53 54 struct xlat16_table { 55 uint32_t * idx[0x200]; 56 void * data; 57 size_t size; 58 }; 59 60 static struct xlat16_table kiconv_xlat16_open(const char *, const char *, int); 61 static int chklocale(int, const char *); 62 63 static int my_iconv_init(void); 64 static iconv_t (*my_iconv_open)(const char *, const char *); 65 static size_t (*my_iconv)(iconv_t, const char **, size_t *, char **, size_t *); 66 static int (*my_iconv_close)(iconv_t); 67 static size_t my_iconv_char(iconv_t, const u_char **, size_t *, u_char **, size_t *); 68 69 int 70 kiconv_add_xlat16_cspair(const char *tocode, const char *fromcode, int flag) 71 { 72 int error; 73 size_t idxsize; 74 struct xlat16_table xt; 75 void *data; 76 char *p; 77 const char unicode[] = ENCODING_UNICODE; 78 79 if ((flag & KICONV_WCTYPE) == 0 && 80 strcmp(unicode, tocode) != 0 && 81 strcmp(unicode, fromcode) != 0 && 82 kiconv_lookupconv(unicode) == 0) { 83 error = kiconv_add_xlat16_cspair(unicode, fromcode, flag); 84 if (error) 85 return (-1); 86 error = kiconv_add_xlat16_cspair(tocode, unicode, flag); 87 return (error); 88 } 89 90 if (kiconv_lookupcs(tocode, fromcode) == 0) 91 return (0); 92 93 if (flag & KICONV_WCTYPE) 94 xt = kiconv_xlat16_open(fromcode, fromcode, flag); 95 else 96 xt = kiconv_xlat16_open(tocode, fromcode, flag); 97 if (xt.size == 0) 98 return (-1); 99 100 idxsize = sizeof(xt.idx); 101 102 if ((idxsize + xt.size) > ICONV_CSMAXDATALEN) { 103 errno = E2BIG; 104 return (-1); 105 } 106 107 if ((data = malloc(idxsize + xt.size)) != NULL) { 108 p = data; 109 memcpy(p, xt.idx, idxsize); 110 p += idxsize; 111 memcpy(p, xt.data, xt.size); 112 error = kiconv_add_xlat16_table(tocode, fromcode, data, 113 (int)(idxsize + xt.size)); 114 return (error); 115 } 116 117 return (-1); 118 } 119 120 int 121 kiconv_add_xlat16_cspairs(const char *foreigncode, const char *localcode) 122 { 123 int error, locale; 124 125 error = kiconv_add_xlat16_cspair(foreigncode, localcode, 126 KICONV_FROM_LOWER | KICONV_FROM_UPPER); 127 if (error) 128 return (error); 129 error = kiconv_add_xlat16_cspair(localcode, foreigncode, 130 KICONV_LOWER | KICONV_UPPER); 131 if (error) 132 return (error); 133 locale = chklocale(LC_CTYPE, localcode); 134 if (locale == 0) { 135 error = kiconv_add_xlat16_cspair(KICONV_WCTYPE_NAME, localcode, 136 KICONV_WCTYPE); 137 if (error) 138 return (error); 139 } 140 141 return (0); 142 } 143 144 static struct xlat16_table 145 kiconv_xlat16_open(const char *tocode, const char *fromcode, int lcase) 146 { 147 u_char src[3], dst[4], *srcp, *dstp, ud, ld; 148 int us, ls, ret; 149 uint16_t c; 150 uint32_t table[0x80]; 151 size_t inbytesleft, outbytesleft, pre_q_size, post_q_size; 152 struct xlat16_table xt; 153 struct quirk_replace_list *pre_q_list, *post_q_list; 154 iconv_t cd; 155 char *p; 156 157 xt.data = NULL; 158 xt.size = 0; 159 160 src[2] = '\0'; 161 dst[3] = '\0'; 162 163 ret = my_iconv_init(); 164 if (ret) 165 return (xt); 166 167 cd = my_iconv_open(search_quirk(tocode, fromcode, &pre_q_list, &pre_q_size), 168 search_quirk(fromcode, tocode, &post_q_list, &post_q_size)); 169 if (cd == (iconv_t) (-1)) 170 return (xt); 171 172 if ((xt.data = malloc(0x200 * 0x80 * sizeof(uint32_t))) == NULL) 173 return (xt); 174 175 p = xt.data; 176 177 for (ls = 0 ; ls < 0x200 ; ls++) { 178 xt.idx[ls] = NULL; 179 for (us = 0 ; us < 0x80 ; us++) { 180 srcp = src; 181 dstp = dst; 182 183 inbytesleft = 2; 184 outbytesleft = 3; 185 bzero(dst, outbytesleft); 186 187 c = ((ls & 0x100 ? us | 0x80 : us) << 8) | (u_char)ls; 188 189 if (lcase & KICONV_WCTYPE) { 190 if ((c & 0xff) == 0) 191 c >>= 8; 192 if (iswupper(c)) { 193 c = towlower(c); 194 if ((c & 0xff00) == 0) 195 c <<= 8; 196 table[us] = c | XLAT16_HAS_LOWER_CASE; 197 } else if (iswlower(c)) { 198 c = towupper(c); 199 if ((c & 0xff00) == 0) 200 c <<= 8; 201 table[us] = c | XLAT16_HAS_UPPER_CASE; 202 } else 203 table[us] = 0; 204 /* 205 * store not NULL 206 */ 207 if (table[us]) 208 xt.idx[ls] = table; 209 210 continue; 211 } 212 213 c = quirk_vendor2unix(c, pre_q_list, pre_q_size); 214 src[0] = (u_char)(c >> 8); 215 src[1] = (u_char)c; 216 217 ret = my_iconv_char(cd, (const u_char **)&srcp, 218 &inbytesleft, &dstp, &outbytesleft); 219 if (ret == -1) { 220 table[us] = 0; 221 continue; 222 } 223 224 ud = (u_char)dst[0]; 225 ld = (u_char)dst[1]; 226 227 switch(outbytesleft) { 228 case 0: 229 #ifdef XLAT16_ACCEPT_3BYTE_CHR 230 table[us] = (ud << 8) | ld; 231 table[us] |= (u_char)dst[2] << 16; 232 table[us] |= XLAT16_IS_3BYTE_CHR; 233 #else 234 table[us] = 0; 235 continue; 236 #endif 237 break; 238 case 1: 239 table[us] = quirk_unix2vendor((ud << 8) | ld, 240 post_q_list, post_q_size); 241 if ((table[us] >> 8) == 0) 242 table[us] |= XLAT16_ACCEPT_NULL_OUT; 243 break; 244 case 2: 245 table[us] = ud; 246 if (lcase & KICONV_LOWER && ud != tolower(ud)) { 247 table[us] |= (u_char)tolower(ud) << 16; 248 table[us] |= XLAT16_HAS_LOWER_CASE; 249 } 250 if (lcase & KICONV_UPPER && ud != toupper(ud)) { 251 table[us] |= (u_char)toupper(ud) << 16; 252 table[us] |= XLAT16_HAS_UPPER_CASE; 253 } 254 break; 255 } 256 257 switch(inbytesleft) { 258 case 0: 259 if ((ls & 0xff) == 0) 260 table[us] |= XLAT16_ACCEPT_NULL_IN; 261 break; 262 case 1: 263 c = ls > 0xff ? us | 0x80 : us; 264 if (lcase & KICONV_FROM_LOWER && c != tolower(c)) { 265 table[us] |= (u_char)tolower(c) << 16; 266 table[us] |= XLAT16_HAS_FROM_LOWER_CASE; 267 } 268 if (lcase & KICONV_FROM_UPPER && c != toupper(c)) { 269 table[us] |= (u_char)toupper(c) << 16; 270 table[us] |= XLAT16_HAS_FROM_UPPER_CASE; 271 } 272 break; 273 } 274 275 if (table[us] == 0) 276 continue; 277 278 /* 279 * store not NULL 280 */ 281 xt.idx[ls] = table; 282 } 283 if (xt.idx[ls]) { 284 memcpy(p, table, sizeof(table)); 285 p += sizeof(table); 286 } 287 } 288 my_iconv_close(cd); 289 290 xt.size = p - (char *)xt.data; 291 xt.data = realloc(xt.data, xt.size); 292 return (xt); 293 } 294 295 static int 296 chklocale(int category, const char *code) 297 { 298 char *p; 299 int error = -1; 300 301 p = strchr(setlocale(category, NULL), '.'); 302 if (p++) { 303 error = strcasecmp(code, p); 304 if (error) { 305 /* XXX - can't avoid calling quirk here... */ 306 error = strcasecmp(code, kiconv_quirkcs(p, 307 KICONV_VENDOR_MICSFT)); 308 } 309 } 310 return (error); 311 } 312 313 static int 314 my_iconv_init(void) 315 { 316 void *iconv_lib; 317 318 iconv_lib = dlopen("libiconv.so", RTLD_LAZY | RTLD_GLOBAL); 319 if (iconv_lib == NULL) { 320 warn("Unable to load iconv library: %s\n", dlerror()); 321 errno = ENOENT; 322 return (-1); 323 } 324 my_iconv_open = dlsym(iconv_lib, "iconv_open"); 325 my_iconv = dlsym(iconv_lib, "iconv"); 326 my_iconv_close = dlsym(iconv_lib, "iconv_close"); 327 328 return (0); 329 } 330 331 static size_t 332 my_iconv_char(iconv_t cd, const u_char **ibuf, size_t * ilen, u_char **obuf, 333 size_t * olen) 334 { 335 const u_char *sp; 336 u_char *dp, ilocal[3], olocal[3]; 337 u_char c1, c2; 338 int ret; 339 size_t ir, or; 340 341 sp = *ibuf; 342 dp = *obuf; 343 ir = *ilen; 344 345 bzero(*obuf, *olen); 346 ret = my_iconv(cd, (const char **)&sp, ilen, (char **)&dp, olen); 347 c1 = (*obuf)[0]; 348 c2 = (*obuf)[1]; 349 350 if (ret == -1) { 351 if (*ilen == ir - 1 && (*ibuf)[1] == '\0' && (c1 || c2)) 352 return (0); 353 else 354 return (-1); 355 } 356 357 /* 358 * We must judge if inbuf is a single byte char or double byte char. 359 * Here, to judge, try first byte(*sp) conversion and compare. 360 */ 361 ir = 1; 362 or = 3; 363 364 bzero(olocal, or); 365 memcpy(ilocal, *ibuf, sizeof(ilocal)); 366 sp = ilocal; 367 dp = olocal; 368 369 if ((my_iconv(cd,(const char **)&sp, &ir, (char **)&dp, &or)) != -1) { 370 if (olocal[0] != c1) 371 return (ret); 372 373 if (olocal[1] == c2 && (*ibuf)[1] == '\0') { 374 /* 375 * inbuf is a single byte char 376 */ 377 *ilen = 1; 378 *olen = or; 379 return (ret); 380 } 381 382 switch(or) { 383 case 0: 384 case 1: 385 if (olocal[1] == c2) { 386 /* 387 * inbuf is a single byte char, 388 * so return false here. 389 */ 390 return (-1); 391 } else { 392 /* 393 * inbuf is a double byte char 394 */ 395 return (ret); 396 } 397 break; 398 case 2: 399 /* 400 * should compare second byte of inbuf 401 */ 402 break; 403 } 404 } else { 405 /* 406 * inbuf clould not be splitted, so inbuf is 407 * a double byte char. 408 */ 409 return (ret); 410 } 411 412 /* 413 * try second byte(*(sp+1)) conversion, and compare 414 */ 415 ir = 1; 416 or = 3; 417 418 bzero(olocal, or); 419 420 sp = ilocal + 1; 421 dp = olocal; 422 423 if ((my_iconv(cd,(const char **)&sp, &ir, (char **)&dp, &or)) != -1) { 424 if (olocal[0] == c2) 425 /* 426 * inbuf is a single byte char 427 */ 428 return (-1); 429 } 430 431 return (ret); 432 } 433 434 #else /* statically linked */ 435 436 #include <sys/types.h> 437 #include <sys/iconv.h> 438 #include <errno.h> 439 440 int 441 kiconv_add_xlat16_cspair(const char *tocode __unused, const char *fromcode __unused, 442 int flag __unused) 443 { 444 445 errno = EINVAL; 446 return (-1); 447 } 448 449 int 450 kiconv_add_xlat16_cspairs(const char *tocode __unused, const char *fromcode __unused) 451 { 452 errno = EINVAL; 453 return (-1); 454 } 455 456 #endif /* PIC */ 457