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