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