Index: head/include/iconv.h =================================================================== --- head/include/iconv.h (revision 258536) +++ head/include/iconv.h (revision 258537) @@ -1,131 +1,133 @@ /* $FreeBSD$ */ /* $NetBSD: iconv.h,v 1.6 2005/02/03 04:39:32 perry Exp $ */ /*- * Copyright (c) 2003 Citrus Project, * Copyright (c) 2009, 2010 Gabor Kovesdan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef _ICONV_H_ #define _ICONV_H_ #include #include #include #include #include #ifdef __cplusplus typedef bool __iconv_bool; #elif __STDC_VERSION__ >= 199901L typedef _Bool __iconv_bool; #else typedef int __iconv_bool; #endif struct __tag_iconv_t; typedef struct __tag_iconv_t *iconv_t; __BEGIN_DECLS iconv_t iconv_open(const char *, const char *); size_t iconv(iconv_t, const char ** __restrict, size_t * __restrict, char ** __restrict, size_t * __restrict); int iconv_close(iconv_t); /* * non-portable interfaces for iconv */ int __iconv_get_list(char ***, size_t *, __iconv_bool); void __iconv_free_list(char **, size_t); size_t __iconv(iconv_t, const char **, size_t *, char **, size_t *, __uint32_t, size_t *); #define __ICONV_F_HIDE_INVALID 0x0001 /* * GNU interfaces for iconv */ typedef struct { void *spaceholder[64]; } iconv_allocation_t; int iconv_open_into(const char *, const char *, iconv_allocation_t *); void iconv_set_relocation_prefix(const char *, const char *); /* * iconvctl() request macros */ #define ICONV_TRIVIALP 0 #define ICONV_GET_TRANSLITERATE 1 #define ICONV_SET_TRANSLITERATE 2 #define ICONV_GET_DISCARD_ILSEQ 3 #define ICONV_SET_DISCARD_ILSEQ 4 #define ICONV_SET_HOOKS 5 #define ICONV_SET_FALLBACKS 6 +#define ICONV_GET_ILSEQ_INVALID 128 +#define ICONV_SET_ILSEQ_INVALID 129 typedef void (*iconv_unicode_char_hook) (unsigned int mbr, void *data); typedef void (*iconv_wide_char_hook) (wchar_t wc, void *data); struct iconv_hooks { iconv_unicode_char_hook uc_hook; iconv_wide_char_hook wc_hook; void *data; }; /* * Fallbacks aren't supported but type definitions are provided for * source compatibility. */ typedef void (*iconv_unicode_mb_to_uc_fallback) (const char*, size_t, void (*write_replacement) (const unsigned int *, size_t, void*), void*, void*); typedef void (*iconv_unicode_uc_to_mb_fallback) (unsigned int, void (*write_replacement) (const char *, size_t, void*), void*, void*); typedef void (*iconv_wchar_mb_to_wc_fallback) (const char*, size_t, void (*write_replacement) (const wchar_t *, size_t, void*), void*, void*); typedef void (*iconv_wchar_wc_to_mb_fallback) (wchar_t, void (*write_replacement) (const char *, size_t, void*), void*, void*); struct iconv_fallbacks { iconv_unicode_mb_to_uc_fallback mb_to_uc_fallback; iconv_unicode_uc_to_mb_fallback uc_to_mb_fallback; iconv_wchar_mb_to_wc_fallback mb_to_wc_fallback; iconv_wchar_wc_to_mb_fallback wc_to_mb_fallback; void *data; }; void iconvlist(int (*do_one) (unsigned int, const char * const *, void *), void *); const char *iconv_canonicalize(const char *); int iconvctl(iconv_t, int, void *); __END_DECLS #endif /* !_ICONV_H_ */ Index: head/lib/libc/iconv/citrus_iconv_local.h =================================================================== --- head/lib/libc/iconv/citrus_iconv_local.h (revision 258536) +++ head/lib/libc/iconv/citrus_iconv_local.h (revision 258537) @@ -1,109 +1,110 @@ /* $FreeBSD$ */ /* $NetBSD: citrus_iconv_local.h,v 1.3 2008/02/09 14:56:20 junyoung Exp $ */ /*- * Copyright (c)2003 Citrus Project, * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _CITRUS_ICONV_LOCAL_H_ #define _CITRUS_ICONV_LOCAL_H_ #include #include #define _CITRUS_ICONV_GETOPS_FUNC_BASE(_n_) \ int _n_(struct _citrus_iconv_ops *) #define _CITRUS_ICONV_GETOPS_FUNC(_n_) \ _CITRUS_ICONV_GETOPS_FUNC_BASE(_citrus_##_n_##_iconv_getops) #define _CITRUS_ICONV_DECLS(_m_) \ static int _citrus_##_m_##_iconv_init_shared \ (struct _citrus_iconv_shared * __restrict, \ const char * __restrict, const char * __restrict); \ static void _citrus_##_m_##_iconv_uninit_shared \ (struct _citrus_iconv_shared *); \ static int _citrus_##_m_##_iconv_convert \ (struct _citrus_iconv * __restrict, \ const char * __restrict * __restrict, \ size_t * __restrict, \ char * __restrict * __restrict, \ size_t * __restrict outbytes, \ uint32_t, size_t * __restrict); \ static int _citrus_##_m_##_iconv_init_context \ (struct _citrus_iconv *); \ static void _citrus_##_m_##_iconv_uninit_context \ (struct _citrus_iconv *) #define _CITRUS_ICONV_DEF_OPS(_m_) \ extern struct _citrus_iconv_ops _citrus_##_m_##_iconv_ops; \ struct _citrus_iconv_ops _citrus_##_m_##_iconv_ops = { \ /* io_init_shared */ &_citrus_##_m_##_iconv_init_shared, \ /* io_uninit_shared */ &_citrus_##_m_##_iconv_uninit_shared, \ /* io_init_context */ &_citrus_##_m_##_iconv_init_context, \ /* io_uninit_context */ &_citrus_##_m_##_iconv_uninit_context, \ /* io_convert */ &_citrus_##_m_##_iconv_convert \ } typedef _CITRUS_ICONV_GETOPS_FUNC_BASE((*_citrus_iconv_getops_t)); typedef int (*_citrus_iconv_init_shared_t) (struct _citrus_iconv_shared * __restrict, const char * __restrict, const char * __restrict); typedef void (*_citrus_iconv_uninit_shared_t) (struct _citrus_iconv_shared *); typedef int (*_citrus_iconv_convert_t) (struct _citrus_iconv * __restrict, const char *__restrict* __restrict, size_t * __restrict, char * __restrict * __restrict, size_t * __restrict, uint32_t, size_t * __restrict); typedef int (*_citrus_iconv_init_context_t)(struct _citrus_iconv *); typedef void (*_citrus_iconv_uninit_context_t)(struct _citrus_iconv *); struct _citrus_iconv_ops { _citrus_iconv_init_shared_t io_init_shared; _citrus_iconv_uninit_shared_t io_uninit_shared; _citrus_iconv_init_context_t io_init_context; _citrus_iconv_uninit_context_t io_uninit_context; _citrus_iconv_convert_t io_convert; }; struct _citrus_iconv_shared { struct _citrus_iconv_ops *ci_ops; void *ci_closure; _CITRUS_HASH_ENTRY(_citrus_iconv_shared) ci_hash_entry; TAILQ_ENTRY(_citrus_iconv_shared) ci_tailq_entry; _citrus_module_t ci_module; unsigned int ci_used_count; char *ci_convname; bool ci_discard_ilseq; struct iconv_hooks *ci_hooks; + bool ci_ilseq_invalid; }; struct _citrus_iconv { struct _citrus_iconv_shared *cv_shared; void *cv_closure; }; #endif Index: head/lib/libc/iconv/iconv.c =================================================================== --- head/lib/libc/iconv/iconv.c (revision 258536) +++ head/lib/libc/iconv/iconv.c (revision 258537) @@ -1,312 +1,318 @@ /* $FreeBSD$ */ /* $NetBSD: iconv.c,v 1.11 2009/03/03 16:22:33 explorer Exp $ */ /*- * Copyright (c) 2003 Citrus Project, * Copyright (c) 2009, 2010 Gabor Kovesdan , * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include "citrus_types.h" #include "citrus_module.h" #include "citrus_esdb.h" #include "citrus_hash.h" #include "citrus_iconv.h" #include "iconv-internal.h" #define ISBADF(_h_) (!(_h_) || (_h_) == (iconv_t)-1) static iconv_t __bsd___iconv_open(const char *out, const char *in, struct _citrus_iconv *handle) { const char *out_slashes; char *out_noslashes; int ret; /* * Remove anything following a //, as these are options (like * //ignore, //translate, etc) and we just don't handle them. * This is for compatibility with software that uses these * blindly. */ out_slashes = strstr(out, "//"); if (out_slashes != NULL) { out_noslashes = strndup(out, out_slashes - out); if (out_noslashes == NULL) { errno = ENOMEM; return ((iconv_t)-1); } ret = _citrus_iconv_open(&handle, in, out_noslashes); free(out_noslashes); } else { ret = _citrus_iconv_open(&handle, in, out); } if (ret) { errno = ret == ENOENT ? EINVAL : ret; return ((iconv_t)-1); } handle->cv_shared->ci_discard_ilseq = strcasestr(out, "//IGNORE"); handle->cv_shared->ci_hooks = NULL; return ((iconv_t)(void *)handle); } iconv_t __bsd_iconv_open(const char *out, const char *in) { return (__bsd___iconv_open(out, in, NULL)); } int __bsd_iconv_open_into(const char *out, const char *in, iconv_allocation_t *ptr) { struct _citrus_iconv *handle; handle = (struct _citrus_iconv *)ptr; return ((__bsd___iconv_open(out, in, handle) == (iconv_t)-1) ? -1 : 0); } int __bsd_iconv_close(iconv_t handle) { if (ISBADF(handle)) { errno = EBADF; return (-1); } _citrus_iconv_close((struct _citrus_iconv *)(void *)handle); return (0); } size_t __bsd_iconv(iconv_t handle, const char **in, size_t *szin, char **out, size_t *szout) { size_t ret; int err; if (ISBADF(handle)) { errno = EBADF; return ((size_t)-1); } err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle, in, szin, out, szout, 0, &ret); if (err) { errno = err; ret = (size_t)-1; } return (ret); } size_t __bsd___iconv(iconv_t handle, const char **in, size_t *szin, char **out, size_t *szout, uint32_t flags, size_t *invalids) { size_t ret; int err; if (ISBADF(handle)) { errno = EBADF; return ((size_t)-1); } err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle, in, szin, out, szout, flags, &ret); if (invalids) *invalids = ret; if (err) { errno = err; ret = (size_t)-1; } return (ret); } int __bsd___iconv_get_list(char ***rlist, size_t *rsz, bool sorted) { int ret; ret = _citrus_esdb_get_list(rlist, rsz, sorted); if (ret) { errno = ret; return (-1); } return (0); } void __bsd___iconv_free_list(char **list, size_t sz) { _citrus_esdb_free_list(list, sz); } /* * GNU-compatibile non-standard interfaces. */ static int qsort_helper(const void *first, const void *second) { const char * const *s1; const char * const *s2; s1 = first; s2 = second; return (strcmp(*s1, *s2)); } void __bsd_iconvlist(int (*do_one) (unsigned int, const char * const *, void *), void *data) { char **list, **names; const char * const *np; char *curitem, *curkey, *slashpos; size_t sz; unsigned int i, j; i = 0; if (__bsd___iconv_get_list(&list, &sz, true)) list = NULL; qsort((void *)list, sz, sizeof(char *), qsort_helper); while (i < sz) { j = 0; slashpos = strchr(list[i], '/'); curkey = (char *)malloc(slashpos - list[i] + 2); names = (char **)malloc(sz * sizeof(char *)); if ((curkey == NULL) || (names == NULL)) { __bsd___iconv_free_list(list, sz); return; } strlcpy(curkey, list[i], slashpos - list[i] + 1); names[j++] = strdup(curkey); for (; (i < sz) && (memcmp(curkey, list[i], strlen(curkey)) == 0); i++) { slashpos = strchr(list[i], '/'); curitem = (char *)malloc(strlen(slashpos) + 1); if (curitem == NULL) { __bsd___iconv_free_list(list, sz); return; } strlcpy(curitem, &slashpos[1], strlen(slashpos) + 1); if (strcmp(curkey, curitem) == 0) { continue; } names[j++] = strdup(curitem); } np = (const char * const *)names; do_one(j, np, data); free(names); } __bsd___iconv_free_list(list, sz); } __inline const char * __bsd_iconv_canonicalize(const char *name) { return (_citrus_iconv_canonicalize(name)); } int __bsd_iconvctl(iconv_t cd, int request, void *argument) { struct _citrus_iconv *cv; struct iconv_hooks *hooks; const char *convname; char src[PATH_MAX], *dst; int *i; cv = (struct _citrus_iconv *)(void *)cd; hooks = (struct iconv_hooks *)argument; i = (int *)argument; if (ISBADF(cd)) { errno = EBADF; return (-1); } switch (request) { case ICONV_TRIVIALP: convname = cv->cv_shared->ci_convname; dst = strchr(convname, '/'); strlcpy(src, convname, dst - convname + 1); dst++; if ((convname == NULL) || (src == NULL) || (dst == NULL)) return (-1); *i = strcmp(src, dst) == 0 ? 1 : 0; return (0); case ICONV_GET_TRANSLITERATE: *i = 1; return (0); case ICONV_SET_TRANSLITERATE: return ((*i == 1) ? 0 : -1); case ICONV_GET_DISCARD_ILSEQ: *i = cv->cv_shared->ci_discard_ilseq ? 1 : 0; return (0); case ICONV_SET_DISCARD_ILSEQ: cv->cv_shared->ci_discard_ilseq = *i; return (0); case ICONV_SET_HOOKS: cv->cv_shared->ci_hooks = hooks; return (0); case ICONV_SET_FALLBACKS: errno = EOPNOTSUPP; return (-1); + case ICONV_GET_ILSEQ_INVALID: + *i = cv->cv_shared->ci_ilseq_invalid ? 1 : 0; + return (0); + case ICONV_SET_ILSEQ_INVALID: + cv->cv_shared->ci_ilseq_invalid = *i; + return (0); default: errno = EINVAL; return (-1); } } void __bsd_iconv_set_relocation_prefix(const char *orig_prefix __unused, const char *curr_prefix __unused) { } Index: head/lib/libc/iconv/iconvctl.3 =================================================================== --- head/lib/libc/iconv/iconvctl.3 (revision 258536) +++ head/lib/libc/iconv/iconvctl.3 (revision 258537) @@ -1,164 +1,189 @@ .\" Copyright (c) 2009 Gabor Kovesdan .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" Portions of this text are reprinted and reproduced in electronic form .\" from IEEE Std 1003.1, 2004 Edition, Standard for Information Technology -- .\" Portable Operating System Interface (POSIX), The Open Group Base .\" Specifications Issue 6, Copyright (C) 2001-2004 by the Institute of .\" Electrical and Electronics Engineers, Inc and The Open Group. In the .\" event of any discrepancy between this version and the original IEEE and .\" The Open Group Standard, the original IEEE and The Open Group Standard is .\" the referee document. The original Standard can be obtained online at .\" http://www.opengroup.org/unix/online.html. .\" .\" $FreeBSD$ .\" -.Dd November 7, 2009 +.Dd November 25, 2009 .Dt ICONVCTL 3 .Os .Sh NAME .Nm iconvctl .Nd controlling and diagnostical facility for .Xr iconv 3 .Sh LIBRARY .Lb libc .Sh SYNOPSIS .In iconv.h .Ft int .Fn iconvctl "iconv_t cd" "int request" "void *argument" .Sh DESCRIPTION The .Fn iconvctl function can retrieve or set specific conversion setting from the .Fa cd conversion descriptor. The .Fa request parameter specifies the operation to accomplish and .Fa argument is an operation-specific argument. .Pp The possible operations are the following: .Bl -tag -width indent .It ICONV_TRIVIALP In this case .Fa argument is an .Ft int * variable, which is set to 1 if the encoding is trivial one, i.e. the input and output encodings are the same. Otherwise, the variable will be 0. .It ICONV_GET_TRANSLITERATE Determines if transliteration is enabled. The answer is stored in .Fa argument , which is of .Ft int * . It will be set to 1 if this feature is enabled or set to 0 otherwise. .It ICONV_SET_TRANSLITERATE Enables transliteration if .Fa argument , which is of .Ft int * set to 1 or disables it if .Fa argument is set to 0. .It ICONV_GET_DISCARD_ILSEQ Determines if illegal sequences are discarded or not. The answer is stored in .Fa argument , which is of .Ft int * . It will be set to 1 if this feature is enabled or set to 0 otherwise. .It ICONV_SET_DISCARD_ILSEQ Sets whether illegal sequences are discarded or not. .Fa argument , which is of .Ft int * set to 1 or disables it if .Fa argument is set to 0. .It ICONV_SET_HOOKS Sets callback functions, which will be called back after successful conversions. The callback functions are stored in a .Ft struct iconv_hooks variable, which is passed to .Nm via .Fa argument by its address. +.It ICONV_GET_ILSEQ_INVALID +Determines if a character in the input buffer that is valid, +but for which an identical character does not exist in the target +codeset returns +.Er EILSEQ +or not. +The answer is stored in +.Fa argument , +which is of +.Ft int * . +It will be set to 1 if this feature is enabled or set to 0 otherwise. +.It ICONV_SET_ILSEQ_INVALID +Sets whether a character in the input buffer that is valid, +but for which an identical character does not exist in the target +codeset returns +.Er EILSEQ +or not. +If +.Fa argument , +which is of +.Ft int * +is set to 1 it will be enabled, +and if +.Fa argument +is set to 0 it will be disabled. .El .\" XXX: fallbacks are unimplemented and trying to set them will always .\" return EOPNOTSUPP but definitions are provided for source-level .\" compatibility. .\".It ICONV_SET_FALLBACKS .\"Sets callback functions, which will be called back after failed .\"conversions. .\"The callback functions are stored in a .\".Ft struct iconv_fallbacks .\"variable, which is passed to .\".Nm .\"via .\".Fa argument .\"by its address. .Sh RETURN VALUES Upon successful completion .Fn iconvctl , returns 0. Otherwise, \-1 is returned and errno is set to specify the kind of error. .Sh ERRORS The .Fn iconvctl function may cause an error in the following cases: .Bl -tag -width Er .It Bq Er EINVAL Unknown or unimplemented operation. .It Bq Er EBADF The conversion descriptor specified by .Fa cd is invalid. .El .Sh SEE ALSO .Xr iconv 1 , .Xr iconv 3 .Sh STANDARDS The .Nm facility is a non-standard extension, which appeared in the GNU implementation and was adopted in .Fx 9 for compatibility's sake. .Sh AUTHORS This manual page was written by .An Gabor Kovesdan Aq gabor@FreeBSD.org . .Sh BUGS Transliteration is enabled in this implementation by default, so it is impossible by design to turn it off. Accordingly, trying to turn it off will always fail and \-1 will be returned. Getting the transliteration state will always succeed and indicate that it is turned on, though. Index: head/lib/libiconv_modules/iconv_std/citrus_iconv_std.c =================================================================== --- head/lib/libiconv_modules/iconv_std/citrus_iconv_std.c (revision 258536) +++ head/lib/libiconv_modules/iconv_std/citrus_iconv_std.c (revision 258537) @@ -1,583 +1,593 @@ /* $FreeBSD$ */ /* $NetBSD: citrus_iconv_std.c,v 1.15 2006/11/13 19:08:19 tnozaki Exp $ */ /*- * Copyright (c)2003 Citrus Project, * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include "citrus_namespace.h" #include "citrus_types.h" #include "citrus_module.h" #include "citrus_region.h" #include "citrus_mmap.h" #include "citrus_hash.h" #include "citrus_iconv.h" #include "citrus_stdenc.h" #include "citrus_mapper.h" #include "citrus_csmapper.h" #include "citrus_memstream.h" #include "citrus_iconv_std.h" #include "citrus_esdb.h" /* ---------------------------------------------------------------------- */ _CITRUS_ICONV_DECLS(iconv_std); _CITRUS_ICONV_DEF_OPS(iconv_std); /* ---------------------------------------------------------------------- */ int _citrus_iconv_std_iconv_getops(struct _citrus_iconv_ops *ops) { memcpy(ops, &_citrus_iconv_std_iconv_ops, sizeof(_citrus_iconv_std_iconv_ops)); return (0); } /* ---------------------------------------------------------------------- */ /* * convenience routines for stdenc. */ static __inline void save_encoding_state(struct _citrus_iconv_std_encoding *se) { if (se->se_ps) memcpy(se->se_pssaved, se->se_ps, _stdenc_get_state_size(se->se_handle)); } static __inline void restore_encoding_state(struct _citrus_iconv_std_encoding *se) { if (se->se_ps) memcpy(se->se_ps, se->se_pssaved, _stdenc_get_state_size(se->se_handle)); } static __inline void init_encoding_state(struct _citrus_iconv_std_encoding *se) { if (se->se_ps) _stdenc_init_state(se->se_handle, se->se_ps); } static __inline int mbtocsx(struct _citrus_iconv_std_encoding *se, _csid_t *csid, _index_t *idx, const char **s, size_t n, size_t *nresult, struct iconv_hooks *hooks) { return (_stdenc_mbtocs(se->se_handle, csid, idx, s, n, se->se_ps, nresult, hooks)); } static __inline int cstombx(struct _citrus_iconv_std_encoding *se, char *s, size_t n, _csid_t csid, _index_t idx, size_t *nresult, struct iconv_hooks *hooks) { return (_stdenc_cstomb(se->se_handle, s, n, csid, idx, se->se_ps, nresult, hooks)); } static __inline int wctombx(struct _citrus_iconv_std_encoding *se, char *s, size_t n, _wc_t wc, size_t *nresult, struct iconv_hooks *hooks) { return (_stdenc_wctomb(se->se_handle, s, n, wc, se->se_ps, nresult, hooks)); } static __inline int put_state_resetx(struct _citrus_iconv_std_encoding *se, char *s, size_t n, size_t *nresult) { return (_stdenc_put_state_reset(se->se_handle, s, n, se->se_ps, nresult)); } static __inline int get_state_desc_gen(struct _citrus_iconv_std_encoding *se, int *rstate) { struct _stdenc_state_desc ssd; int ret; ret = _stdenc_get_state_desc(se->se_handle, se->se_ps, _STDENC_SDID_GENERIC, &ssd); if (!ret) *rstate = ssd.u.generic.state; return (ret); } /* * init encoding context */ static int init_encoding(struct _citrus_iconv_std_encoding *se, struct _stdenc *cs, void *ps1, void *ps2) { int ret = -1; se->se_handle = cs; se->se_ps = ps1; se->se_pssaved = ps2; if (se->se_ps) ret = _stdenc_init_state(cs, se->se_ps); if (!ret && se->se_pssaved) ret = _stdenc_init_state(cs, se->se_pssaved); return (ret); } static int open_csmapper(struct _csmapper **rcm, const char *src, const char *dst, unsigned long *rnorm) { struct _csmapper *cm; int ret; ret = _csmapper_open(&cm, src, dst, 0, rnorm); if (ret) return (ret); if (_csmapper_get_src_max(cm) != 1 || _csmapper_get_dst_max(cm) != 1 || _csmapper_get_state_size(cm) != 0) { _csmapper_close(cm); return (EINVAL); } *rcm = cm; return (0); } static void close_dsts(struct _citrus_iconv_std_dst_list *dl) { struct _citrus_iconv_std_dst *sd; while ((sd = TAILQ_FIRST(dl)) != NULL) { TAILQ_REMOVE(dl, sd, sd_entry); _csmapper_close(sd->sd_mapper); free(sd); } } static int open_dsts(struct _citrus_iconv_std_dst_list *dl, const struct _esdb_charset *ec, const struct _esdb *dbdst) { struct _citrus_iconv_std_dst *sd, *sdtmp; unsigned long norm; int i, ret; sd = malloc(sizeof(*sd)); if (sd == NULL) return (errno); for (i = 0; i < dbdst->db_num_charsets; i++) { ret = open_csmapper(&sd->sd_mapper, ec->ec_csname, dbdst->db_charsets[i].ec_csname, &norm); if (ret == 0) { sd->sd_csid = dbdst->db_charsets[i].ec_csid; sd->sd_norm = norm; /* insert this mapper by sorted order. */ TAILQ_FOREACH(sdtmp, dl, sd_entry) { if (sdtmp->sd_norm > norm) { TAILQ_INSERT_BEFORE(sdtmp, sd, sd_entry); sd = NULL; break; } } if (sd) TAILQ_INSERT_TAIL(dl, sd, sd_entry); sd = malloc(sizeof(*sd)); if (sd == NULL) { ret = errno; close_dsts(dl); return (ret); } } else if (ret != ENOENT) { close_dsts(dl); free(sd); return (ret); } } free(sd); return (0); } static void close_srcs(struct _citrus_iconv_std_src_list *sl) { struct _citrus_iconv_std_src *ss; while ((ss = TAILQ_FIRST(sl)) != NULL) { TAILQ_REMOVE(sl, ss, ss_entry); close_dsts(&ss->ss_dsts); free(ss); } } static int open_srcs(struct _citrus_iconv_std_src_list *sl, const struct _esdb *dbsrc, const struct _esdb *dbdst) { struct _citrus_iconv_std_src *ss; int count = 0, i, ret; ss = malloc(sizeof(*ss)); if (ss == NULL) return (errno); TAILQ_INIT(&ss->ss_dsts); for (i = 0; i < dbsrc->db_num_charsets; i++) { ret = open_dsts(&ss->ss_dsts, &dbsrc->db_charsets[i], dbdst); if (ret) goto err; if (!TAILQ_EMPTY(&ss->ss_dsts)) { ss->ss_csid = dbsrc->db_charsets[i].ec_csid; TAILQ_INSERT_TAIL(sl, ss, ss_entry); ss = malloc(sizeof(*ss)); if (ss == NULL) { ret = errno; goto err; } count++; TAILQ_INIT(&ss->ss_dsts); } } free(ss); return (count ? 0 : ENOENT); err: free(ss); close_srcs(sl); return (ret); } /* do convert a character */ #define E_NO_CORRESPONDING_CHAR ENOENT /* XXX */ static int /*ARGSUSED*/ do_conv(const struct _citrus_iconv_std_shared *is, _csid_t *csid, _index_t *idx) { struct _citrus_iconv_std_dst *sd; struct _citrus_iconv_std_src *ss; _index_t tmpidx; int ret; TAILQ_FOREACH(ss, &is->is_srcs, ss_entry) { if (ss->ss_csid == *csid) { TAILQ_FOREACH(sd, &ss->ss_dsts, sd_entry) { ret = _csmapper_convert(sd->sd_mapper, &tmpidx, *idx, NULL); switch (ret) { case _MAPPER_CONVERT_SUCCESS: *csid = sd->sd_csid; *idx = tmpidx; return (0); case _MAPPER_CONVERT_NONIDENTICAL: break; case _MAPPER_CONVERT_SRC_MORE: /*FALLTHROUGH*/ case _MAPPER_CONVERT_DST_MORE: /*FALLTHROUGH*/ case _MAPPER_CONVERT_ILSEQ: return (EILSEQ); case _MAPPER_CONVERT_FATAL: return (EINVAL); } } break; } } return (E_NO_CORRESPONDING_CHAR); } /* ---------------------------------------------------------------------- */ static int /*ARGSUSED*/ _citrus_iconv_std_iconv_init_shared(struct _citrus_iconv_shared *ci, const char * __restrict src, const char * __restrict dst) { struct _citrus_esdb esdbdst, esdbsrc; struct _citrus_iconv_std_shared *is; int ret; is = malloc(sizeof(*is)); if (is == NULL) { ret = errno; goto err0; } ret = _citrus_esdb_open(&esdbsrc, src); if (ret) goto err1; ret = _citrus_esdb_open(&esdbdst, dst); if (ret) goto err2; ret = _stdenc_open(&is->is_src_encoding, esdbsrc.db_encname, esdbsrc.db_variable, esdbsrc.db_len_variable); if (ret) goto err3; ret = _stdenc_open(&is->is_dst_encoding, esdbdst.db_encname, esdbdst.db_variable, esdbdst.db_len_variable); if (ret) goto err4; is->is_use_invalid = esdbdst.db_use_invalid; is->is_invalid = esdbdst.db_invalid; TAILQ_INIT(&is->is_srcs); ret = open_srcs(&is->is_srcs, &esdbsrc, &esdbdst); if (ret) goto err5; _esdb_close(&esdbsrc); _esdb_close(&esdbdst); ci->ci_closure = is; return (0); err5: _stdenc_close(is->is_dst_encoding); err4: _stdenc_close(is->is_src_encoding); err3: _esdb_close(&esdbdst); err2: _esdb_close(&esdbsrc); err1: free(is); err0: return (ret); } static void _citrus_iconv_std_iconv_uninit_shared(struct _citrus_iconv_shared *ci) { struct _citrus_iconv_std_shared *is = ci->ci_closure; if (is == NULL) return; _stdenc_close(is->is_src_encoding); _stdenc_close(is->is_dst_encoding); close_srcs(&is->is_srcs); free(is); } static int _citrus_iconv_std_iconv_init_context(struct _citrus_iconv *cv) { const struct _citrus_iconv_std_shared *is = cv->cv_shared->ci_closure; struct _citrus_iconv_std_context *sc; char *ptr; size_t sz, szpsdst, szpssrc; szpssrc = _stdenc_get_state_size(is->is_src_encoding); szpsdst = _stdenc_get_state_size(is->is_dst_encoding); sz = (szpssrc + szpsdst)*2 + sizeof(struct _citrus_iconv_std_context); sc = malloc(sz); if (sc == NULL) return (errno); ptr = (char *)&sc[1]; if (szpssrc > 0) init_encoding(&sc->sc_src_encoding, is->is_src_encoding, ptr, ptr+szpssrc); else init_encoding(&sc->sc_src_encoding, is->is_src_encoding, NULL, NULL); ptr += szpssrc*2; if (szpsdst > 0) init_encoding(&sc->sc_dst_encoding, is->is_dst_encoding, ptr, ptr+szpsdst); else init_encoding(&sc->sc_dst_encoding, is->is_dst_encoding, NULL, NULL); cv->cv_closure = (void *)sc; return (0); } static void _citrus_iconv_std_iconv_uninit_context(struct _citrus_iconv *cv) { free(cv->cv_closure); } static int _citrus_iconv_std_iconv_convert(struct _citrus_iconv * __restrict cv, const char * __restrict * __restrict in, size_t * __restrict inbytes, char * __restrict * __restrict out, size_t * __restrict outbytes, uint32_t flags, size_t * __restrict invalids) { const struct _citrus_iconv_std_shared *is = cv->cv_shared->ci_closure; struct _citrus_iconv_std_context *sc = cv->cv_closure; _csid_t csid; _index_t idx; const char *tmpin; size_t inval, szrin, szrout; int ret, state = 0; inval = 0; if (in == NULL || *in == NULL) { /* special cases */ if (out != NULL && *out != NULL) { /* init output state and store the shift sequence */ save_encoding_state(&sc->sc_src_encoding); save_encoding_state(&sc->sc_dst_encoding); szrout = 0; ret = put_state_resetx(&sc->sc_dst_encoding, *out, *outbytes, &szrout); if (ret) goto err; if (szrout == (size_t)-2) { /* too small to store the character */ ret = EINVAL; goto err; } *out += szrout; *outbytes -= szrout; } else /* otherwise, discard the shift sequence */ init_encoding_state(&sc->sc_dst_encoding); init_encoding_state(&sc->sc_src_encoding); *invalids = 0; return (0); } /* normal case */ for (;;) { if (*inbytes == 0) { ret = get_state_desc_gen(&sc->sc_src_encoding, &state); if (state == _STDENC_SDGEN_INITIAL || state == _STDENC_SDGEN_STABLE) break; } /* save the encoding states for the error recovery */ save_encoding_state(&sc->sc_src_encoding); save_encoding_state(&sc->sc_dst_encoding); /* mb -> csid/index */ tmpin = *in; szrin = szrout = 0; ret = mbtocsx(&sc->sc_src_encoding, &csid, &idx, &tmpin, *inbytes, &szrin, cv->cv_shared->ci_hooks); if (ret) goto err; if (szrin == (size_t)-2) { /* incompleted character */ ret = get_state_desc_gen(&sc->sc_src_encoding, &state); if (ret) { ret = EINVAL; goto err; } switch (state) { case _STDENC_SDGEN_INITIAL: case _STDENC_SDGEN_STABLE: /* fetch shift sequences only. */ goto next; } ret = EINVAL; goto err; } /* convert the character */ ret = do_conv(is, &csid, &idx); if (ret) { if (ret == E_NO_CORRESPONDING_CHAR) { + /* + * GNU iconv returns EILSEQ when no + * corresponding character in the output. + * Some software depends on this behavior + * though this is against POSIX specification. + */ + if (cv->cv_shared->ci_ilseq_invalid != 0) { + ret = EILSEQ; + goto err; + } inval++; szrout = 0; if ((((flags & _CITRUS_ICONV_F_HIDE_INVALID) == 0) && !cv->cv_shared->ci_discard_ilseq) && is->is_use_invalid) { ret = wctombx(&sc->sc_dst_encoding, *out, *outbytes, is->is_invalid, &szrout, cv->cv_shared->ci_hooks); if (ret) goto err; } goto next; } else goto err; } /* csid/index -> mb */ ret = cstombx(&sc->sc_dst_encoding, *out, *outbytes, csid, idx, &szrout, cv->cv_shared->ci_hooks); if (ret) goto err; next: *inbytes -= tmpin-*in; /* szrin is insufficient on \0. */ *in = tmpin; *outbytes -= szrout; *out += szrout; } *invalids = inval; return (0); err: restore_encoding_state(&sc->sc_src_encoding); restore_encoding_state(&sc->sc_dst_encoding); *invalids = inval; return (ret); }