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