2 Unix SMB/CIFS implementation.
3 Character set conversion Extensions
4 Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001
5 Copyright (C) Andrew Tridgell 2001
6 Copyright (C) Simo Sorce 2001
7 Copyright (C) Martin Pool 2003
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #endif /* HAVE_CONFIG_H */
32 #include <sys/param.h>
35 #include <atalk/logger.h>
38 #include <netatalk/endian.h>
39 #include <atalk/unicode.h>
41 #ifdef HAVE_USABLE_ICONV
52 #include "byteorder.h"
58 * @brief Character-set conversion routines built on our iconv.
60 * @note Samba's internal character set (at least in the 3.0 series)
61 * is always the same as the one for the Unix filesystem. It is
62 * <b>not</b> necessarily UTF-8 and may be different on machines that
63 * need i18n filenames to be compatible with Unix software. It does
64 * have to be a superset of ASCII. All multibyte sequences must start
65 * with a byte with the high bit set.
71 #define MAX_CHARSETS 10
73 #define CHECK_FLAGS(a,b) (((a)!=NULL) ? (*(a) & (b)) : 0 )
75 static atalk_iconv_t conv_handles[MAX_CHARSETS][MAX_CHARSETS];
76 static char* charset_names[MAX_CHARSETS];
77 static struct charset_functions* charsets[MAX_CHARSETS];
78 static char hexdig[] = "0123456789abcdef";
79 #define hextoint( c ) ( isdigit( c ) ? c - '0' : c + 10 - 'a' )
82 * Return the name of a charset to give to iconv().
84 static const char *charset_name(charset_t ch)
86 const char *ret = NULL;
88 if (ch == CH_UCS2) ret = "UCS-2";
89 else if (ch == CH_UNIX) ret = "LOCALE"; /*lp_unix_charset();*/
90 else if (ch == CH_MAC) ret = "MAC_ROMAN"; /*lp_display_charset();*/
91 else if (ch == CH_UTF8) ret = "UTF8";
92 else if (ch == CH_UTF8_MAC) ret = "UTF8-MAC";
95 ret = charset_names[ch];
97 #if defined(HAVE_NL_LANGINFO) && defined(CODESET)
98 if (ret && strcasecmp(ret, "LOCALE") == 0) {
99 const char *ln = NULL;
101 #ifdef HAVE_SETLOCALE
102 setlocale(LC_ALL, "");
104 ln = nl_langinfo(CODESET);
106 /* Check whether the charset name is supported
108 atalk_iconv_t handle = atalk_iconv_open(ln,"UCS-2");
109 if (handle == (atalk_iconv_t) -1) {
110 LOG(log_debug, logtype_default, "Locale charset '%s' unsupported, using ASCII instead", ln);
113 atalk_iconv_close(handle);
118 #else /* system doesn't have LOCALE support */
119 if (ch == CH_UNIX) ret = NULL;
122 if (!ret || !*ret) ret = "ASCII";
126 struct charset_functions* get_charset_functions (charset_t ch)
128 if (charsets[ch] != NULL)
131 charsets[ch] = find_charset_functions(charset_name(ch));
137 void lazy_initialize_conv(void)
139 static int initialized = 0;
147 charset_t add_charset(char* name)
149 static charset_t max_charset_t = NUM_CHARSETS-1;
150 charset_t cur_charset_t = max_charset_t+1;
153 lazy_initialize_conv();
155 for (c1=0; c1<=max_charset_t;c1++) {
156 if ( strcasecmp(name, charset_name(c1)) == 0)
160 if ( cur_charset_t >= MAX_CHARSETS ) {
161 LOG (log_debug, logtype_default, "Adding charset %s failed, too many charsets (max. %u allowed)",
163 return (charset_t) -1;
166 /* First try to setup the required conversions */
168 conv_handles[cur_charset_t][CH_UCS2] = atalk_iconv_open( charset_name(CH_UCS2), name);
169 if (conv_handles[cur_charset_t][CH_UCS2] == (atalk_iconv_t)-1) {
170 LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
171 name, charset_name(CH_UCS2));
172 conv_handles[cur_charset_t][CH_UCS2] = NULL;
173 return (charset_t) -1;
176 conv_handles[CH_UCS2][cur_charset_t] = atalk_iconv_open( name, charset_name(CH_UCS2));
177 if (conv_handles[CH_UCS2][cur_charset_t] == (atalk_iconv_t)-1) {
178 LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
179 charset_name(CH_UCS2), name);
180 conv_handles[CH_UCS2][cur_charset_t] = NULL;
181 return (charset_t) -1;
184 /* register the new charset_t name */
185 charset_names[cur_charset_t] = strdup(name);
187 charsets[cur_charset_t] = get_charset_functions (cur_charset_t);
191 LOG(log_debug, logtype_default, "Added charset %s with handle %u", name, cur_charset_t);
193 return (cur_charset_t);
197 * Initialize iconv conversion descriptors.
199 * This is called the first time it is needed, and also called again
200 * every time the configuration is reloaded, because the charset or
201 * codepage might have changed.
203 void init_iconv(void)
207 /* so that charset_name() works we need to get the UNIX<->UCS2 going
209 if (!conv_handles[CH_UNIX][CH_UCS2])
210 conv_handles[CH_UNIX][CH_UCS2] = atalk_iconv_open("UCS-2", "ASCII");
212 if (!conv_handles[CH_UCS2][CH_UNIX])
213 conv_handles[CH_UCS2][CH_UNIX] = atalk_iconv_open("ASCII", "UCS-2");
215 for (c1=0;c1<NUM_CHARSETS;c1++) {
216 const char *name = charset_name((charset_t)c1);
218 conv_handles[c1][CH_UCS2] = atalk_iconv_open( charset_name(CH_UCS2), name);
219 if (conv_handles[c1][CH_UCS2] == (atalk_iconv_t)-1) {
220 LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
221 name, charset_name(CH_UCS2));
222 conv_handles[c1][CH_UCS2] = NULL;
225 conv_handles[CH_UCS2][c1] = atalk_iconv_open( name, charset_name(CH_UCS2));
226 if (conv_handles[CH_UCS2][1] == (atalk_iconv_t)-1) {
227 LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
228 charset_name(CH_UCS2), name);
229 conv_handles[c1][c1] = NULL;
232 charsets[c1] = get_charset_functions (c1);
237 * Convert string from one encoding to another, making error checking etc
239 * @param src pointer to source string (multibyte or singlebyte)
240 * @param srclen length of the source string in bytes
241 * @param dest pointer to destination string (multibyte or singlebyte)
242 * @param destlen maximal length allowed for string
243 * @returns the number of bytes occupied in the destination
245 static size_t convert_string_internal(charset_t from, charset_t to,
246 void const *src, size_t srclen,
247 void *dest, size_t destlen)
251 const char* inbuf = (const char*)src;
252 char* outbuf = (char*)dest;
253 char* o_save = outbuf;
254 atalk_iconv_t descriptor;
256 if (srclen == (size_t)-1)
257 srclen = strlen(src)+1;
259 lazy_initialize_conv();
261 descriptor = conv_handles[from][to];
263 if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
269 retval = atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len);
270 if(retval==(size_t)-1) {
271 const char *reason="unknown error";
274 reason="Incomplete multibyte sequence";
277 reason="No more room";
280 reason="Illegal multibyte sequence";
283 LOG(log_debug, logtype_default,"Conversion error: %s(%s)\n",reason,inbuf);
287 /* Terminate the string */
288 if (to == CH_UCS2 && destlen-o_len >= 2) {
289 o_save[destlen-o_len] = 0;
290 o_save[destlen-o_len+1] = 0;
292 else if ( destlen-o_len > 0)
293 o_save[destlen-o_len] = 0;
295 return destlen-o_len;
299 size_t convert_string(charset_t from, charset_t to,
300 void const *src, size_t srclen,
301 void *dest, size_t destlen)
305 char buffer[MAXPATHLEN];
306 char buffer2[MAXPATHLEN];
309 lazy_initialize_conv();
311 /* convert from_set to UCS2 */
312 if ((size_t)(-1) == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen, buffer, MAXPATHLEN)) ) {
313 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from));
317 /* Do pre/decomposition */
318 if ( ((!(charsets[to]) || !(charsets[to]->flags & CHARSET_DECOMPOSED)) &&
319 (!(charsets[from]) || (charsets[from]->flags & CHARSET_DECOMPOSED))))
321 if ((charsets[to] && charsets[to]->flags & CHARSET_DECOMPOSED) )
327 switch (composition) {
333 if ( (size_t)-1 == (i_len = precompose_w((ucs2_t *)buffer, o_len, (ucs2_t *)u, &i_len)) )
337 if ( (size_t)-1 == (i_len = decompose_w((ucs2_t *)buffer, o_len, (ucs2_t *)u, &i_len)) )
342 /* Convert UCS2 to to_set */
343 if ((size_t)(-1) == ( o_len = convert_string_internal( CH_UCS2, to, u, i_len, dest, destlen)) ) {
344 LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
354 * Convert between character sets, allocating a new buffer for the result.
356 * @param srclen length of source buffer.
357 * @param dest always set at least to NULL
358 * @note -1 is not accepted for srclen.
360 * @returns Size in bytes of the converted string; or -1 in case of error.
363 static size_t convert_string_allocate_internal(charset_t from, charset_t to,
364 void const *src, size_t srclen, char **dest)
366 size_t i_len, o_len, destlen;
368 const char *inbuf = (const char *)src;
370 atalk_iconv_t descriptor;
374 if (src == NULL || srclen == (size_t)-1)
377 lazy_initialize_conv();
379 descriptor = conv_handles[from][to];
381 if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
382 /* conversion not supported, return -1*/
383 LOG(log_debug, logtype_default, "convert_string_allocate: conversion not supported!\n");
387 destlen = MAX(srclen, 512);
390 destlen = destlen * 2;
391 ob = (char *)realloc(outbuf, destlen);
393 LOG(log_debug, logtype_default,"convert_string_allocate: realloc failed!\n");
401 retval = atalk_iconv(descriptor,
404 if(retval == (size_t)-1) {
405 const char *reason="unknown error";
408 reason="Incomplete multibyte sequence";
413 reason="Illegal multibyte sequence";
416 LOG(log_debug, logtype_default,"Conversion error: %s(%s)\n",reason,inbuf);
417 /* smb_panic(reason); */
422 destlen = destlen - o_len;
424 /* Terminate the string */
425 if (to == CH_UCS2 && destlen-o_len >= 2) {
428 *dest = (char *)realloc(ob,destlen+2);
430 else if ( destlen-o_len > 0) {
432 *dest = (char *)realloc(ob,destlen+1);
435 if (destlen && !*dest) {
436 LOG(log_debug, logtype_default, "convert_string_allocate: out of memory!\n");
445 size_t convert_string_allocate(charset_t from, charset_t to,
446 void const *src, size_t srclen,
451 char buffer[MAXPATHLEN];
452 char buffer2[MAXPATHLEN];
455 lazy_initialize_conv();
457 /* convert from_set to UCS2 */
458 if ((size_t)(-1) == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen, buffer, MAXPATHLEN)) ) {
459 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from));
463 /* Do pre/decomposition */
464 if ( ((!(charsets[to]) || !(charsets[to]->flags & CHARSET_DECOMPOSED)) &&
465 (!(charsets[from]) || (charsets[from]->flags & CHARSET_DECOMPOSED))))
467 if ((charsets[to] && charsets[to]->flags & CHARSET_DECOMPOSED) )
473 switch (composition) {
479 if ( (size_t)-1 == (i_len = precompose_w((ucs2_t *)buffer, o_len, (ucs2_t *)u, &i_len)) )
483 if ( (size_t)-1 == (i_len = decompose_w((ucs2_t *)buffer, o_len, (ucs2_t *)u, &i_len)) )
488 /* Convert UCS2 to to_set */
489 if ((size_t)(-1) == ( o_len = convert_string_allocate_internal( CH_UCS2, to, u, i_len, dest)) )
490 LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
496 size_t charset_strupper(charset_t ch, const char *src, size_t srclen, char *dest, size_t destlen)
501 size = convert_string_allocate_internal(ch, CH_UCS2, src, srclen,
503 if (size == (size_t)-1) {
507 if (!strupper_w((ucs2_t *)buffer) && (dest == src)) {
512 size = convert_string_internal(CH_UCS2, ch, buffer, size, dest, destlen);
517 size_t charset_strlower(charset_t ch, const char *src, size_t srclen, char *dest, size_t destlen)
522 size = convert_string_allocate_internal(ch, CH_UCS2, src, srclen,
524 if (size == (size_t)-1) {
528 if (!strlower_w((ucs2_t *)buffer) && (dest == src)) {
533 size = convert_string_internal(CH_UCS2, ch, buffer, size, dest, destlen);
539 size_t unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
541 return charset_strupper( CH_UNIX, src, srclen, dest, destlen);
544 size_t unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
546 return charset_strlower( CH_UNIX, src, srclen, dest, destlen);
549 size_t utf8_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
551 return charset_strupper( CH_UTF8, src, srclen, dest, destlen);
554 size_t utf8_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
556 return charset_strlower( CH_UTF8, src, srclen, dest, destlen);
560 * Copy a string from a charset_t char* src to a UCS2 destination, allocating a buffer
562 * @param dest always set at least to NULL
564 * @returns The number of bytes occupied by the string in the destination
565 * or -1 in case of error.
568 size_t charset_to_ucs2_allocate(charset_t ch, ucs2_t **dest, const char *src)
570 size_t src_len = strlen(src)+1;
573 return convert_string_allocate(ch, CH_UCS2, src, src_len, (char**) dest);
577 * Copy a string from a charset_t char* src to a UTF-8 destination, allocating a buffer
579 * @param dest always set at least to NULL
581 * @returns The number of bytes occupied by the string in the destination
584 size_t charset_to_utf8_allocate(charset_t ch, char **dest, const char *src)
586 size_t src_len = strlen(src)+1;
589 return convert_string_allocate(ch, CH_UTF8, src, src_len, dest);
593 * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer
595 * @param dest always set at least to NULL
597 * @returns The number of bytes occupied by the string in the destination
600 size_t ucs2_to_charset_allocate(charset_t ch, char **dest, const ucs2_t *src)
602 size_t src_len = (strlen_w(src)+1) * sizeof(ucs2_t);
604 return convert_string_allocate(CH_UCS2, ch, src, src_len, dest);
608 * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer
610 * @param dest always set at least to NULL
612 * @returns The number of bytes occupied by the string in the destination
615 size_t utf8_to_charset_allocate(charset_t ch, char **dest, const char *src)
617 size_t src_len = strlen(src)+1;
619 return convert_string_allocate(CH_UTF8, ch, src, src_len, dest);
622 size_t charset_precompose ( charset_t ch, char * src, size_t inlen, char * dst, size_t outlen)
629 if ((size_t)(-1) == (len = convert_string_allocate_internal(ch, CH_UCS2, src, inlen, &buffer)) )
634 if ( (size_t)-1 == (ilen = precompose_w((ucs2_t *)buffer, len, (ucs2_t *)u, &ilen)) ) {
639 if ((size_t)(-1) == (len = convert_string_internal( CH_UCS2, ch, u, ilen, dst, outlen)) ) {
649 size_t charset_decompose ( charset_t ch, char * src, size_t inlen, char * dst, size_t outlen)
656 if ((size_t)(-1) == (len = convert_string_allocate_internal(ch, CH_UCS2, src, inlen, &buffer)) )
661 if ( (size_t)-1 == (ilen = decompose_w((ucs2_t *)buffer, len, (ucs2_t *)u, &ilen)) ) {
666 if ((size_t)(-1) == (len = convert_string_internal( CH_UCS2, ch, u, ilen, dst, outlen)) ) {
676 size_t utf8_precompose ( char * src, size_t inlen, char * dst, size_t outlen)
678 return charset_precompose ( CH_UTF8, src, inlen, dst, outlen);
681 size_t utf8_decompose ( char * src, size_t inlen, char * dst, size_t outlen)
683 return charset_decompose ( CH_UTF8, src, inlen, dst, outlen);
686 static char debugbuf[ MAXPATHLEN +1 ];
687 char * debug_out ( char * seq, size_t len)
693 p = (unsigned char*) seq;
696 for ( i = 0; i<=(len-1); i++)
698 sprintf(q, "%2.2x.", *p);
708 * Convert from MB to UCS2 charset
710 * CONV_UNESCAPEHEX: ':XX' will be converted to an UCS2 character
711 * CONV_IGNORE: return the first convertable characters.
713 * This will *not* work if the destination charset is not multibyte, i.e. UCS2->UCS2 will fail
714 * The (un)escape scheme is not compatible to the old cap style escape. This is bad, we need it
715 * for e.g. HFS cdroms.
718 static size_t pull_charset_flags (charset_t from_set, charset_t cap_charset, char* src, size_t srclen, char* dest, size_t destlen, u_int16_t *flags)
720 size_t i_len, o_len, hlen;
721 size_t retval, j = 0;
722 const char* inbuf = (const char*)src;
723 char* outbuf = (char*)dest;
724 atalk_iconv_t descriptor;
725 atalk_iconv_t descriptor_cap;
730 if (srclen == (size_t)-1)
731 srclen = strlen(src)+1;
733 lazy_initialize_conv();
735 descriptor = conv_handles[from_set][CH_UCS2];
736 descriptor_cap = conv_handles[cap_charset][CH_UCS2];
738 if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
747 if ( flags && (*flags & CONV_UNESCAPEHEX)) {
748 if ( NULL != (s = strchr ( inbuf, ':'))) {
749 j = i_len - (s - inbuf);
750 if ( 0 == (i_len = (s - inbuf)))
755 retval = atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len);
756 if(retval==(size_t)-1) {
757 if (errno == EILSEQ && flags && (*flags & CONV_IGNORE)) {
758 *flags |= CONV_REQMANGLE;
759 return destlen-o_len;
766 if (j && flags && (*flags & CONV_UNESCAPEHEX )) {
767 /* we're at the start on an hex encoded ucs2 char */
773 isxdigit( *(inbuf+1)) && isxdigit( *(inbuf+2)) ) {
775 while ( *inbuf == ':' && j >=3 &&
776 isxdigit( *(inbuf+1)) && isxdigit( *(inbuf+2)) ) {
778 h[hlen] = hextoint( *inbuf ) << 4;
780 h[hlen++] |= hextoint( *inbuf );
784 h_buf = (const char*) h;
785 if ((size_t) -1 == (retval = atalk_iconv(descriptor_cap, &h_buf, &hlen, &outbuf, &o_len)) ) {
786 if (errno == EILSEQ && CHECK_FLAGS(flags, CONV_IGNORE)) {
787 *flags |= CONV_REQMANGLE;
788 return destlen-o_len;
796 /* We have an invalid :xx sequence */
797 if (CHECK_FLAGS(flags, CONV_IGNORE)) {
798 *flags |= CONV_REQMANGLE;
799 return destlen-o_len;
809 goto conversion_loop;
814 return destlen-o_len;
818 * Convert from UCS2 to MB charset
820 * CONV_ESCAPEDOTS: escape leading dots
821 * CONV_ESCAPEHEX: unconvertable characters and '/' will be escaped to :XX
822 * CONV_IGNORE: unconvertable characters will be replaced with '_'
824 * CONV_IGNORE and CONV_ESCAPEHEX can't work together. Should we check this ?
825 * This will *not* work if the destination charset is not multibyte, i.e. UCS2->UCS2 will fail
826 * The escape scheme is not compatible to the old cap style escape. This is bad, we need it
827 * for e.g. HFS cdroms.
831 static size_t push_charset_flags (charset_t to_set, charset_t cap_set, char* src, size_t srclen, char* dest, size_t destlen, u_int16_t *flags)
833 size_t i_len, o_len, i;
834 size_t retval, j = 0;
835 const char* inbuf = (const char*)src;
836 char* outbuf = (char*)dest;
837 atalk_iconv_t descriptor;
839 char *buf, *buf_save;
842 lazy_initialize_conv();
844 descriptor = conv_handles[CH_UCS2][to_set];
846 if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
854 if ( SVAL(inbuf,0) == 0x002e && flags && (*flags & CONV_ESCAPEDOTS)) { /* 0x002e = . */
866 if (flags) *flags |= CONV_REQESCAPE;
870 if ( flags && (*flags & CONV_ESCAPEHEX)) {
871 for ( i = 0; i < i_len; i+=2) {
872 if ( SVAL((inbuf+i),0) == 0x002f) { /* 0x002f = / */
874 if ( 0 == ( i_len = i))
877 } else if ( SVAL(inbuf+i,0) == 0x003a) { /* 0x003a = : */
884 retval = atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len);
885 if (retval==(size_t)-1) {
886 if (errno == EILSEQ && CHECK_FLAGS(flags, CONV_IGNORE)) {
887 *flags |= CONV_REQMANGLE;
888 return destlen -o_len;
890 else if ( errno == EILSEQ && flags && (*flags & CONV_ESCAPEHEX)) {
895 if ((size_t) -1 == (buflen = convert_string_allocate_internal(CH_UCS2, cap_set, inbuf, 2, &buf)) )
904 *outbuf++ = hexdig[ ( *buf & 0xf0 ) >> 4 ];
905 *outbuf++ = hexdig[ *buf & 0x0f ];
914 if (flags) *flags |= CONV_REQESCAPE;
916 goto conversion_loop;
923 if (j && flags && (*flags & CONV_ESCAPEHEX)) {
928 o_save[destlen -o_len] = ':';
929 o_save[destlen -o_len+1] = '2';
930 o_save[destlen -o_len+2] = 'f';
937 goto conversion_loop;
939 return destlen -o_len;
942 size_t convert_charset ( charset_t from_set, charset_t to_set, charset_t cap_charset, char* src, size_t src_len, char* dest, size_t dest_len, u_int16_t *flags)
946 char buffer[MAXPATHLEN];
947 char buffer2[MAXPATHLEN];
950 lazy_initialize_conv();
952 /* convert from_set to UCS2 */
953 if ((size_t)(-1) == ( o_len = pull_charset_flags( from_set, cap_charset, src, src_len, buffer, MAXPATHLEN, flags)) ) {
954 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from_set));
961 /* Do pre/decomposition */
962 if (CHECK_FLAGS(flags, CONV_PRECOMPOSE) ||
963 ((!(charsets[to_set]) || !(charsets[to_set]->flags & CHARSET_DECOMPOSED)) &&
964 (!(charsets[from_set]) || (charsets[from_set]->flags & CHARSET_DECOMPOSED))))
966 if (CHECK_FLAGS(flags, CONV_DECOMPOSE) || (charsets[to_set] && charsets[to_set]->flags & CHARSET_DECOMPOSED) )
972 switch (composition) {
978 if ( (size_t)-1 == (i_len = precompose_w((ucs2_t *)buffer, o_len, (ucs2_t *)u, &i_len)) )
982 if ( (size_t)-1 == (i_len = decompose_w((ucs2_t *)buffer, o_len, (ucs2_t *)u, &i_len)) )
987 /* Do case conversions */
988 if (CHECK_FLAGS(flags, CONV_TOUPPER)) {
989 if (!strupper_w((ucs2_t *) u))
992 if (CHECK_FLAGS(flags, CONV_TOLOWER)) {
993 if (!strlower_w((ucs2_t *) u))
997 /* Convert UCS2 to to_set */
998 if ((size_t)(-1) == ( o_len = push_charset_flags( to_set, cap_charset, u, i_len, dest, dest_len, flags )) ) {
999 LOG(log_error, logtype_default,
1000 "Conversion failed (CH_UCS2 to %s):%s", charset_name(to_set), strerror(errno));