NetBSD/sys/netsmb/iconv.c

497 lines
10 KiB
C

/* $NetBSD: iconv.c,v 1.2 2001/11/13 01:04:46 lukem Exp $ */
/*
* Copyright (c) 2000, Boris Popov
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Boris Popov.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* 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.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: iconv.c,v 1.2 2001/11/13 01:04:46 lukem Exp $");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#ifndef NetBSD
#include <sys/kobj.h>
#endif
#include <sys/queue.h>
#include <sys/errno.h>
#include <uvm/uvm_extern.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
#ifndef NetBSD
#include "iconv_drv_if.h"
#endif
#include "iconv.h"
#ifdef SYSCTL_DECL
SYSCTL_DECL(_kern_iconv);
#endif
#ifndef NetBSD
SYSCTL_NODE(_kern, OID_AUTO, iconv, CTLFLAG_RW, NULL, "kernel iconv interface");
MALLOC_DEFINE(M_ICONV, "ICONV data", "ICONV data");
#endif
#ifdef MODULE_VERSION
MODULE_VERSION(libiconv, 1);
#endif
#ifndef NetBSD
struct sysctl_oid *iconv_oid_hook = &sysctl___kern_iconv;
static struct iconv_drv_list iconv_drivers;
#endif
#ifndef NetBSD
static int
iconv_mod_handler(module_t mod, int type, void *data)
{
int error;
printf("%s\n", __FUNCTION__);
switch (type) {
case MOD_LOAD:
TAILQ_INIT(&iconv_drivers);
error = 0;
break;
case MOD_UNLOAD:
error = 0;
break;
default:
error = EINVAL;
}
return error;
}
static moduledata_t iconv_mod = {
"iconv", iconv_mod_handler, NULL
};
DECLARE_MODULE(iconv, iconv_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
static int
iconv_add_driver(struct iconv_drv_class *dcp)
{
kobj_class_compile((struct kobj_class*)dcp);
dcp->refs++;
TAILQ_INSERT_TAIL(&iconv_drivers, dcp, dc_link);
return 0;
}
static int
iconv_rm_driver(struct iconv_drv_class *dcp)
{
TAILQ_REMOVE(&iconv_drivers, dcp, dc_link);
kobj_class_free((struct kobj_class*)dcp);
return 0;
}
int
iconv_drv_initstub(struct iconv_drv_class *dp)
{
return 0;
}
int
iconv_drv_donestub(struct iconv_drv_class *dp)
{
return 0;
}
int
iconv_drvmod_handler(module_t mod, int type, void *data)
{
struct iconv_drv_class *dcp = data;
int error;
switch (type) {
case MOD_LOAD:
error = iconv_add_driver(dcp);
if (error)
break;
error = ICONV_DRV_INIT(dcp);
if (error)
iconv_rm_driver(dcp);
break;
case MOD_UNLOAD:
error = ICONV_DRV_DONE(dcp);
if (!error)
error = iconv_rm_driver(dcp);
break;
default:
error = EINVAL;
}
return error;
}
#endif
int
iconv_open(const char *to, const char *from, struct iconv_drv **dpp)
{
#ifndef NetBSD
struct iconv_drv_class *dcp;
int error;
TAILQ_FOREACH(dcp, &iconv_drivers, dc_link) {
error = ICONV_DRV_OPEN(dcp, to, from, dpp);
if (error == 0)
return 0;
if (error != ENOENT)
return error;
}
return ENOENT;
#else
dpp = NULL;
return 0;
#endif
}
int
iconv_close(struct iconv_drv *dp)
{
#ifndef NetBSD
return ICONV_DRV_CLOSE(dp);
#else
return 0;
#endif
}
int
iconv_conv(struct iconv_drv *dp, const char **inbuf,
size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
{
#ifndef NetBSD
return ICONV_DRV_CONV(dp, inbuf, inbytesleft, outbuf, outbytesleft);
#else
return 1;
#endif
}
char *
iconv_convstr(struct iconv_drv *dp, char *dst, const char *src)
{
char *p = dst;
int inlen, outlen, error;
if (dp == NULL) {
strcpy(dst, src);
return dst;
}
inlen = outlen = strlen(src);
error = iconv_conv(dp, NULL, NULL, &p, &outlen);
if (error)
return NULL;
error = iconv_conv(dp, &src, &inlen, &p, &outlen);
if (error)
return NULL;
*p = 0;
return dst;
}
void *
iconv_convmem(struct iconv_drv *dp, void *dst, const void *src, int size)
{
const char *s = src;
char *d = dst;
int inlen, outlen, error;
if (size == 0)
return dst;
if (dp == NULL) {
memcpy(dst, src, size);
return dst;
}
inlen = outlen = size;
error = iconv_conv(dp, NULL, NULL, &d, &outlen);
if (error)
return NULL;
error = iconv_conv(dp, &s, &inlen, &d, &outlen);
if (error)
return NULL;
return dst;
}
int
iconv_lookupcp(const char **cpp, const char *s)
{
for (; *cpp; cpp++)
if (strcmp(*cpp, s) == 0)
return 0;
return ENOENT;
}
#ifndef NetBSD
/*
* Built-in "XLAT" driver
*/
struct iconv_drv_xlat {
struct iconv_drv * d_static;
u_char * d_table;
};
static TAILQ_HEAD(, iconv_xlat_tbl) iconv_xlat_tables;
int
iconv_xlat_add_table(struct iconv_xlat_tbl *xp)
{
TAILQ_INSERT_TAIL(&iconv_xlat_tables, xp, x_link);
return 0;
}
int
iconv_xlat_rm_table(struct iconv_xlat_tbl *xp)
{
TAILQ_REMOVE(&iconv_xlat_tables, xp, x_link);
return 0;
}
int
iconv_xlatmod_handler(module_t mod, int type, void *data)
{
struct iconv_xlat_tbl *xp = data;
int error;
switch (type) {
case MOD_LOAD:
error = iconv_xlat_add_table(xp);
break;
case MOD_UNLOAD:
error = iconv_xlat_rm_table(xp);
break;
default:
error = EINVAL;
}
return error;
}
static int
iconv_xlat_lookup(const char *to, const char *from, struct iconv_xlat_tbl **xpp)
{
struct iconv_xlat_tbl *xp;
TAILQ_FOREACH(xp, &iconv_xlat_tables, x_link) {
if (iconv_lookupcp(xp->x_from, from) == 0 &&
iconv_lookupcp(xp->x_to, to) == 0) {
*xpp = xp;
return 0;
}
}
return ENOENT;
}
static int
iconv_xlat_open(struct iconv_drv_class *dcp, const char *to,
const char *from, struct iconv_drv **dpp)
{
struct iconv_xlat_tbl *xp;
struct iconv_drv_xlat *dp;
int error;
error = iconv_xlat_lookup(to, from, &xp);
if (error)
return error;
dp = (struct iconv_drv_xlat *)kobj_create((struct kobj_class*)dcp, M_ICONV, M_WAITOK);
if (dp == NULL)
return ENOENT;
dp->d_table = xp->x_table;
*dpp = (struct iconv_drv*)dp;
return 0;
}
static int
iconv_xlat_close(struct iconv_drv *dp)
{
kobj_delete((struct kobj*)dp, M_ICONV);
return 0;
}
static int
iconv_xlat_conv(struct iconv_drv *d2p, const char **inbuf,
size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
{
struct iconv_drv_xlat *dp = (struct iconv_drv_xlat*)d2p;
const char *src;
char *dst;
int n, r;
if (inbuf == NULL || *inbuf == NULL || outbuf == NULL || *outbuf == NULL)
return 0;
r = n = min(*inbytesleft, *outbytesleft);
src = *inbuf;
dst = *outbuf;
while(r--)
*dst++ = dp->d_table[(u_char)*src++];
*inbuf += n;
*outbuf += n;
*inbytesleft += n;
*outbytesleft += n;
return 0;
}
static int
iconv_xlat_init(struct iconv_drv_class *dcp)
{
TAILQ_INIT(&iconv_xlat_tables);
return 0;
}
static int
iconv_xlat_done(struct iconv_drv_class *dcp)
{
/*
* TODO: free memory
*/
return 0;
}
static kobj_method_t iconv_xlat_methods[] = {
KOBJMETHOD(iconv_drv_open, iconv_xlat_open),
KOBJMETHOD(iconv_drv_close, iconv_xlat_close),
KOBJMETHOD(iconv_drv_conv, iconv_xlat_conv),
KOBJMETHOD(iconv_drv_init, iconv_xlat_init),
KOBJMETHOD(iconv_drv_done, iconv_xlat_done),
{0, 0}
};
ICONV_DRIVER(xlat, sizeof(struct iconv_drv_xlat));
/*
* sysctl interface to xlat driver
*/
static int
iconv_xlat_outlist(struct sysctl_req *req, const char **cpp)
{
char comm = ',';
int len, error = 0;
for (; *cpp; cpp++) {
len = strlen(*cpp);
error = SYSCTL_OUT(req, *cpp, len);
if (error)
break;
if (cpp[1]) {
error = SYSCTL_OUT(req, &comm, 1);
if (error)
break;
}
}
return error;
}
static int
#if __FreeBSD_version > 500000
iconv_xlat_sysctl_list(SYSCTL_HANDLER_ARGS)
#else
iconv_xlat_sysctl_list SYSCTL_HANDLER_ARGS
#endif
{
struct iconv_xlat_tbl *xp;
char spc = ' ', smc = ';';
int error;
error = 0;
TAILQ_FOREACH(xp, &iconv_xlat_tables, x_link) {
error = iconv_xlat_outlist(req, xp->x_from);
if (error)
break;
error = SYSCTL_OUT(req, &spc, 1);
if (error)
break;
error = iconv_xlat_outlist(req, xp->x_to);
if (error)
break;
error = SYSCTL_OUT(req, &smc, 1);
if (error)
break;
}
if (error)
return error;
smc = 0;
error = SYSCTL_OUT(req, &smc, 1);
return error;
}
static int
#if __FreeBSD_version > 500000
iconv_xlat_sysctl_add(SYSCTL_HANDLER_ARGS)
#else
iconv_xlat_sysctl_add SYSCTL_HANDLER_ARGS
#endif
{
struct iconv_xlat_add *xap;
struct iconv_xlat_tbl *xp, *xp1;
const char **cpp;
int error, size;
size = sizeof(*xp) + sizeof(*xap) + sizeof(char*) * 4;
xp = malloc(size, M_ICONV, M_WAITOK);
if (xp == NULL)
return ENOMEM;
bzero(xp, size);
xap = (struct iconv_xlat_add *)(xp + 1);
error = SYSCTL_IN(req, xap, sizeof(*xap));
if (error)
goto bad; /* sigh, no exceptions... */
if (xap->xa_version != ICONV_XLAT_ADD_VER) {
error = EINVAL;
goto bad;
}
if (iconv_xlat_lookup(xap->xa_to, xap->xa_from, &xp1) == 0) {
error = EEXIST;
goto bad;
}
cpp = (const char**)(xap + 1);
xp->x_from = cpp;
*cpp++ = xap->xa_from;
*cpp++ = NULL;
xp->x_to = cpp;
*cpp++ = xap->xa_to;
*cpp = NULL;
xp->x_table = xap->xa_table;
error = iconv_xlat_add_table(xp);
if (error)
goto bad;
return 0;
bad:
free(xp, M_ICONV);
return error;
}
SYSCTL_NODE(_kern_iconv, OID_AUTO, xlat, CTLFLAG_RW, NULL, "xlat interface");
SYSCTL_PROC(_kern_iconv_xlat, OID_AUTO, list, CTLFLAG_RD | CTLTYPE_STRING,
NULL, 0, iconv_xlat_sysctl_list, "S,xlat", "registered tables");
SYSCTL_PROC(_kern_iconv_xlat, OID_AUTO, add, CTLFLAG_WR | CTLTYPE_OPAQUE,
NULL, 0, iconv_xlat_sysctl_add, "O,xlataddd", "input to add a table");
#endif