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