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