2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 1993, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "lint.h"
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/mman.h>
2N/A#include <stdlib.h>
2N/A#include <stdio.h>
2N/A#include <dlfcn.h>
2N/A#include <fcntl.h>
2N/A#include <unistd.h>
2N/A#include <string.h>
2N/A#include <errno.h>
2N/A#include <sys/param.h>
2N/A#include <alloca.h>
2N/A#include <langinfo.h>
2N/A#include "iconv.h"
2N/A#include "iconvP.h"
2N/A#include "../i18n/_loc_path.h"
2N/A
2N/Astatic iconv_t iconv_open_real(const char *, const char *, int);
2N/Astatic iconv_p iconv_open_all(char *, char *, char *, int, int);
2N/Astatic iconv_p iconv_open_private(const char *, const char *, int, int);
2N/Astatic iconv_p iconv_search_alias(char **, char **, const char *,
2N/A const char *, char *, int, int);
2N/Astatic size_t passthru_icv_iconv(iconv_t, const char **, size_t *, char **,
2N/A size_t *);
2N/Astatic size_t passthru_icv_iconvstr(char *, size_t *, char *, size_t *, int);
2N/Astatic int passthru_icv_iconvctl(iconv_t, int, void *);
2N/Astatic void passthru_icv_close(iconv_t);
2N/Astatic char *process_conv_modifier_and_special_names(const char *, int *);
2N/Astatic void free_names(char *, const char *, char *, const char *);
2N/A
2N/A
2N/A/*
2N/A * These functions are mainly implemented by using a shared object and
2N/A * the dlopen() functions. The actual conversion algorithm for a particular
2N/A * conversion is implemented via a shared object as a loadable conversion
2N/A * module which is linked dynamically at run time.
2N/A *
2N/A * The loadable conversion module resides as either:
2N/A *
2N/A * /usr/lib/iconv/geniconvtbl.so
2N/A *
2N/A * if the conversion is supported through a geniconvtbl code conversion
2N/A * binary table or as a module that directly specifies the conversion at:
2N/A *
2N/A * /usr/lib/iconv/fromcode%tocode.so
2N/A *
2N/A * where fromcode is the source encoding and tocode is the target encoding.
2N/A * The modules have three must-have entries, _icv_open(), _icv_iconv(), and
2N/A * _icv_close(), and three optional entries, _icv_open_attr(), _icv_iconvctl(),
2N/A * and _icv_iconvstr().
2N/A *
2N/A * If there is no code conversion supported and if the fromcode and the tocode
2N/A * are specifying the same codeset, then, the byte-by-byte, pass-through code
2N/A * conversion that is embedded in the libc is used instead.
2N/A *
2N/A * The following are the related PSARC cases:
2N/A *
2N/A * PSARC/1993/153 iconv/iconv_open/iconv_close
2N/A * PSARC/1999/292 Addition of geniconvtbl(1)
2N/A * PSARC/2001/072 GNU gettext support
2N/A * PSARC/2009/561 Pass-through iconv code conversion
2N/A * PSARC/2010/160 Libc iconv enhancement
2N/A *
2N/A * The PSARC/2001/072 includes the /usr/lib/iconv/alias interface.
2N/A */
2N/A
2N/Aiconv_t
2N/Aiconv_open(const char *tocode, const char *fromcode)
2N/A{
2N/A return (iconv_open_real(tocode, fromcode, 0));
2N/A}
2N/A
2N/Astatic iconv_t
2N/Aiconv_open_real(const char *tocode, const char *fromcode, int string_based)
2N/A{
2N/A iconv_t cd;
2N/A char *ipath;
2N/A char *from;
2N/A char *from_canonical;
2N/A char *to;
2N/A char *to_canonical;
2N/A int flag;
2N/A
2N/A flag = 0;
2N/A
2N/A from = process_conv_modifier_and_special_names(fromcode, &flag);
2N/A if (from == NULL) {
2N/A errno = ENOMEM;
2N/A return ((iconv_t)-1);
2N/A }
2N/A
2N/A to = process_conv_modifier_and_special_names(tocode, &flag);
2N/A if (to == NULL) {
2N/A if (from != fromcode)
2N/A free(from);
2N/A errno = ENOMEM;
2N/A return ((iconv_t)-1);
2N/A }
2N/A
2N/A if ((cd = malloc(sizeof (struct _iconv_info))) == NULL) {
2N/A free_names(to, tocode, from, fromcode);
2N/A errno = ENOMEM;
2N/A return ((iconv_t)-1);
2N/A }
2N/A
2N/A /*
2N/A * Memory for ipath is allocated/released in this function.
2N/A */
2N/A ipath = malloc(MAXPATHLEN);
2N/A if (ipath == NULL) {
2N/A free_names(to, tocode, from, fromcode);
2N/A free(cd);
2N/A errno = ENOMEM;
2N/A return ((iconv_t)-1);
2N/A }
2N/A
2N/A cd->_conv = iconv_open_all(to, from, ipath, flag, string_based);
2N/A if (cd->_conv != (iconv_p)-1) {
2N/A /* found a valid module for this conversion */
2N/A free_names(to, tocode, from, fromcode);
2N/A free(ipath);
2N/A return (cd);
2N/A }
2N/A
2N/A /*
2N/A * Now, try using the encoding name aliasing table
2N/A */
2N/A cd->_conv = iconv_search_alias(&to_canonical, &from_canonical,
2N/A to, from, ipath, flag, string_based);
2N/A free(ipath);
2N/A if (cd->_conv == (iconv_p)-1) {
2N/A /*
2N/A * As the last resort, check if the tocode and the fromcode
2N/A * are referring to the same codeset name or not. If so,
2N/A * assign the embedded pass-through code conversion.
2N/A */
2N/A if (strcasecmp(to_canonical, from_canonical) != 0) {
2N/A /*
2N/A * No valid conversion available. Do failure retrun
2N/A * with the errno set by iconv_search_alias().
2N/A */
2N/A free_names(to_canonical, to, from_canonical, from);
2N/A free_names(to, tocode, from, fromcode);
2N/A free(cd);
2N/A return ((iconv_t)-1);
2N/A }
2N/A
2N/A /*
2N/A * For a pass-through byte-by-byte code conversion, allocate
2N/A * an internal conversion descriptor and initialize the data
2N/A * fields appropriately and we are done.
2N/A */
2N/A cd->_conv = malloc(sizeof (struct _iconv_fields));
2N/A if (cd->_conv == NULL) {
2N/A free_names(to_canonical, to, from_canonical, from);
2N/A free_names(to, tocode, from, fromcode);
2N/A free(cd);
2N/A errno = ENOMEM;
2N/A return ((iconv_t)-1);
2N/A }
2N/A
2N/A cd->_conv->_icv_state = (void *)malloc(sizeof (int));
2N/A if (cd->_conv->_icv_state == NULL) {
2N/A free_names(to_canonical, to, from_canonical, from);
2N/A free_names(to, tocode, from, fromcode);
2N/A free(cd->_conv);
2N/A free(cd);
2N/A errno = ENOMEM;
2N/A return ((iconv_t)-1);
2N/A }
2N/A cd->_conv->_icv_handle = NULL;
2N/A cd->_conv->_icv_iconv = passthru_icv_iconv;
2N/A cd->_conv->_icv_iconvctl = passthru_icv_iconvctl;
2N/A cd->_conv->_icv_iconvstr = passthru_icv_iconvstr;
2N/A cd->_conv->_icv_close = passthru_icv_close;
2N/A *((int *)(cd->_conv->_icv_state)) = flag;
2N/A }
2N/A
2N/A /* found a valid module for this conversion */
2N/A free_names(to_canonical, to, from_canonical, from);
2N/A free_names(to, tocode, from, fromcode);
2N/A return (cd);
2N/A}
2N/A
2N/Astatic size_t
2N/Asearch_alias(char **paddr, size_t size, const char *variant)
2N/A{
2N/A char *addr = *paddr;
2N/A char *p, *sp, *q;
2N/A size_t var_len, can_len;
2N/A
2N/A var_len = strlen(variant);
2N/A p = addr;
2N/A q = addr + size;
2N/A while (q > p) {
2N/A if (*p == '#') {
2N/A /*
2N/A * Line beginning with '#' is a comment
2N/A */
2N/A p++;
2N/A while ((q > p) && (*p++ != '\n'))
2N/A ;
2N/A continue;
2N/A }
2N/A /* skip leading spaces */
2N/A while ((q > p) &&
2N/A ((*p == ' ') || (*p == '\t')))
2N/A p++;
2N/A if (q <= p)
2N/A break;
2N/A sp = p;
2N/A while ((q > p) && (*p != ' ') &&
2N/A (*p != '\t') && (*p != '\n'))
2N/A p++;
2N/A if (q <= p) {
2N/A /* invalid entry */
2N/A break;
2N/A }
2N/A if (*p == '\n') {
2N/A /* invalid entry */
2N/A p++;
2N/A continue;
2N/A }
2N/A
2N/A if (((p - sp) != var_len) ||
2N/A ((strncmp(sp, variant, var_len) != 0) &&
2N/A (strncasecmp(sp, variant, var_len) != 0))) {
2N/A /*
2N/A * didn't match
2N/A */
2N/A
2N/A /* skip remaining chars in this line */
2N/A p++;
2N/A while ((q > p) && (*p++ != '\n'))
2N/A ;
2N/A continue;
2N/A }
2N/A
2N/A /* matching entry found */
2N/A
2N/A /* skip spaces */
2N/A while ((q > p) &&
2N/A ((*p == ' ') || (*p == '\t')))
2N/A p++;
2N/A if (q <= p)
2N/A break;
2N/A sp = p;
2N/A while ((q > p) && (*p != ' ') &&
2N/A (*p != '\t') && (*p != '\n'))
2N/A p++;
2N/A can_len = p - sp;
2N/A if (can_len == 0) {
2N/A while ((q > p) && (*p++ != '\n'))
2N/A ;
2N/A continue;
2N/A }
2N/A *paddr = sp;
2N/A return (can_len);
2N/A /* NOTREACHED */
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Astatic iconv_p
2N/Aiconv_open_all(char *to, char *from, char *ipath, int flag, int string_based)
2N/A{
2N/A iconv_p cv;
2N/A int len;
2N/A
2N/A /*
2N/A * First, try using the geniconvtbl conversion, which is
2N/A * performed by /usr/lib/iconv/geniconvtbl.so with
2N/A * the conversion table file:
2N/A * /usr/lib/iconv/geniconvtbl/binarytables/fromcode%tocode.bt
2N/A *
2N/A * If the geniconvtbl conversion cannot be done,
2N/A * try the conversion by the individual shared object.
2N/A */
2N/A
2N/A len = snprintf(ipath, MAXPATHLEN, _GENICONVTBL_PATH, from, to);
2N/A if ((len <= MAXPATHLEN) && (access(ipath, R_OK) == 0)) {
2N/A /*
2N/A * from%to.bt exists in the table dir
2N/A */
2N/A cv = iconv_open_private(_GENICONVTBL_INT_PATH, ipath,
2N/A flag, string_based);
2N/A if (cv != (iconv_p)-1) {
2N/A /* found a valid module for this conversion */
2N/A return (cv);
2N/A }
2N/A }
2N/A
2N/A /* Next, try /usr/lib/iconv/from%to.so */
2N/A len = snprintf(ipath, MAXPATHLEN, _ICONV_PATH, from, to);
2N/A if ((len <= MAXPATHLEN) && (access(ipath, R_OK) == 0)) {
2N/A /*
2N/A * /usr/lib/iconv/from%to.so exists
2N/A * errno will be set by iconv_open_private on error
2N/A */
2N/A return (iconv_open_private(ipath, NULL, flag, string_based));
2N/A }
2N/A /* no valid module for this conversion found */
2N/A errno = EINVAL;
2N/A return ((iconv_p)-1);
2N/A}
2N/A
2N/Astatic iconv_p
2N/Aiconv_search_alias(char **to_canonical, char **from_canonical,
2N/A const char *tocode, const char *fromcode, char *ipath,
2N/A int flag, int string_based)
2N/A{
2N/A char *p;
2N/A size_t tolen, fromlen;
2N/A iconv_p cv;
2N/A int fd;
2N/A struct stat64 statbuf;
2N/A caddr_t addr;
2N/A size_t buflen;
2N/A
2N/A *to_canonical = (char *)tocode;
2N/A *from_canonical = (char *)fromcode;
2N/A
2N/A fd = open(_ENCODING_ALIAS_PATH, O_RDONLY);
2N/A if (fd == -1) {
2N/A /*
2N/A * if no alias file found,
2N/A * errno will be set to EINVAL.
2N/A */
2N/A errno = EINVAL;
2N/A return ((iconv_p)-1);
2N/A }
2N/A if (fstat64(fd, &statbuf) == -1) {
2N/A (void) close(fd);
2N/A /* use errno set by fstat64 */
2N/A return ((iconv_p)-1);
2N/A }
2N/A buflen = (size_t)statbuf.st_size;
2N/A addr = mmap(NULL, buflen, PROT_READ, MAP_SHARED, fd, 0);
2N/A (void) close(fd);
2N/A if (addr == MAP_FAILED) {
2N/A /* use errno set by mmap */
2N/A return ((iconv_p)-1);
2N/A }
2N/A p = (char *)addr;
2N/A tolen = search_alias(&p, buflen, tocode);
2N/A if (tolen) {
2N/A *to_canonical = malloc(tolen + 1);
2N/A if (*to_canonical == NULL) {
2N/A (void) munmap(addr, buflen);
2N/A *to_canonical = (char *)tocode;
2N/A errno = ENOMEM;
2N/A return ((iconv_p)-1);
2N/A }
2N/A (void) memcpy(*to_canonical, p, tolen);
2N/A (*to_canonical)[tolen] = '\0';
2N/A }
2N/A p = (char *)addr;
2N/A fromlen = search_alias(&p, buflen, fromcode);
2N/A if (fromlen) {
2N/A *from_canonical = malloc(fromlen + 1);
2N/A if (*from_canonical == NULL) {
2N/A (void) munmap(addr, buflen);
2N/A *from_canonical = (char *)fromcode;
2N/A errno = ENOMEM;
2N/A return ((iconv_p)-1);
2N/A }
2N/A (void) memcpy(*from_canonical, p, fromlen);
2N/A (*from_canonical)[fromlen] = '\0';
2N/A }
2N/A (void) munmap(addr, buflen);
2N/A if (tolen == 0 && fromlen == 0) {
2N/A errno = EINVAL;
2N/A return ((iconv_p)-1);
2N/A }
2N/A
2N/A cv = iconv_open_all(*to_canonical, *from_canonical, ipath,
2N/A flag, string_based);
2N/A
2N/A /* errno set by iconv_open_all on error */
2N/A return (cv);
2N/A}
2N/A
2N/Astatic iconv_p
2N/Aiconv_open_private(const char *lib, const char *tbl, int flag, int string_based)
2N/A{
2N/A iconv_t (*fptr)(const char *);
2N/A iconv_t (*fptr_attr)(int, void *);
2N/A iconv_p cdpath;
2N/A
2N/A if ((cdpath = malloc(sizeof (struct _iconv_fields))) == NULL) {
2N/A errno = ENOMEM;
2N/A return ((iconv_p)-1);
2N/A }
2N/A
2N/A if ((cdpath->_icv_handle = dlopen(lib, RTLD_LAZY)) == 0)
2N/A goto ICONV_OPEN_ERR_TWO;
2N/A
2N/A /*
2N/A * If this is called from iconvstr(), get the address of
2N/A * _icv_iconvstr and return since that's all we need.
2N/A */
2N/A if (string_based) {
2N/A if ((cdpath->_icv_iconvstr =
2N/A (size_t (*)(char *, size_t *, char *, size_t *, int))
2N/A dlsym(cdpath->_icv_handle, "_icv_iconvstr")) == NULL)
2N/A goto ICONV_OPEN_ERR_ONE;
2N/A
2N/A return (cdpath);
2N/A }
2N/A
2N/A /*
2N/A * Get address of _icv_open or _icv_open_attr depending on whether
2N/A * we have at least a value defined in the flag.
2N/A */
2N/A if (flag == 0) {
2N/A if ((fptr = (iconv_t (*)(const char *))dlsym(
2N/A cdpath->_icv_handle, "_icv_open")) == NULL)
2N/A goto ICONV_OPEN_ERR_ONE;
2N/A } else if ((fptr_attr = (iconv_t (*)(int, void *))dlsym(
2N/A cdpath->_icv_handle, "_icv_open_attr")) == NULL) {
2N/A goto ICONV_OPEN_ERR_ONE;
2N/A }
2N/A
2N/A /*
2N/A * gets address of _icv_iconv in the loadable conversion module
2N/A * and stores it in cdpath->_icv_iconv
2N/A */
2N/A if ((cdpath->_icv_iconv = (size_t(*)(iconv_t, const char **,
2N/A size_t *, char **, size_t *))dlsym(cdpath->_icv_handle,
2N/A "_icv_iconv")) == NULL)
2N/A goto ICONV_OPEN_ERR_ONE;
2N/A
2N/A /*
2N/A * gets address of _icv_close in the loadable conversion module
2N/A * and stores it in cd->_icv_close
2N/A */
2N/A if ((cdpath->_icv_close = (void(*)(iconv_t))dlsym(cdpath->_icv_handle,
2N/A "_icv_close")) == NULL)
2N/A goto ICONV_OPEN_ERR_ONE;
2N/A
2N/A /*
2N/A * Get the address of _icv_iconvctl() from the module.
2N/A * Saving NULL via dlsym() is normal and, in that case, simply,
2N/A * the module doesn't support the iconvctl().
2N/A */
2N/A cdpath->_icv_iconvctl = (int (*)(iconv_t, int, void *))dlsym(
2N/A cdpath->_icv_handle, "_icv_iconvctl");
2N/A
2N/A /*
2N/A * Initialize the state of the _icv_iconv conversion routine by
2N/A * calling _icv_open() or _icv_open_attr().
2N/A *
2N/A * For all regular iconv modules, NULL will be passed for the tbl
2N/A * argument although the iconv_open() of the module won't use that.
2N/A */
2N/A if (flag == 0) {
2N/A cdpath->_icv_state = (void *)(*fptr)(tbl);
2N/A } else {
2N/A cdpath->_icv_state = (void *)(*fptr_attr)(flag, (void *)tbl);
2N/A }
2N/A
2N/A if (cdpath->_icv_state != (struct _icv_state *)-1)
2N/A return (cdpath);
2N/A
2N/AICONV_OPEN_ERR_ONE:
2N/A (void) dlclose(cdpath->_icv_handle);
2N/AICONV_OPEN_ERR_TWO:
2N/A free(cdpath);
2N/A errno = EINVAL;
2N/A
2N/A return ((iconv_p)-1);
2N/A}
2N/A
2N/Aint
2N/Aiconv_close(iconv_t cd)
2N/A{
2N/A if (cd == NULL) {
2N/A errno = EBADF;
2N/A return (-1);
2N/A }
2N/A (*(cd->_conv)->_icv_close)(cd->_conv->_icv_state);
2N/A if (cd->_conv->_icv_handle != NULL)
2N/A (void) dlclose(cd->_conv->_icv_handle);
2N/A free(cd->_conv);
2N/A free(cd);
2N/A return (0);
2N/A}
2N/A
2N/Astatic void
2N/Apassthru_icv_close(iconv_t cd)
2N/A{
2N/A free((void *)cd);
2N/A}
2N/A
2N/A#pragma weak __xpg5_iconv = iconv
2N/Asize_t
2N/Aiconv(iconv_t cd, char **inbuf, size_t *inbytesleft,
2N/A char **outbuf, size_t *outbytesleft)
2N/A{
2N/A /* check if cd is valid */
2N/A if (cd == NULL) {
2N/A errno = EBADF;
2N/A return ((size_t)-1);
2N/A }
2N/A
2N/A /* direct conversion */
2N/A return ((*(cd->_conv)->_icv_iconv)(cd->_conv->_icv_state,
2N/A (const char **)inbuf, inbytesleft, outbuf, outbytesleft));
2N/A}
2N/A
2N/Astatic size_t
2N/A/*LINTED E_FUNC_ARG_UNUSED*/
2N/Apassthru_icv_iconv(iconv_t cd, const char **inbuf, size_t *inbufleft,
2N/A char **outbuf, size_t *outbufleft)
2N/A{
2N/A size_t ibl;
2N/A size_t obl;
2N/A size_t len;
2N/A size_t ret_val;
2N/A
2N/A /* For any state reset request, return success. */
2N/A if (inbuf == NULL || *inbuf == NULL)
2N/A return (0);
2N/A
2N/A /*
2N/A * Initialize internally used variables for a better performance
2N/A * and prepare for a couple of the return values before the actual
2N/A * copying of the bytes.
2N/A */
2N/A ibl = *inbufleft;
2N/A obl = *outbufleft;
2N/A
2N/A if (ibl > obl) {
2N/A len = obl;
2N/A errno = E2BIG;
2N/A ret_val = (size_t)-1;
2N/A } else {
2N/A len = ibl;
2N/A ret_val = 0;
2N/A }
2N/A
2N/A /*
2N/A * Do the copy using memmove(). There are no EILSEQ or EINVAL
2N/A * checkings since this is a simple copying.
2N/A */
2N/A (void) memmove((void *)*outbuf, (const void *)*inbuf, len);
2N/A
2N/A /* Update the return values related to the buffers then do return. */
2N/A *inbuf = *inbuf + len;
2N/A *outbuf = *outbuf + len;
2N/A *inbufleft = ibl - len;
2N/A *outbufleft = obl - len;
2N/A
2N/A return (ret_val);
2N/A}
2N/A
2N/Aint
2N/Aiconvctl(iconv_t cd, int req, void *arg)
2N/A{
2N/A int flag;
2N/A
2N/A if (cd == NULL) {
2N/A errno = EBADF;
2N/A return (-1);
2N/A }
2N/A
2N/A if (req < ICONV_GET_CONVERSION_BEHAVIOR || req > ICONV_TRIVIALP) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A
2N/A if (cd->_conv->_icv_iconvctl == NULL) {
2N/A errno = ENOTSUP;
2N/A return (-1);
2N/A }
2N/A
2N/A if (req == ICONV_SET_CONVERSION_BEHAVIOR) {
2N/A flag = *((int *)arg);
2N/A
2N/A if ((flag & ICONV_CONV_ILLEGAL_DISCARD) != 0)
2N/A flag &= ~(ICONV_CONV_ILLEGAL_REPLACE_HEX);
2N/A if ((flag & ICONV_CONV_NON_IDENTICAL_DISCARD) != 0)
2N/A flag &= ~(ICONV_CONV_NON_IDENTICAL_REPLACE_HEX |
2N/A ICONV_CONV_NON_IDENTICAL_TRANSLITERATE);
2N/A if ((flag & ICONV_CONV_NON_IDENTICAL_REPLACE_HEX) != 0)
2N/A flag &= ~(ICONV_CONV_NON_IDENTICAL_TRANSLITERATE);
2N/A
2N/A *((int *)arg) = flag;
2N/A }
2N/A
2N/A return ((*cd->_conv->_icv_iconvctl)(cd->_conv->_icv_state, req, arg));
2N/A}
2N/A
2N/Astatic int
2N/Apassthru_icv_iconvctl(iconv_t cd, int req, void *arg)
2N/A{
2N/A int a;
2N/A long f;
2N/A
2N/A a = *((int *)arg);
2N/A f = *((int *)cd);
2N/A
2N/A switch (req) {
2N/A case ICONV_GET_CONVERSION_BEHAVIOR:
2N/A a = f;
2N/A break;
2N/A case ICONV_GET_DISCARD_ILSEQ:
2N/A if ((f & ICONV_CONV_ILLEGAL_DISCARD) != 0 &&
2N/A (f & ICONV_CONV_NON_IDENTICAL_DISCARD) != 0)
2N/A a = 1;
2N/A else
2N/A a = 0;
2N/A break;
2N/A case ICONV_GET_TRANSLITERATE:
2N/A if ((f & ICONV_CONV_NON_IDENTICAL_TRANSLITERATE) != 0)
2N/A a = 1;
2N/A else
2N/A a = 0;
2N/A break;
2N/A case ICONV_SET_CONVERSION_BEHAVIOR:
2N/A f = a;
2N/A break;
2N/A case ICONV_SET_DISCARD_ILSEQ:
2N/A if (a == 0)
2N/A f &= ~(ICONV_CONV_ILLEGAL_DISCARD |
2N/A ICONV_CONV_NON_IDENTICAL_DISCARD);
2N/A else
2N/A f |= ICONV_CONV_ILLEGAL_DISCARD |
2N/A ICONV_CONV_NON_IDENTICAL_DISCARD;
2N/A break;
2N/A case ICONV_SET_TRANSLITERATE:
2N/A if (a == 0)
2N/A f &= ~(ICONV_CONV_NON_IDENTICAL_TRANSLITERATE);
2N/A else
2N/A f |= ICONV_CONV_NON_IDENTICAL_TRANSLITERATE;
2N/A break;
2N/A case ICONV_TRIVIALP:
2N/A a = 1;
2N/A break;
2N/A }
2N/A
2N/A *((int *)cd) = f;
2N/A *((int *)arg) = a;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Asize_t
2N/Aiconvstr(const char *tocode, const char *fromcode, char *inarray,
2N/A size_t *inlen, char *outarray, size_t *outlen, int flag)
2N/A{
2N/A iconv_t cd;
2N/A size_t r;
2N/A
2N/A if ((cd = iconv_open_real(tocode, fromcode, 1)) == (iconv_t)-1) {
2N/A errno = EBADF;
2N/A return ((size_t)-1);
2N/A }
2N/A
2N/A r = (*cd->_conv->_icv_iconvstr)(inarray, inlen, outarray, outlen, flag);
2N/A
2N/A (void) dlclose(cd->_conv->_icv_handle);
2N/A free(cd->_conv);
2N/A free(cd);
2N/A
2N/A return (r);
2N/A}
2N/A
2N/Astatic size_t
2N/Apassthru_icv_iconvstr(char *inarray, size_t *inlen, char *outarray,
2N/A size_t *outlen, int flag)
2N/A{
2N/A char *np;
2N/A size_t len;
2N/A size_t ret_val;
2N/A
2N/A if ((flag & ICONV_IGNORE_NULL) == 0 &&
2N/A (np = (char *)memchr((const void *)inarray, 0, *inlen)) != NULL)
2N/A len = np - inarray;
2N/A else
2N/A len = *inlen;
2N/A
2N/A if (len > *outlen) {
2N/A len = *outlen;
2N/A errno = E2BIG;
2N/A ret_val = (size_t)-1;
2N/A } else {
2N/A ret_val = 0;
2N/A }
2N/A
2N/A (void) memmove((void *)outarray, (const void *)inarray, len);
2N/A
2N/A *inlen -= len;
2N/A *outlen -= len;
2N/A
2N/A return (ret_val);
2N/A}
2N/A
2N/A/*
2N/A * The following function maps "", "char", and "wchar_t" into some
2N/A * uniquely identifiable names as specified in the iconv-l10n-guide.txt at
2N/A * the materials directory of PSARC/2010/160.
2N/A *
2N/A * For any other names, if requested, it duplicates the name into
2N/A * a new memory block and returns.
2N/A */
2N/Astatic char *
2N/Aprocess_special_names(char *name, size_t len, int no_malloc)
2N/A{
2N/A char *s;
2N/A
2N/A if (len == 0 || (len == 4 && strncasecmp(name, "char", 4) == 0)) {
2N/A return (strdup(nl_langinfo(CODESET)));
2N/A } else if (len == 7 && strncasecmp(name, "wchar_t", 7) == 0) {
2N/A s = nl_langinfo(CODESET);
2N/A
2N/A if ((name = malloc(strlen(s) + 9)) == NULL)
2N/A return (NULL);
2N/A
2N/A (void) strcpy(name, "wchar_t-");
2N/A return (strcat(name, s));
2N/A }
2N/A
2N/A if (no_malloc)
2N/A return (name);
2N/A
2N/A if ((s = malloc(len + 1)) == NULL)
2N/A return (NULL);
2N/A
2N/A (void) memcpy(s, name, len);
2N/A s[len] = '\0';
2N/A
2N/A return (s);
2N/A}
2N/A
2N/A/*
2N/A * The min and max lengths of all indicators at this point are 6 of "IGNORE"
2N/A * and 27 of "NON_IDENTICAL_TRANSLITERATE", respectively.
2N/A */
2N/A#define ICONV_MIN_INDICATOR_LEN 6
2N/A#define ICONV_MAX_INDICATOR_LEN 27
2N/A
2N/A#define SAME_LEN(len, t) ((len) == (sizeof (t) - 1))
2N/A#define SAME_STR(s, t) (strncasecmp((s), (t), sizeof (t) - 1) == 0)
2N/A#define SAME_LEN_STR(s, t, len) (SAME_LEN((len), (t)) && SAME_STR((s), (t)))
2N/A
2N/A/*
2N/A * The following function clears any prior flag values that are
2N/A * conflicting with the new value asked in "s" and then sets the new
2N/A * one at the flag.
2N/A */
2N/Astatic int
2N/Acheck_flag_values(char *s, size_t len, int flag)
2N/A{
2N/A if (len < ICONV_MIN_INDICATOR_LEN || len > ICONV_MAX_INDICATOR_LEN)
2N/A return (flag);
2N/A
2N/A if (SAME_LEN_STR(s, "ILLEGAL_DISCARD", len)) {
2N/A
2N/A flag = flag & ~(ICONV_CONV_ILLEGAL_REPLACE_HEX) |
2N/A ICONV_CONV_ILLEGAL_DISCARD;
2N/A
2N/A } else if (SAME_LEN(len, "ILLEGAL_REPLACE_HEX")) {
2N/A
2N/A if (SAME_STR(s, "ILLEGAL_REPLACE_HEX"))
2N/A flag = flag & ~(ICONV_CONV_ILLEGAL_DISCARD) |
2N/A ICONV_CONV_ILLEGAL_REPLACE_HEX;
2N/A else if (SAME_STR(s, "ILLEGAL_RESTORE_HEX"))
2N/A flag |= ICONV_CONV_ILLEGAL_RESTORE_HEX;
2N/A
2N/A } else if (SAME_LEN_STR(s, "NON_IDENTICAL_DISCARD", len)) {
2N/A
2N/A flag = flag & ~(ICONV_CONV_NON_IDENTICAL_REPLACE_HEX |
2N/A ICONV_CONV_NON_IDENTICAL_TRANSLITERATE) |
2N/A ICONV_CONV_NON_IDENTICAL_DISCARD;
2N/A
2N/A } else if (SAME_LEN(len, "NON_IDENTICAL_REPLACE_HEX")) {
2N/A
2N/A if (SAME_STR(s, "NON_IDENTICAL_REPLACE_HEX"))
2N/A flag = flag & ~(ICONV_CONV_NON_IDENTICAL_DISCARD |
2N/A ICONV_CONV_NON_IDENTICAL_TRANSLITERATE) |
2N/A ICONV_CONV_NON_IDENTICAL_REPLACE_HEX;
2N/A else if (SAME_STR(s, "NON_IDENTICAL_RESTORE_HEX"))
2N/A flag |= ICONV_CONV_NON_IDENTICAL_RESTORE_HEX;
2N/A
2N/A } else if (SAME_LEN_STR(s, "NON_IDENTICAL_TRANSLITERATE", len) ||
2N/A SAME_LEN_STR(s, "TRANSLIT", len)) {
2N/A
2N/A flag = flag & ~(ICONV_CONV_NON_IDENTICAL_DISCARD |
2N/A ICONV_CONV_NON_IDENTICAL_REPLACE_HEX) |
2N/A ICONV_CONV_NON_IDENTICAL_TRANSLITERATE;
2N/A
2N/A } else if (SAME_LEN_STR(s, "IGNORE", len)) {
2N/A
2N/A flag = flag & ~(ICONV_CONV_ILLEGAL_REPLACE_HEX |
2N/A ICONV_CONV_NON_IDENTICAL_REPLACE_HEX |
2N/A ICONV_CONV_NON_IDENTICAL_TRANSLITERATE) |
2N/A ICONV_CONV_ILLEGAL_DISCARD |
2N/A ICONV_CONV_NON_IDENTICAL_DISCARD;
2N/A
2N/A } else if (SAME_LEN(len, "REPLACE_HEX")) {
2N/A
2N/A if (SAME_STR(s, "REPLACE_HEX"))
2N/A flag = flag & ~(ICONV_CONV_ILLEGAL_DISCARD |
2N/A ICONV_CONV_NON_IDENTICAL_DISCARD |
2N/A ICONV_CONV_NON_IDENTICAL_TRANSLITERATE) |
2N/A ICONV_CONV_ILLEGAL_REPLACE_HEX |
2N/A ICONV_CONV_NON_IDENTICAL_REPLACE_HEX;
2N/A else if (SAME_STR(s, "RESTORE_HEX"))
2N/A flag |= ICONV_CONV_ILLEGAL_RESTORE_HEX |
2N/A ICONV_CONV_NON_IDENTICAL_RESTORE_HEX;
2N/A
2N/A }
2N/A
2N/A return (flag);
2N/A}
2N/A
2N/A/*
2N/A * The following function accepts a tocode/fromcode name, separates iconv
2N/A * code conversion behavior modification indicators if any and the actual
2N/A * codeset name from the name and sets the flag. It also processes special
2N/A * tocode/fromcode names, "", "char", and "wchar_t" by using
2N/A * process_special_names().
2N/A */
2N/Astatic char *
2N/Aprocess_conv_modifier_and_special_names(const char *name, int *flag)
2N/A{
2N/A char *start;
2N/A char *prev_start;
2N/A char *end;
2N/A size_t len;
2N/A
2N/A start = (char *)name;
2N/A while ((start = strchr(start, '/')) != NULL) {
2N/A if (*(start + 1) == '/')
2N/A break;
2N/A start++;
2N/A }
2N/A
2N/A if (start == NULL)
2N/A return (process_special_names((char *)name, strlen(name), 1));
2N/A
2N/A len = start - name;
2N/A
2N/A prev_start = start += 2;
2N/A
2N/A while ((end = strchr(start, '/')) != NULL) {
2N/A if (*(end + 1) != '/') {
2N/A start = end + 1;
2N/A continue;
2N/A }
2N/A
2N/A *flag = check_flag_values(prev_start, end - prev_start, *flag);
2N/A
2N/A prev_start = start = end + 2;
2N/A }
2N/A
2N/A end = start + strlen(start);
2N/A *flag = check_flag_values(prev_start, end - prev_start, *flag);
2N/A
2N/A return (process_special_names((char *)name, len, 0));
2N/A}
2N/A
2N/Astatic void
2N/Afree_names(char *newto, const char *orgto, char *newfrom, const char *orgfrom)
2N/A{
2N/A if (newto != orgto)
2N/A free((void *)newto);
2N/A
2N/A if (newfrom != orgfrom)
2N/A free((void *)newfrom);
2N/A}