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/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 54 static iconv_t 55 __bsd___iconv_open(const char *out, const char *in, struct _citrus_iconv *handle) 56 { 57 int ret; 58 59 /* 60 * Remove anything following a //, as these are options (like 61 * //ignore, //translate, etc) and we just don't handle them. 62 * This is for compatibility with software that uses these 63 * blindly. 64 */ 65 ret = _citrus_iconv_open(&handle, in, out); 66 if (ret) { 67 errno = ret == ENOENT ? EINVAL : ret; 68 return ((iconv_t)-1); 69 } 70 71 handle->cv_shared->ci_discard_ilseq = strcasestr(out, "//IGNORE"); 72 73 return ((iconv_t)(void *)handle); 74 } 75 76 iconv_t 77 __bsd_iconv_open(const char *out, const char *in) 78 { 79 80 return (__bsd___iconv_open(out, in, NULL)); 81 } 82 83 int 84 __bsd_iconv_open_into(const char *out, const char *in, iconv_allocation_t *ptr) 85 { 86 struct _citrus_iconv *handle; 87 88 handle = (struct _citrus_iconv *)ptr; 89 return ((__bsd___iconv_open(out, in, handle) == (iconv_t)-1) ? -1 : 0); 90 } 91 92 int 93 __bsd_iconv_close(iconv_t handle) 94 { 95 96 if (ISBADF(handle)) { 97 errno = EBADF; 98 return (-1); 99 } 100 101 _citrus_iconv_close((struct _citrus_iconv *)(void *)handle); 102 103 return (0); 104 } 105 106 size_t 107 __bsd_iconv(iconv_t handle, char **in, size_t *szin, char **out, size_t *szout) 108 { 109 size_t ret; 110 int err; 111 112 if (ISBADF(handle)) { 113 errno = EBADF; 114 return ((size_t)-1); 115 } 116 117 err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle, 118 in, szin, out, szout, 0, &ret); 119 if (err) { 120 errno = err; 121 ret = (size_t)-1; 122 } 123 124 return (ret); 125 } 126 127 size_t 128 __bsd___iconv(iconv_t handle, char **in, size_t *szin, char **out, 129 size_t *szout, uint32_t flags, size_t *invalids) 130 { 131 size_t ret; 132 int err; 133 134 if (ISBADF(handle)) { 135 errno = EBADF; 136 return ((size_t)-1); 137 } 138 139 err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle, 140 in, szin, out, szout, flags, &ret); 141 if (invalids) 142 *invalids = ret; 143 if (err) { 144 errno = err; 145 ret = (size_t)-1; 146 } 147 148 return (ret); 149 } 150 151 int 152 __bsd___iconv_get_list(char ***rlist, size_t *rsz, bool sorted) 153 { 154 int ret; 155 156 ret = _citrus_esdb_get_list(rlist, rsz, sorted); 157 if (ret) { 158 errno = ret; 159 return (-1); 160 } 161 162 return (0); 163 } 164 165 void 166 __bsd___iconv_free_list(char **list, size_t sz) 167 { 168 169 _citrus_esdb_free_list(list, sz); 170 } 171 172 /* 173 * GNU-compatibile non-standard interfaces. 174 */ 175 static int 176 qsort_helper(const void *first, const void *second) 177 { 178 const char * const *s1; 179 const char * const *s2; 180 181 s1 = first; 182 s2 = second; 183 return (strcmp(*s1, *s2)); 184 } 185 186 void 187 __bsd_iconvlist(int (*do_one) (unsigned int, const char * const *, 188 void *), void *data) 189 { 190 char **list, **names; 191 const char * const *np; 192 char *curitem, *curkey, *slashpos; 193 size_t sz; 194 unsigned int i, j, n; 195 196 i = 0; 197 names = NULL; 198 199 if (__bsd___iconv_get_list(&list, &sz, true)) { 200 list = NULL; 201 goto out; 202 } 203 qsort((void *)list, sz, sizeof(char *), qsort_helper); 204 while (i < sz) { 205 j = 0; 206 slashpos = strchr(list[i], '/'); 207 names = malloc(sz * sizeof(char *)); 208 if (names == NULL) 209 goto out; 210 curkey = strndup(list[i], slashpos - list[i]); 211 if (curkey == NULL) 212 goto out; 213 names[j++] = curkey; 214 for (; (i < sz) && (memcmp(curkey, list[i], strlen(curkey)) == 0); i++) { 215 slashpos = strchr(list[i], '/'); 216 if (strcmp(curkey, &slashpos[1]) == 0) 217 continue; 218 curitem = strdup(&slashpos[1]); 219 if (curitem == NULL) 220 goto out; 221 names[j++] = curitem; 222 } 223 np = (const char * const *)names; 224 do_one(j, np, data); 225 for (n = 0; n < j; n++) 226 free(names[n]); 227 free(names); 228 names = NULL; 229 } 230 231 out: 232 if (names != NULL) { 233 for (n = 0; n < j; n++) 234 free(names[n]); 235 free(names); 236 } 237 if (list != NULL) 238 __bsd___iconv_free_list(list, sz); 239 } 240 241 __inline const char * 242 __bsd_iconv_canonicalize(const char *name) 243 { 244 245 return (_citrus_iconv_canonicalize(name)); 246 } 247 248 int 249 __bsd_iconvctl(iconv_t cd, int request, void *argument) 250 { 251 struct _citrus_iconv *cv; 252 struct iconv_hooks *hooks; 253 const char *convname, *dst; 254 int *i; 255 size_t srclen; 256 257 cv = (struct _citrus_iconv *)(void *)cd; 258 hooks = (struct iconv_hooks *)argument; 259 i = (int *)argument; 260 261 if (ISBADF(cd)) { 262 errno = EBADF; 263 return (-1); 264 } 265 266 switch (request) { 267 case ICONV_TRIVIALP: 268 convname = cv->cv_shared->ci_convname; 269 dst = strchr(convname, '/'); 270 srclen = dst - convname; 271 dst++; 272 *i = (srclen == strlen(dst)) && !memcmp(convname, dst, srclen); 273 return (0); 274 case ICONV_GET_TRANSLITERATE: 275 *i = 1; 276 return (0); 277 case ICONV_SET_TRANSLITERATE: 278 return ((*i == 1) ? 0 : -1); 279 case ICONV_GET_DISCARD_ILSEQ: 280 *i = cv->cv_shared->ci_discard_ilseq ? 1 : 0; 281 return (0); 282 case ICONV_SET_DISCARD_ILSEQ: 283 cv->cv_shared->ci_discard_ilseq = *i; 284 return (0); 285 case ICONV_SET_HOOKS: 286 cv->cv_shared->ci_hooks = hooks; 287 return (0); 288 case ICONV_SET_FALLBACKS: 289 errno = EOPNOTSUPP; 290 return (-1); 291 case ICONV_GET_ILSEQ_INVALID: 292 *i = cv->cv_shared->ci_ilseq_invalid ? 1 : 0; 293 return (0); 294 case ICONV_SET_ILSEQ_INVALID: 295 cv->cv_shared->ci_ilseq_invalid = *i; 296 return (0); 297 default: 298 errno = EINVAL; 299 return (-1); 300 } 301 } 302 303 void 304 __bsd_iconv_set_relocation_prefix(const char *orig_prefix __unused, 305 const char *curr_prefix __unused) 306 { 307 308 } 309