freebsd-dev/lib/libkiconv/xlat16_iconv.c
Pedro F. Giffuni 5e53a4f90f lib: further adoption of SPDX licensing ID tags.
Mainly focus on files that use BSD 2-Clause license, however the tool I
was using mis-identified many licenses so this was mostly a manual - error
prone - task.

The Software Package Data Exchange (SPDX) group provides a specification
to make it easier for automated tools to detect and summarize well known
opensource licenses. We are gradually adopting the specification, noting
that the tags are considered only advisory and do not, in any way,
superceed or replace the license texts.
2017-11-26 02:00:33 +00:00

467 lines
10 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2003, 2005 Ryuichiro Imura
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
/*
* kiconv(3) requires shared linked, and reduce module size
* when statically linked.
*/
#ifdef PIC
#include <sys/types.h>
#include <sys/iconv.h>
#include <sys/sysctl.h>
#include <ctype.h>
#include <dlfcn.h>
#include <err.h>
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wctype.h>
#include "quirks.h"
struct xlat16_table {
uint32_t * idx[0x200];
void * data;
size_t size;
};
static struct xlat16_table kiconv_xlat16_open(const char *, const char *, int);
static int chklocale(int, const char *);
#ifdef ICONV_DLOPEN
typedef void *iconv_t;
static int my_iconv_init(void);
static iconv_t (*my_iconv_open)(const char *, const char *);
static size_t (*my_iconv)(iconv_t, char **, size_t *, char **, size_t *);
static int (*my_iconv_close)(iconv_t);
#else
#include <iconv.h>
#define my_iconv_init() 0
#define my_iconv_open iconv_open
#define my_iconv iconv
#define my_iconv_close iconv_close
#endif
static size_t my_iconv_char(iconv_t, u_char **, size_t *, u_char **, size_t *);
int
kiconv_add_xlat16_cspair(const char *tocode, const char *fromcode, int flag)
{
int error;
size_t idxsize;
struct xlat16_table xt;
void *data;
char *p;
const char unicode[] = ENCODING_UNICODE;
if ((flag & KICONV_WCTYPE) == 0 &&
strcmp(unicode, tocode) != 0 &&
strcmp(unicode, fromcode) != 0 &&
kiconv_lookupconv(unicode) == 0) {
error = kiconv_add_xlat16_cspair(unicode, fromcode, flag);
if (error)
return (-1);
error = kiconv_add_xlat16_cspair(tocode, unicode, flag);
return (error);
}
if (kiconv_lookupcs(tocode, fromcode) == 0)
return (0);
if (flag & KICONV_WCTYPE)
xt = kiconv_xlat16_open(fromcode, fromcode, flag);
else
xt = kiconv_xlat16_open(tocode, fromcode, flag);
if (xt.size == 0)
return (-1);
idxsize = sizeof(xt.idx);
if ((idxsize + xt.size) > ICONV_CSMAXDATALEN) {
errno = E2BIG;
return (-1);
}
if ((data = malloc(idxsize + xt.size)) != NULL) {
p = data;
memcpy(p, xt.idx, idxsize);
p += idxsize;
memcpy(p, xt.data, xt.size);
error = kiconv_add_xlat16_table(tocode, fromcode, data,
(int)(idxsize + xt.size));
return (error);
}
return (-1);
}
int
kiconv_add_xlat16_cspairs(const char *foreigncode, const char *localcode)
{
int error, locale;
error = kiconv_add_xlat16_cspair(foreigncode, localcode,
KICONV_FROM_LOWER | KICONV_FROM_UPPER);
if (error)
return (error);
error = kiconv_add_xlat16_cspair(localcode, foreigncode,
KICONV_LOWER | KICONV_UPPER);
if (error)
return (error);
locale = chklocale(LC_CTYPE, localcode);
if (locale == 0) {
error = kiconv_add_xlat16_cspair(KICONV_WCTYPE_NAME, localcode,
KICONV_WCTYPE);
if (error)
return (error);
}
return (0);
}
static struct xlat16_table
kiconv_xlat16_open(const char *tocode, const char *fromcode, int lcase)
{
u_char src[3], dst[4], *srcp, *dstp, ud, ld;
int us, ls, ret;
uint16_t c;
uint32_t table[0x80];
size_t inbytesleft, outbytesleft, pre_q_size, post_q_size;
struct xlat16_table xt;
struct quirk_replace_list *pre_q_list, *post_q_list;
iconv_t cd;
char *p;
xt.data = NULL;
xt.size = 0;
src[2] = '\0';
dst[3] = '\0';
ret = my_iconv_init();
if (ret)
return (xt);
cd = my_iconv_open(search_quirk(tocode, fromcode, &pre_q_list, &pre_q_size),
search_quirk(fromcode, tocode, &post_q_list, &post_q_size));
if (cd == (iconv_t) (-1))
return (xt);
if ((xt.data = malloc(0x200 * 0x80 * sizeof(uint32_t))) == NULL)
return (xt);
p = xt.data;
for (ls = 0 ; ls < 0x200 ; ls++) {
xt.idx[ls] = NULL;
for (us = 0 ; us < 0x80 ; us++) {
srcp = src;
dstp = dst;
inbytesleft = 2;
outbytesleft = 3;
bzero(dst, outbytesleft);
c = ((ls & 0x100 ? us | 0x80 : us) << 8) | (u_char)ls;
if (lcase & KICONV_WCTYPE) {
if ((c & 0xff) == 0)
c >>= 8;
if (iswupper(c)) {
c = towlower(c);
if ((c & 0xff00) == 0)
c <<= 8;
table[us] = c | XLAT16_HAS_LOWER_CASE;
} else if (iswlower(c)) {
c = towupper(c);
if ((c & 0xff00) == 0)
c <<= 8;
table[us] = c | XLAT16_HAS_UPPER_CASE;
} else
table[us] = 0;
/*
* store not NULL
*/
if (table[us])
xt.idx[ls] = table;
continue;
}
c = quirk_vendor2unix(c, pre_q_list, pre_q_size);
src[0] = (u_char)(c >> 8);
src[1] = (u_char)c;
ret = my_iconv_char(cd, &srcp, &inbytesleft,
&dstp, &outbytesleft);
if (ret == -1) {
table[us] = 0;
continue;
}
ud = (u_char)dst[0];
ld = (u_char)dst[1];
switch(outbytesleft) {
case 0:
#ifdef XLAT16_ACCEPT_3BYTE_CHR
table[us] = (ud << 8) | ld;
table[us] |= (u_char)dst[2] << 16;
table[us] |= XLAT16_IS_3BYTE_CHR;
#else
table[us] = 0;
continue;
#endif
break;
case 1:
table[us] = quirk_unix2vendor((ud << 8) | ld,
post_q_list, post_q_size);
if ((table[us] >> 8) == 0)
table[us] |= XLAT16_ACCEPT_NULL_OUT;
break;
case 2:
table[us] = ud;
if (lcase & KICONV_LOWER && ud != tolower(ud)) {
table[us] |= (u_char)tolower(ud) << 16;
table[us] |= XLAT16_HAS_LOWER_CASE;
}
if (lcase & KICONV_UPPER && ud != toupper(ud)) {
table[us] |= (u_char)toupper(ud) << 16;
table[us] |= XLAT16_HAS_UPPER_CASE;
}
break;
}
switch(inbytesleft) {
case 0:
if ((ls & 0xff) == 0)
table[us] |= XLAT16_ACCEPT_NULL_IN;
break;
case 1:
c = ls > 0xff ? us | 0x80 : us;
if (lcase & KICONV_FROM_LOWER && c != tolower(c)) {
table[us] |= (u_char)tolower(c) << 16;
table[us] |= XLAT16_HAS_FROM_LOWER_CASE;
}
if (lcase & KICONV_FROM_UPPER && c != toupper(c)) {
table[us] |= (u_char)toupper(c) << 16;
table[us] |= XLAT16_HAS_FROM_UPPER_CASE;
}
break;
}
if (table[us] == 0)
continue;
/*
* store not NULL
*/
xt.idx[ls] = table;
}
if (xt.idx[ls]) {
memcpy(p, table, sizeof(table));
p += sizeof(table);
}
}
my_iconv_close(cd);
xt.size = p - (char *)xt.data;
xt.data = realloc(xt.data, xt.size);
return (xt);
}
static int
chklocale(int category, const char *code)
{
char *p;
int error = -1;
p = strchr(setlocale(category, NULL), '.');
if (p++) {
error = strcasecmp(code, p);
if (error) {
/* XXX - can't avoid calling quirk here... */
error = strcasecmp(code, kiconv_quirkcs(p,
KICONV_VENDOR_MICSFT));
}
}
return (error);
}
#ifdef ICONV_DLOPEN
static int
my_iconv_init(void)
{
void *iconv_lib;
iconv_lib = dlopen("libiconv.so", RTLD_LAZY | RTLD_GLOBAL);
if (iconv_lib == NULL) {
warn("Unable to load iconv library: %s\n", dlerror());
errno = ENOENT;
return (-1);
}
my_iconv_open = dlsym(iconv_lib, "iconv_open");
my_iconv = dlsym(iconv_lib, "iconv");
my_iconv_close = dlsym(iconv_lib, "iconv_close");
return (0);
}
#endif
static size_t
my_iconv_char(iconv_t cd, u_char **ibuf, size_t * ilen, u_char **obuf,
size_t * olen)
{
u_char *sp, *dp, ilocal[3], olocal[3];
u_char c1, c2;
int ret;
size_t ir, or;
sp = *ibuf;
dp = *obuf;
ir = *ilen;
bzero(*obuf, *olen);
ret = my_iconv(cd, (char **)&sp, ilen, (char **)&dp, olen);
c1 = (*obuf)[0];
c2 = (*obuf)[1];
if (ret == -1) {
if (*ilen == ir - 1 && (*ibuf)[1] == '\0' && (c1 || c2))
return (0);
else
return (-1);
}
/*
* We must judge if inbuf is a single byte char or double byte char.
* Here, to judge, try first byte(*sp) conversion and compare.
*/
ir = 1;
or = 3;
bzero(olocal, or);
memcpy(ilocal, *ibuf, sizeof(ilocal));
sp = ilocal;
dp = olocal;
if ((my_iconv(cd,(char **)&sp, &ir, (char **)&dp, &or)) != -1) {
if (olocal[0] != c1)
return (ret);
if (olocal[1] == c2 && (*ibuf)[1] == '\0') {
/*
* inbuf is a single byte char
*/
*ilen = 1;
*olen = or;
return (ret);
}
switch(or) {
case 0:
case 1:
if (olocal[1] == c2) {
/*
* inbuf is a single byte char,
* so return false here.
*/
return (-1);
} else {
/*
* inbuf is a double byte char
*/
return (ret);
}
break;
case 2:
/*
* should compare second byte of inbuf
*/
break;
}
} else {
/*
* inbuf clould not be splitted, so inbuf is
* a double byte char.
*/
return (ret);
}
/*
* try second byte(*(sp+1)) conversion, and compare
*/
ir = 1;
or = 3;
bzero(olocal, or);
sp = ilocal + 1;
dp = olocal;
if ((my_iconv(cd,(char **)&sp, &ir, (char **)&dp, &or)) != -1) {
if (olocal[0] == c2)
/*
* inbuf is a single byte char
*/
return (-1);
}
return (ret);
}
#else /* statically linked */
#include <sys/types.h>
#include <sys/iconv.h>
#include <errno.h>
int
kiconv_add_xlat16_cspair(const char *tocode __unused, const char *fromcode __unused,
int flag __unused)
{
errno = EINVAL;
return (-1);
}
int
kiconv_add_xlat16_cspairs(const char *tocode __unused, const char *fromcode __unused)
{
errno = EINVAL;
return (-1);
}
#endif /* PIC */