iconv.c revision 109623
167754Smsmith/* 267754Smsmith * Copyright (c) 2000-2001, Boris Popov 367754Smsmith * All rights reserved. 477424Smsmith * 591116Smsmith * Redistribution and use in source and binary forms, with or without 667754Smsmith * modification, are permitted provided that the following conditions 767754Smsmith * are met: 867754Smsmith * 1. Redistributions of source code must retain the above copyright 967754Smsmith * notice, this list of conditions and the following disclaimer. 1067754Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1167754Smsmith * notice, this list of conditions and the following disclaimer in the 1267754Smsmith * documentation and/or other materials provided with the distribution. 1391116Smsmith * 3. All advertising materials mentioning features or use of this software 1470243Smsmith * must display the following acknowledgement: 1567754Smsmith * This product includes software developed by Boris Popov. 1667754Smsmith * 4. Neither the name of the author nor the names of any co-contributors 1767754Smsmith * may be used to endorse or promote products derived from this software 1867754Smsmith * without specific prior written permission. 1967754Smsmith * 2067754Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2167754Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2267754Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2367754Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2467754Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2567754Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2667754Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2767754Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2867754Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2967754Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3067754Smsmith * SUCH DAMAGE. 3167754Smsmith * 3267754Smsmith * $FreeBSD: head/sys/libkern/iconv.c 109623 2003-01-21 08:56:16Z alfred $ 3367754Smsmith */ 3467754Smsmith#include <sys/param.h> 3567754Smsmith#include <sys/systm.h> 3667754Smsmith#include <sys/kernel.h> 3767754Smsmith#include <sys/iconv.h> 3867754Smsmith#include <sys/malloc.h> 3967754Smsmith 4067754Smsmith#include "iconv_converter_if.h" 4167754Smsmith 4267754SmsmithSYSCTL_DECL(_kern_iconv); 4367754Smsmith 4467754SmsmithSYSCTL_NODE(_kern, OID_AUTO, iconv, CTLFLAG_RW, NULL, "kernel iconv interface"); 4567754Smsmith 4667754SmsmithMALLOC_DEFINE(M_ICONV, "ICONV", "ICONV structures"); 4767754SmsmithMALLOC_DEFINE(M_ICONVDATA, "ICONV data", "ICONV data"); 4867754Smsmith 4967754SmsmithMODULE_VERSION(libiconv, 1); 5067754Smsmith 5167754Smsmith#ifdef notnow 5267754Smsmith/* 5367754Smsmith * iconv converter instance 5467754Smsmith */ 5567754Smsmithstruct iconv_converter { 5667754Smsmith KOBJ_FIELDS; 5767754Smsmith void * c_data; 5867754Smsmith}; 5967754Smsmith#endif 6067754Smsmith 6167754Smsmithstruct sysctl_oid *iconv_oid_hook = &sysctl___kern_iconv; 6267754Smsmith 6367754Smsmith/* 6467754Smsmith * List of loaded converters 6567754Smsmith */ 6667754Smsmithstatic TAILQ_HEAD(iconv_converter_list, iconv_converter_class) 6767754Smsmith iconv_converters = TAILQ_HEAD_INITIALIZER(iconv_converters); 6867754Smsmith 6967754Smsmith/* 7067754Smsmith * List of supported/loaded charsets pairs 7167754Smsmith */ 7267754Smsmithstatic TAILQ_HEAD(, iconv_cspair) 7367754Smsmith iconv_cslist = TAILQ_HEAD_INITIALIZER(iconv_cslist); 7467754Smsmithstatic int iconv_csid = 1; 7567754Smsmith 7667754Smsmithstatic char iconv_unicode_string[] = "unicode"; /* save eight bytes when possible */ 7767754Smsmith 7867754Smsmithstatic void iconv_unregister_cspair(struct iconv_cspair *csp); 7967754Smsmith 8067754Smsmithstatic int 8167754Smsmithiconv_mod_unload(void) 8267754Smsmith{ 8367754Smsmith struct iconv_cspair *csp; 8467754Smsmith 8567754Smsmith while ((csp = TAILQ_FIRST(&iconv_cslist)) != NULL) { 8667754Smsmith if (csp->cp_refcount) 8767754Smsmith return EBUSY; 8867754Smsmith iconv_unregister_cspair(csp); 8967754Smsmith } 9067754Smsmith return 0; 9167754Smsmith} 9267754Smsmith 9367754Smsmithstatic int 9467754Smsmithiconv_mod_handler(module_t mod, int type, void *data) 9567754Smsmith{ 9667754Smsmith int error; 9767754Smsmith 9867754Smsmith switch (type) { 9967754Smsmith case MOD_LOAD: 10067754Smsmith error = 0; 10167754Smsmith break; 10267754Smsmith case MOD_UNLOAD: 10367754Smsmith error = iconv_mod_unload(); 10467754Smsmith break; 10567754Smsmith default: 10667754Smsmith error = EINVAL; 10767754Smsmith } 10867754Smsmith return error; 10967754Smsmith} 11067754Smsmith 11167754Smsmithstatic moduledata_t iconv_mod = { 11267754Smsmith "iconv", iconv_mod_handler, NULL 11367754Smsmith}; 11467754Smsmith 11567754SmsmithDECLARE_MODULE(iconv, iconv_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); 11667754Smsmith 11767754Smsmithstatic int 11877424Smsmithiconv_register_converter(struct iconv_converter_class *dcp) 11967754Smsmith{ 12083174Smsmith kobj_class_compile((struct kobj_class*)dcp); 12183174Smsmith dcp->refs++; 12283174Smsmith TAILQ_INSERT_TAIL(&iconv_converters, dcp, cc_link); 12383174Smsmith return 0; 12483174Smsmith} 12583174Smsmith 12683174Smsmithstatic int 12783174Smsmithiconv_unregister_converter(struct iconv_converter_class *dcp) 12883174Smsmith{ 12983174Smsmith if (dcp->refs > 1) { 13083174Smsmith ICDEBUG("converter have %d referenses left\n", dcp->refs); 13183174Smsmith return EBUSY; 13283174Smsmith } 13383174Smsmith TAILQ_REMOVE(&iconv_converters, dcp, cc_link); 13483174Smsmith kobj_class_free((struct kobj_class*)dcp); 13583174Smsmith return 0; 13667754Smsmith} 13767754Smsmith 13867754Smsmithstatic int 13967754Smsmithiconv_lookupconv(const char *name, struct iconv_converter_class **dcpp) 14067754Smsmith{ 14167754Smsmith struct iconv_converter_class *dcp; 14283174Smsmith 14367754Smsmith TAILQ_FOREACH(dcp, &iconv_converters, cc_link) { 14477424Smsmith if (name == NULL) 14591116Smsmith continue; 14667754Smsmith if (strcmp(name, ICONV_CONVERTER_NAME(dcp)) == 0) { 14767754Smsmith if (dcpp) 14867754Smsmith *dcpp = dcp; 14967754Smsmith return 0; 15077424Smsmith } 15167754Smsmith } 15267754Smsmith return ENOENT; 15367754Smsmith} 15491116Smsmith 15591116Smsmithstatic int 15667754Smsmithiconv_lookupcs(const char *to, const char *from, struct iconv_cspair **cspp) 15767754Smsmith{ 15867754Smsmith struct iconv_cspair *csp; 15977424Smsmith 16077424Smsmith TAILQ_FOREACH(csp, &iconv_cslist, cp_link) { 16167754Smsmith if (strcmp(csp->cp_to, to) == 0 && 16277424Smsmith strcmp(csp->cp_from, from) == 0) { 16367754Smsmith if (cspp) 16491116Smsmith *cspp = csp; 16567754Smsmith return 0; 16667754Smsmith } 16777424Smsmith } 16891116Smsmith return ENOENT; 16991116Smsmith} 17091116Smsmith 17191116Smsmithstatic int 17291116Smsmithiconv_register_cspair(const char *to, const char *from, 17377424Smsmith struct iconv_converter_class *dcp, void *data, 17467754Smsmith struct iconv_cspair **cspp) 17567754Smsmith{ 17667754Smsmith struct iconv_cspair *csp; 17767754Smsmith char *cp; 17867754Smsmith int csize, ucsto, ucsfrom; 17977424Smsmith 18067754Smsmith if (iconv_lookupcs(to, from, NULL) == 0) 18167754Smsmith return EEXIST; 18267754Smsmith csize = sizeof(*csp); 18367754Smsmith ucsto = strcmp(to, iconv_unicode_string) == 0; 18467754Smsmith if (!ucsto) 18567754Smsmith csize += strlen(to) + 1; 18667754Smsmith ucsfrom = strcmp(from, iconv_unicode_string) == 0; 18767754Smsmith if (!ucsfrom) 18867754Smsmith csize += strlen(from) + 1; 18967754Smsmith csp = malloc(csize, M_ICONV, 0); 19067754Smsmith bzero(csp, csize); 19167754Smsmith csp->cp_id = iconv_csid++; 19267754Smsmith csp->cp_dcp = dcp; 19367754Smsmith cp = (char*)(csp + 1); 19467754Smsmith if (!ucsto) { 19567754Smsmith strcpy(cp, to); 19667754Smsmith csp->cp_to = cp; 19767754Smsmith cp += strlen(cp) + 1; 19877424Smsmith } else 19967754Smsmith csp->cp_to = iconv_unicode_string; 20091116Smsmith if (!ucsfrom) { 20167754Smsmith strcpy(cp, from); 20267754Smsmith csp->cp_from = cp; 20391116Smsmith } else 20467754Smsmith csp->cp_from = iconv_unicode_string; 20591116Smsmith csp->cp_data = data; 20691116Smsmith 20791116Smsmith TAILQ_INSERT_TAIL(&iconv_cslist, csp, cp_link); 20867754Smsmith *cspp = csp; 20967754Smsmith return 0; 21067754Smsmith} 21167754Smsmith 21267754Smsmithstatic void 21367754Smsmithiconv_unregister_cspair(struct iconv_cspair *csp) 21477424Smsmith{ 21567754Smsmith TAILQ_REMOVE(&iconv_cslist, csp, cp_link); 21667754Smsmith if (csp->cp_data) 21767754Smsmith free(csp->cp_data, M_ICONVDATA); 21867754Smsmith free(csp, M_ICONV); 21967754Smsmith} 22067754Smsmith 22167754Smsmith/* 22267754Smsmith * Lookup and create an instance of converter. 22377424Smsmith * Currently this layer didn't have associated 'instance' structure 22467754Smsmith * to avoid unnesessary memory allocation. 22567754Smsmith */ 22667754Smsmithint 22791116Smsmithiconv_open(const char *to, const char *from, void **handle) 22883174Smsmith{ 22983174Smsmith struct iconv_cspair *csp, *cspfrom, *cspto; 23067754Smsmith struct iconv_converter_class *dcp; 23167754Smsmith const char *cnvname; 23267754Smsmith int error; 23367754Smsmith 23467754Smsmith /* 23567754Smsmith * First, lookup fully qualified cspairs 23667754Smsmith */ 23767754Smsmith error = iconv_lookupcs(to, from, &csp); 23867754Smsmith if (error == 0) 23967754Smsmith return ICONV_CONVERTER_OPEN(csp->cp_dcp, csp, NULL, handle); 24067754Smsmith 24167754Smsmith /* 24277424Smsmith * Well, nothing found. Now try to construct a composite conversion 24367754Smsmith * ToDo: add a 'capability' field to converter 24469450Smsmith */ 24569450Smsmith TAILQ_FOREACH(dcp, &iconv_converters, cc_link) { 24669450Smsmith cnvname = ICONV_CONVERTER_NAME(dcp); 24767754Smsmith if (cnvname == NULL) 24869450Smsmith continue; 24967754Smsmith error = iconv_lookupcs(cnvname, from, &cspfrom); 25069450Smsmith if (error) 25169450Smsmith continue; 25267754Smsmith error = iconv_lookupcs(to, cnvname, &cspto); 25367754Smsmith if (error) 25467754Smsmith continue; 25569450Smsmith /* 25677424Smsmith * Fine, we're found a pair which can be combined together 25769450Smsmith */ 25869450Smsmith return ICONV_CONVERTER_OPEN(dcp, cspto, cspfrom, handle); 25967754Smsmith } 26067754Smsmith return ENOENT; 26191116Smsmith} 26283174Smsmith 26383174Smsmithint 26469450Smsmithiconv_close(void *handle) 26569450Smsmith{ 26669450Smsmith return ICONV_CONVERTER_CLOSE(handle); 26769450Smsmith} 26869450Smsmith 26971867Smsmithint 27069450Smsmithiconv_conv(void *handle, const char **inbuf, 27169450Smsmith size_t *inbytesleft, char **outbuf, size_t *outbytesleft) 27269450Smsmith{ 27369450Smsmith return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft); 27469450Smsmith} 27569450Smsmith 27669450Smsmith/* 27769450Smsmith * Give a list of loaded converters. Each name terminated with 0. 27869450Smsmith * An empty string terminates the list. 27969450Smsmith */ 28069450Smsmithstatic int 28171867Smsmithiconv_sysctl_drvlist(SYSCTL_HANDLER_ARGS) 28269450Smsmith{ 28367754Smsmith struct iconv_converter_class *dcp; 28467754Smsmith const char *name; 28567754Smsmith char spc; 28667754Smsmith int error; 28767754Smsmith 28877424Smsmith error = 0; 28967754Smsmith 29091116Smsmith TAILQ_FOREACH(dcp, &iconv_converters, cc_link) { 29187031Smsmith name = ICONV_CONVERTER_NAME(dcp); 29267754Smsmith if (name == NULL) 29367754Smsmith continue; 29467754Smsmith error = SYSCTL_OUT(req, name, strlen(name) + 1); 29567754Smsmith if (error) 29667754Smsmith break; 29767754Smsmith } 29867754Smsmith if (error) 29967754Smsmith return error; 30067754Smsmith spc = 0; 30167754Smsmith error = SYSCTL_OUT(req, &spc, sizeof(spc)); 30277424Smsmith return error; 30387031Smsmith} 30467754Smsmith 30567754SmsmithSYSCTL_PROC(_kern_iconv, OID_AUTO, drvlist, CTLFLAG_RD | CTLTYPE_OPAQUE, 30667754Smsmith NULL, 0, iconv_sysctl_drvlist, "S,xlat", "registered converters"); 30767754Smsmith 30867754Smsmith/* 30991116Smsmith * List all available charset pairs. 31067754Smsmith */ 31167754Smsmithstatic int 31287031Smsmithiconv_sysctl_cslist(SYSCTL_HANDLER_ARGS) 31367754Smsmith{ 31487031Smsmith struct iconv_cspair *csp; 31567754Smsmith struct iconv_cspair_info csi; 31691116Smsmith int error; 31767754Smsmith 31891116Smsmith error = 0; 31977424Smsmith bzero(&csi, sizeof(csi)); 32067754Smsmith csi.cs_version = ICONV_CSPAIR_INFO_VER; 32177424Smsmith 32267754Smsmith TAILQ_FOREACH(csp, &iconv_cslist, cp_link) { 32377424Smsmith csi.cs_id = csp->cp_id; 32467754Smsmith csi.cs_refcount = csp->cp_refcount; 32582367Smsmith csi.cs_base = csp->cp_base ? csp->cp_base->cp_id : 0; 32680062Smsmith strcpy(csi.cs_to, csp->cp_to); 32767754Smsmith strcpy(csi.cs_from, csp->cp_from); 32867754Smsmith error = SYSCTL_OUT(req, &csi, sizeof(csi)); 32967754Smsmith if (error) 33067754Smsmith break; 33167754Smsmith } 33267754Smsmith return error; 33367754Smsmith} 33467754Smsmith 33567754SmsmithSYSCTL_PROC(_kern_iconv, OID_AUTO, cslist, CTLFLAG_RD | CTLTYPE_OPAQUE, 33677424Smsmith NULL, 0, iconv_sysctl_cslist, "S,xlat", "registered charset pairs"); 33767754Smsmith 33867754Smsmith/* 33967754Smsmith * Add new charset pair 34067754Smsmith */ 34167754Smsmithstatic int 34267754Smsmithiconv_sysctl_add(SYSCTL_HANDLER_ARGS) 34367754Smsmith{ 34467754Smsmith struct iconv_converter_class *dcp; 34567754Smsmith struct iconv_cspair *csp; 34667754Smsmith struct iconv_add_in din; 34767754Smsmith struct iconv_add_out dout; 34877424Smsmith int error; 34967754Smsmith 35067754Smsmith error = SYSCTL_IN(req, &din, sizeof(din)); 35167754Smsmith if (error) 35291116Smsmith return error; 35367754Smsmith if (din.ia_version != ICONV_ADD_VER) 35467754Smsmith return EINVAL; 35567754Smsmith if (din.ia_datalen > ICONV_CSMAXDATALEN) 35667754Smsmith return EINVAL; 35767754Smsmith if (iconv_lookupconv(din.ia_converter, &dcp) != 0) 35867754Smsmith return EINVAL; 35977424Smsmith error = iconv_register_cspair(din.ia_to, din.ia_from, dcp, NULL, &csp); 36067754Smsmith if (error) 36177424Smsmith return error; 36267754Smsmith if (din.ia_datalen) { 36367754Smsmith csp->cp_data = malloc(din.ia_datalen, M_ICONVDATA, 0); 36467754Smsmith error = copyin(din.ia_data, csp->cp_data, din.ia_datalen); 36567754Smsmith if (error) 36667754Smsmith goto bad; 36767754Smsmith } 36867754Smsmith dout.ia_csid = csp->cp_id; 36967754Smsmith error = SYSCTL_OUT(req, &dout, sizeof(dout)); 37077424Smsmith if (error) 37167754Smsmith goto bad; 37284491Smsmith return 0; 37384491Smsmithbad: 37467754Smsmith iconv_unregister_cspair(csp); 37584491Smsmith return error; 37667754Smsmith} 37767754Smsmith 37867754SmsmithSYSCTL_PROC(_kern_iconv, OID_AUTO, add, CTLFLAG_RW | CTLTYPE_OPAQUE, 37967754Smsmith NULL, 0, iconv_sysctl_add, "S,xlat", "register charset pair"); 38077424Smsmith 38184491Smsmith/* 38284491Smsmith * Default stubs for converters 38367754Smsmith */ 38467754Smsmithint 38567754Smsmithiconv_converter_initstub(struct iconv_converter_class *dp) 38667754Smsmith{ 38791116Smsmith return 0; 38867754Smsmith} 38967754Smsmith 39084491Smsmithint 39167754Smsmithiconv_converter_donestub(struct iconv_converter_class *dp) 39291116Smsmith{ 39367754Smsmith return 0; 39467754Smsmith} 39567754Smsmith 39677424Smsmithint 39784491Smsmithiconv_converter_handler(module_t mod, int type, void *data) 39877424Smsmith{ 39987031Smsmith struct iconv_converter_class *dcp = data; 40087031Smsmith int error; 40184491Smsmith 40267754Smsmith switch (type) { 40367754Smsmith case MOD_LOAD: 40467754Smsmith error = iconv_register_converter(dcp); 40567754Smsmith if (error) 40667754Smsmith break; 40767754Smsmith error = ICONV_CONVERTER_INIT(dcp); 40867754Smsmith if (error) 40967754Smsmith iconv_unregister_converter(dcp); 41067754Smsmith break; 41167754Smsmith case MOD_UNLOAD: 41267754Smsmith ICONV_CONVERTER_DONE(dcp); 41367754Smsmith error = iconv_unregister_converter(dcp); 41467754Smsmith break; 41567754Smsmith default: 41667754Smsmith error = EINVAL; 41767754Smsmith } 41867754Smsmith return error; 41969450Smsmith} 42067754Smsmith 42167754Smsmith/* 42267754Smsmith * Common used functions 42367754Smsmith */ 42467754Smsmithchar * 42567754Smsmithiconv_convstr(void *handle, char *dst, const char *src) 42667754Smsmith{ 42767754Smsmith char *p = dst; 42867754Smsmith size_t inlen, outlen; 42967754Smsmith int error; 43067754Smsmith 43167754Smsmith if (handle == NULL) { 43267754Smsmith strcpy(dst, src); 43367754Smsmith return dst; 43467754Smsmith } 43567754Smsmith inlen = outlen = strlen(src); 43691116Smsmith error = iconv_conv(handle, NULL, NULL, &p, &outlen); 43783174Smsmith if (error) 43883174Smsmith return NULL; 43967754Smsmith error = iconv_conv(handle, &src, &inlen, &p, &outlen); 44067754Smsmith if (error) 44167754Smsmith return NULL; 44267754Smsmith *p = 0; 44367754Smsmith return dst; 44467754Smsmith} 44567754Smsmith 44667754Smsmithvoid * 44767754Smsmithiconv_convmem(void *handle, void *dst, const void *src, int size) 44867754Smsmith{ 44967754Smsmith const char *s = src; 45067754Smsmith char *d = dst; 45167754Smsmith size_t inlen, outlen; 45277424Smsmith int error; 45367754Smsmith 45467754Smsmith if (size == 0) 45567754Smsmith return dst; 45667754Smsmith if (handle == NULL) { 45767754Smsmith memcpy(dst, src, size); 45867754Smsmith return dst; 45967754Smsmith } 46067754Smsmith inlen = outlen = size; 46167754Smsmith error = iconv_conv(handle, NULL, NULL, &d, &outlen); 46277424Smsmith if (error) 46367754Smsmith return NULL; 46467754Smsmith error = iconv_conv(handle, &s, &inlen, &d, &outlen); 46567754Smsmith if (error) 46667754Smsmith return NULL; 46767754Smsmith return dst; 46883174Smsmith} 46991116Smsmith 47083174Smsmithint 47183174Smsmithiconv_lookupcp(char **cpp, const char *s) 47267754Smsmith{ 47367754Smsmith if (cpp == NULL) { 47467754Smsmith ICDEBUG("warning a NULL list passed\n", ""); /* XXX ISO variadic macros cannot 47567754Smsmith leave out the 47667754Smsmith variadic args */ 47767754Smsmith return ENOENT; 47867754Smsmith } 47982367Smsmith for (; *cpp; cpp++) 48082367Smsmith if (strcmp(*cpp, s) == 0) 48182367Smsmith return 0; 48282367Smsmith return ENOENT; 48367754Smsmith} 48467754Smsmith