1/* $FreeBSD$ */ 2/* $NetBSD: iconv.c,v 1.11 2009/03/03 16:22:33 explorer Exp $ */ 3 4/*- 5 * Copyright (c) 2003 Citrus Project, 6 * Copyright (c) 2009, 2010 Gabor Kovesdan <gabor@FreeBSD.org>, 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32#include <sys/queue.h> 33#include <sys/types.h> 34 35#include <assert.h> 36#include <errno.h> 37#include <iconv.h> 38#include <limits.h> 39#include <paths.h> 40#include <stdbool.h> 41#include <stdlib.h> 42#include <string.h> 43 44#include "citrus_types.h" 45#include "citrus_module.h" 46#include "citrus_esdb.h" 47#include "citrus_hash.h" 48#include "citrus_iconv.h" 49 50#include "iconv-internal.h" 51 52#define ISBADF(_h_) (!(_h_) || (_h_) == (iconv_t)-1) 53 54static iconv_t 55__bsd___iconv_open(const char *out, const char *in, struct _citrus_iconv *handle) 56{ 57 const char *out_slashes; 58 char *out_noslashes; 59 int ret; 60 61 /* 62 * Remove anything following a //, as these are options (like 63 * //ignore, //translate, etc) and we just don't handle them. 64 * This is for compatibility with software that uses these 65 * blindly. 66 */ 67 out_slashes = strstr(out, "//"); 68 if (out_slashes != NULL) { 69 out_noslashes = strndup(out, out_slashes - out); 70 if (out_noslashes == NULL) { 71 errno = ENOMEM; 72 return ((iconv_t)-1); 73 } 74 ret = _citrus_iconv_open(&handle, in, out_noslashes); 75 free(out_noslashes); 76 } else { 77 ret = _citrus_iconv_open(&handle, in, out); 78 } 79 80 if (ret) { 81 errno = ret == ENOENT ? EINVAL : ret; 82 return ((iconv_t)-1); 83 } 84 85 handle->cv_shared->ci_discard_ilseq = strcasestr(out, "//IGNORE"); 86 handle->cv_shared->ci_hooks = NULL; 87 88 return ((iconv_t)(void *)handle); 89} 90 91iconv_t 92__bsd_iconv_open(const char *out, const char *in) 93{ 94 95 return (__bsd___iconv_open(out, in, NULL)); 96} 97 98int 99__bsd_iconv_open_into(const char *out, const char *in, iconv_allocation_t *ptr) 100{ 101 struct _citrus_iconv *handle; 102 103 handle = (struct _citrus_iconv *)ptr; 104 return ((__bsd___iconv_open(out, in, handle) == (iconv_t)-1) ? -1 : 0); 105} 106 107int 108__bsd_iconv_close(iconv_t handle) 109{ 110 111 if (ISBADF(handle)) { 112 errno = EBADF; 113 return (-1); 114 } 115 116 _citrus_iconv_close((struct _citrus_iconv *)(void *)handle); 117 118 return (0); 119} 120 121size_t 122__bsd_iconv(iconv_t handle, const char **in, size_t *szin, char **out, size_t *szout) 123{ 124 size_t ret; 125 int err; 126 127 if (ISBADF(handle)) { 128 errno = EBADF; 129 return ((size_t)-1); 130 } 131 132 err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle, 133 in, szin, out, szout, 0, &ret); 134 if (err) { 135 errno = err; 136 ret = (size_t)-1; 137 } 138 139 return (ret); 140} 141 142size_t 143__bsd___iconv(iconv_t handle, const char **in, size_t *szin, char **out, 144 size_t *szout, uint32_t flags, size_t *invalids) 145{ 146 size_t ret; 147 int err; 148 149 if (ISBADF(handle)) { 150 errno = EBADF; 151 return ((size_t)-1); 152 } 153 154 err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle, 155 in, szin, out, szout, flags, &ret); 156 if (invalids) 157 *invalids = ret; 158 if (err) { 159 errno = err; 160 ret = (size_t)-1; 161 } 162 163 return (ret); 164} 165 166int 167__bsd___iconv_get_list(char ***rlist, size_t *rsz, bool sorted) 168{ 169 int ret; 170 171 ret = _citrus_esdb_get_list(rlist, rsz, sorted); 172 if (ret) { 173 errno = ret; 174 return (-1); 175 } 176 177 return (0); 178} 179 180void 181__bsd___iconv_free_list(char **list, size_t sz) 182{ 183 184 _citrus_esdb_free_list(list, sz); 185} 186 187/* 188 * GNU-compatibile non-standard interfaces. 189 */ 190static int 191qsort_helper(const void *first, const void *second) 192{ 193 const char * const *s1; 194 const char * const *s2; 195 196 s1 = first; 197 s2 = second; 198 return (strcmp(*s1, *s2)); 199} 200 201void 202__bsd_iconvlist(int (*do_one) (unsigned int, const char * const *, 203 void *), void *data) 204{ 205 char **list, **names; 206 const char * const *np; 207 char *curitem, *curkey, *slashpos; 208 size_t sz; 209 unsigned int i, j; 210 211 i = 0; 212 213 if (__bsd___iconv_get_list(&list, &sz, true)) 214 list = NULL; 215 qsort((void *)list, sz, sizeof(char *), qsort_helper); 216 while (i < sz) { 217 j = 0; 218 slashpos = strchr(list[i], '/'); 219 curkey = (char *)malloc(slashpos - list[i] + 2); 220 names = (char **)malloc(sz * sizeof(char *)); 221 if ((curkey == NULL) || (names == NULL)) { 222 __bsd___iconv_free_list(list, sz); 223 return; 224 } 225 strlcpy(curkey, list[i], slashpos - list[i] + 1); 226 names[j++] = strdup(curkey); 227 for (; (i < sz) && (memcmp(curkey, list[i], strlen(curkey)) == 0); i++) { 228 slashpos = strchr(list[i], '/'); 229 curitem = (char *)malloc(strlen(slashpos) + 1); 230 if (curitem == NULL) { 231 __bsd___iconv_free_list(list, sz); 232 return; 233 } 234 strlcpy(curitem, &slashpos[1], strlen(slashpos) + 1); 235 if (strcmp(curkey, curitem) == 0) { 236 continue; 237 } 238 names[j++] = strdup(curitem); 239 } 240 np = (const char * const *)names; 241 do_one(j, np, data); 242 free(names); 243 } 244 245 __bsd___iconv_free_list(list, sz); 246} 247 248__inline const char * 249__bsd_iconv_canonicalize(const char *name) 250{ 251 252 return (_citrus_iconv_canonicalize(name)); 253} 254 255int 256__bsd_iconvctl(iconv_t cd, int request, void *argument) 257{ 258 struct _citrus_iconv *cv; 259 struct iconv_hooks *hooks; 260 const char *convname; 261 char src[PATH_MAX], *dst; 262 int *i; 263 264 cv = (struct _citrus_iconv *)(void *)cd; 265 hooks = (struct iconv_hooks *)argument; 266 i = (int *)argument; 267 268 if (ISBADF(cd)) { 269 errno = EBADF; 270 return (-1); 271 } 272 273 switch (request) { 274 case ICONV_TRIVIALP: 275 convname = cv->cv_shared->ci_convname; 276 dst = strchr(convname, '/'); 277 278 strlcpy(src, convname, dst - convname + 1); 279 dst++; 280 if ((convname == NULL) || (src == NULL) || (dst == NULL)) 281 return (-1); 282 *i = strcmp(src, dst) == 0 ? 1 : 0; 283 return (0); 284 case ICONV_GET_TRANSLITERATE: 285 *i = 1; 286 return (0); 287 case ICONV_SET_TRANSLITERATE: 288 return ((*i == 1) ? 0 : -1); 289 case ICONV_GET_DISCARD_ILSEQ: 290 *i = cv->cv_shared->ci_discard_ilseq ? 1 : 0; 291 return (0); 292 case ICONV_SET_DISCARD_ILSEQ: 293 cv->cv_shared->ci_discard_ilseq = *i; 294 return (0); 295 case ICONV_SET_HOOKS: 296 cv->cv_shared->ci_hooks = hooks; 297 return (0); 298 case ICONV_SET_FALLBACKS: 299 errno = EOPNOTSUPP; 300 return (-1); 301 case ICONV_GET_ILSEQ_INVALID: 302 *i = cv->cv_shared->ci_ilseq_invalid ? 1 : 0; 303 return (0); 304 case ICONV_SET_ILSEQ_INVALID: 305 cv->cv_shared->ci_ilseq_invalid = *i; 306 return (0); 307 default: 308 errno = EINVAL; 309 return (-1); 310 } 311} 312 313void 314__bsd_iconv_set_relocation_prefix(const char *orig_prefix __unused, 315 const char *curr_prefix __unused) 316{ 317 318} 319