4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 #include <sys/types.h>
38 #include <sys/param.h>
42 #include "../i18n/_loc_path.h"
44 static iconv_p
iconv_open_all(const char *, const char *, char *);
45 static iconv_p
iconv_open_private(const char *, const char *);
46 static iconv_p
iconv_search_alias(const char *, const char *, char *);
47 static size_t passthru_icv_iconv(iconv_t
, const char **, size_t *, char **,
49 static void passthru_icv_close(iconv_t
);
51 #define PASSTHRU_MAGIC_NUMBER (0x53756e)
55 * These functions are mainly implemented by using a shared object and
56 * the dlopen() functions. The actual conversion algorithm for a particular
57 * conversion is implemented via a shared object as a loadable conversion
58 * module which is linked dynamically at run time.
60 * The loadable conversion module resides as either:
62 * /usr/lib/iconv/geniconvtbl.so
64 * if the conversion is supported through a geniconvtbl code conversion
65 * binary table or as a module that directly specifies the conversion at:
67 * /usr/lib/iconv/fromcode%tocode.so
69 * where fromcode is the source encoding and tocode is the target encoding.
70 * The modules have 3 entries: _icv_open(), _icv_iconv(), and _icv_close().
72 * If there is no code conversion supported and if the fromcode and the tocode
73 * are specifying the same codeset, then, the byte-by-byte, pass-through code
74 * conversion that is embedded in the libc is used instead.
76 * The following are the related PSARC cases:
78 * PSARC/1993/153 iconv/iconv_open/iconv_close
79 * PSARC/1999/292 Addition of geniconvtbl(1)
80 * PSARC/2001/072 GNU gettext support
81 * PSARC/2009/561 Pass-through iconv code conversion
83 * The PSARC/2001/072 includes the /usr/lib/iconv/alias interface.
87 iconv_open(const char *tocode
, const char *fromcode
)
92 if ((cd
= malloc(sizeof (struct _iconv_info
))) == NULL
)
96 * Memory for ipath is allocated/released in this function.
98 ipath
= malloc(MAXPATHLEN
);
101 return ((iconv_t
)-1);
104 cd
->_conv
= iconv_open_all(tocode
, fromcode
, ipath
);
105 if (cd
->_conv
!= (iconv_p
)-1) {
106 /* found a valid module for this conversion */
112 * Now, try using the encoding name aliasing table
114 cd
->_conv
= iconv_search_alias(tocode
, fromcode
, ipath
);
116 if (cd
->_conv
== (iconv_p
)-1) {
118 * As the last resort, check if the tocode and the fromcode
119 * are referring to the same codeset name or not. If so,
120 * assign the embedded pass-through code conversion.
122 if (strcasecmp(tocode
, fromcode
) != 0) {
124 * No valid conversion available. Do failure retrun
125 * with the errno set by iconv_search_alias().
128 return ((iconv_t
)-1);
132 * For a pass-through byte-by-byte code conversion, allocate
133 * an internal conversion descriptor and initialize the data
134 * fields appropriately and we are done.
136 cd
->_conv
= malloc(sizeof (struct _iconv_fields
));
137 if (cd
->_conv
== NULL
) {
139 return ((iconv_t
)-1);
142 cd
->_conv
->_icv_handle
= NULL
;
143 cd
->_conv
->_icv_iconv
= passthru_icv_iconv
;
144 cd
->_conv
->_icv_close
= passthru_icv_close
;
145 cd
->_conv
->_icv_state
= (void *)PASSTHRU_MAGIC_NUMBER
;
148 /* found a valid module for this conversion */
153 search_alias(char **paddr
, size_t size
, const char *variant
)
157 size_t var_len
, can_len
;
159 var_len
= strlen(variant
);
165 * Line beginning with '#' is a comment
168 while ((q
> p
) && (*p
++ != '\n'))
172 /* skip leading spaces */
174 ((*p
== ' ') || (*p
== '\t')))
179 while ((q
> p
) && (*p
!= ' ') &&
180 (*p
!= '\t') && (*p
!= '\n'))
192 if (((p
- sp
) != var_len
) ||
193 ((strncmp(sp
, variant
, var_len
) != 0) &&
194 (strncasecmp(sp
, variant
, var_len
) != 0))) {
199 /* skip remaining chars in this line */
201 while ((q
> p
) && (*p
++ != '\n'))
206 /* matching entry found */
210 ((*p
== ' ') || (*p
== '\t')))
215 while ((q
> p
) && (*p
!= ' ') &&
216 (*p
!= '\t') && (*p
!= '\n'))
220 while ((q
> p
) && (*p
++ != '\n'))
232 iconv_open_all(const char *to
, const char *from
, char *ipath
)
238 * First, try using the geniconvtbl conversion, which is
239 * performed by /usr/lib/iconv/geniconvtbl.so with
240 * the conversion table file:
241 * /usr/lib/iconv/geniconvtbl/binarytables/fromcode%tocode.bt
243 * If the geniconvtbl conversion cannot be done,
244 * try the conversion by the individual shared object.
247 len
= snprintf(ipath
, MAXPATHLEN
, _GENICONVTBL_PATH
, from
, to
);
248 if ((len
<= MAXPATHLEN
) && (access(ipath
, R_OK
) == 0)) {
250 * from%to.bt exists in the table dir
252 cv
= iconv_open_private(_GENICONVTBL_INT_PATH
, ipath
);
253 if (cv
!= (iconv_p
)-1) {
254 /* found a valid module for this conversion */
259 /* Next, try /usr/lib/iconv/from%to.so */
260 len
= snprintf(ipath
, MAXPATHLEN
, _ICONV_PATH
, from
, to
);
261 if ((len
<= MAXPATHLEN
) && (access(ipath
, R_OK
) == 0)) {
263 * /usr/lib/iconv/from%to.so exists
264 * errno will be set by iconv_open_private on error
266 return (iconv_open_private(ipath
, NULL
));
268 /* no valid module for this conversion found */
270 return ((iconv_p
)-1);
274 iconv_search_alias(const char *tocode
, const char *fromcode
, char *ipath
)
277 char *to_canonical
, *from_canonical
;
278 size_t tolen
, fromlen
;
281 struct stat64 statbuf
;
285 fd
= open(_ENCODING_ALIAS_PATH
, O_RDONLY
);
288 * if no alias file found,
289 * errno will be set to EINVAL.
292 return ((iconv_p
)-1);
294 if (fstat64(fd
, &statbuf
) == -1) {
296 /* use errno set by fstat64 */
297 return ((iconv_p
)-1);
299 buflen
= (size_t)statbuf
.st_size
;
300 addr
= mmap(NULL
, buflen
, PROT_READ
, MAP_SHARED
, fd
, 0);
302 if (addr
== MAP_FAILED
) {
303 /* use errno set by mmap */
304 return ((iconv_p
)-1);
307 tolen
= search_alias(&p
, buflen
, tocode
);
309 to_canonical
= alloca(tolen
+ 1);
310 (void) memcpy(to_canonical
, p
, tolen
);
311 to_canonical
[tolen
] = '\0';
313 to_canonical
= (char *)tocode
;
316 fromlen
= search_alias(&p
, buflen
, fromcode
);
318 from_canonical
= alloca(fromlen
+ 1);
319 (void) memcpy(from_canonical
, p
, fromlen
);
320 from_canonical
[fromlen
] = '\0';
322 from_canonical
= (char *)fromcode
;
324 (void) munmap(addr
, buflen
);
325 if (tolen
== 0 && fromlen
== 0) {
327 return ((iconv_p
)-1);
330 cv
= iconv_open_all(to_canonical
, from_canonical
, ipath
);
332 /* errno set by iconv_open_all on error */
337 iconv_open_private(const char *lib
, const char *tbl
)
339 iconv_t (*fptr
)(const char *);
342 if ((cdpath
= malloc(sizeof (struct _iconv_fields
))) == NULL
)
343 return ((iconv_p
)-1);
345 if ((cdpath
->_icv_handle
= dlopen(lib
, RTLD_LAZY
)) == 0) {
347 /* dlopen does not define error no */
349 return ((iconv_p
)-1);
352 /* gets address of _icv_open */
353 if ((fptr
= (iconv_t(*)(const char *))dlsym(cdpath
->_icv_handle
,
354 "_icv_open")) == NULL
) {
355 (void) dlclose(cdpath
->_icv_handle
);
357 /* dlsym does not define errno */
359 return ((iconv_p
)-1);
363 * gets address of _icv_iconv in the loadable conversion module
364 * and stores it in cdpath->_icv_iconv
367 if ((cdpath
->_icv_iconv
= (size_t(*)(iconv_t
, const char **,
368 size_t *, char **, size_t *))dlsym(cdpath
->_icv_handle
,
369 "_icv_iconv")) == NULL
) {
370 (void) dlclose(cdpath
->_icv_handle
);
372 /* dlsym does not define errno */
374 return ((iconv_p
)-1);
378 * gets address of _icv_close in the loadable conversion module
379 * and stores it in cd->_icv_close
381 if ((cdpath
->_icv_close
= (void(*)(iconv_t
))dlsym(cdpath
->_icv_handle
,
382 "_icv_close")) == NULL
) {
383 (void) dlclose(cdpath
->_icv_handle
);
385 /* dlsym does not define errno */
387 return ((iconv_p
)-1);
391 * initialize the state of the actual _icv_iconv conversion routine
392 * For the normal iconv module, NULL will be passed as an argument
393 * although the iconv_open() of the module won't use that.
395 cdpath
->_icv_state
= (void *)(*fptr
)(tbl
);
397 if (cdpath
->_icv_state
== (struct _icv_state
*)-1) {
398 (void) dlclose(cdpath
->_icv_handle
);
400 /* this module does not satisfy this conversion */
402 return ((iconv_p
)-1);
409 iconv_close(iconv_t cd
)
415 (*(cd
->_conv
)->_icv_close
)(cd
->_conv
->_icv_state
);
416 if (cd
->_conv
->_icv_handle
!= NULL
)
417 (void) dlclose(cd
->_conv
->_icv_handle
);
424 * To have minimal performance impact to the existing run-time behavior,
425 * we supply a dummy passthru_icv_close() that will just return.
428 /*LINTED E_FUNC_ARG_UNUSED*/
429 passthru_icv_close(iconv_t cd
)
434 iconv(iconv_t cd
, const char **inbuf
, size_t *inbytesleft
,
435 char **outbuf
, size_t *outbytesleft
)
437 /* check if cd is valid */
438 if (cd
== NULL
|| cd
== (iconv_t
)-1) {
443 /* direct conversion */
444 return ((*(cd
->_conv
)->_icv_iconv
)(cd
->_conv
->_icv_state
,
445 inbuf
, inbytesleft
, outbuf
, outbytesleft
));
449 passthru_icv_iconv(iconv_t cd
, const char **inbuf
, size_t *inbufleft
,
450 char **outbuf
, size_t *outbufleft
)
457 /* Check if the conversion descriptor is a valid one. */
458 if (cd
!= (iconv_t
)PASSTHRU_MAGIC_NUMBER
) {
463 /* For any state reset request, return success. */
464 if (inbuf
== NULL
|| *inbuf
== NULL
)
468 * Initialize internally used variables for a better performance
469 * and prepare for a couple of the return values before the actual
470 * copying of the bytes.
478 ret_val
= (size_t)-1;
485 * Do the copy using memmove(). There are no EILSEQ or EINVAL
486 * checkings since this is a simple copying.
488 (void) memmove((void *)*outbuf
, (const void *)*inbuf
, len
);
490 /* Update the return values related to the buffers then do return. */
491 *inbuf
= *inbuf
+ len
;
492 *outbuf
= *outbuf
+ len
;
493 *inbufleft
= ibl
- len
;
494 *outbufleft
= obl
- len
;