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