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