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