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
57 * @brief Character-set conversion routines built on our iconv.
59 * @note Samba's internal character set (at least in the 3.0 series)
60 * is always the same as the one for the Unix filesystem. It is
61 * <b>not</b> necessarily UTF-8 and may be different on machines that
62 * need i18n filenames to be compatible with Unix software. It does
63 * have to be a superset of ASCII. All multibyte sequences must start
64 * with a byte with the high bit set.
70 #define MAX_CHARSETS 10
72 #define CHECK_FLAGS(a,b) (((a)!=NULL) ? (*(a) & (b)) : 0 )
74 static atalk_iconv_t conv_handles[MAX_CHARSETS][MAX_CHARSETS];
75 static char* charset_names[MAX_CHARSETS];
76 static struct charset_functions* charsets[MAX_CHARSETS];
77 static char hexdig[] = "0123456789abcdef";
78 #define hextoint( c ) ( isdigit( c ) ? c - '0' : c + 10 - 'a' )
81 * Return the name of a charset to give to iconv().
83 static const char *charset_name(charset_t ch)
85 const char *ret = NULL;
87 if (ch == CH_UCS2) ret = "UCS-2LE";
88 else if (ch == CH_UNIX) ret = "LOCALE"; /*lp_unix_charset();*/
89 else if (ch == CH_MAC) ret = "MAC_ROMAN"; /*lp_display_charset();*/
90 else if (ch == CH_UTF8) ret = "UTF8";
91 else if (ch == CH_UTF8_MAC) ret = "UTF8-MAC";
94 ret = charset_names[ch];
96 #if defined(HAVE_NL_LANGINFO) && defined(CODESET)
97 if (ret && strcasecmp(ret, "LOCALE") == 0) {
98 const char *ln = NULL;
100 #ifdef HAVE_SETLOCALE
101 setlocale(LC_ALL, "");
103 ln = nl_langinfo(CODESET);
105 /* Check whether the charset name is supported
107 atalk_iconv_t handle = atalk_iconv_open(ln,"UCS-2LE");
108 if (handle == (atalk_iconv_t) -1) {
109 LOG(log_debug, logtype_default, "Locale charset '%s' unsupported, using ASCII instead", ln);
112 atalk_iconv_close(handle);
119 if (!ret || !*ret) ret = "ASCII";
123 struct charset_functions* get_charset_functions (charset_t ch)
125 if (charsets[ch] != NULL)
128 charsets[ch] = find_charset_functions(charset_name(ch));
134 void lazy_initialize_conv(void)
136 static int initialized = 0;
144 charset_t add_charset(char* name)
146 static charset_t max_charset_t = NUM_CHARSETS-1;
147 charset_t cur_charset_t = max_charset_t+1;
150 lazy_initialize_conv();
152 for (c1=0; c1<=max_charset_t;c1++) {
153 if ( strcasecmp(name, charset_name(c1)) == 0)
157 if ( cur_charset_t >= MAX_CHARSETS ) {
158 LOG (log_debug, logtype_default, "Adding charset %s failed, too many charsets (max. %u allowed)",
160 return (charset_t) -1;
163 /* First try to setup the required conversions */
165 conv_handles[cur_charset_t][CH_UCS2] = atalk_iconv_open( charset_name(CH_UCS2), name);
166 if (conv_handles[cur_charset_t][CH_UCS2] == (atalk_iconv_t)-1) {
167 LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
168 name, charset_name(CH_UCS2));
169 conv_handles[cur_charset_t][CH_UCS2] = NULL;
170 return (charset_t) -1;
173 conv_handles[CH_UCS2][cur_charset_t] = atalk_iconv_open( name, charset_name(CH_UCS2));
174 if (conv_handles[CH_UCS2][cur_charset_t] == (atalk_iconv_t)-1) {
175 LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
176 charset_name(CH_UCS2), name);
177 conv_handles[CH_UCS2][cur_charset_t] = NULL;
178 return (charset_t) -1;
181 /* register the new charset_t name */
182 charset_names[cur_charset_t] = strdup(name);
184 charsets[cur_charset_t] = get_charset_functions (cur_charset_t);
188 LOG(log_debug, logtype_default, "Added charset %s with handle %u", name, cur_charset_t);
190 return (cur_charset_t);
194 * Initialize iconv conversion descriptors.
196 * This is called the first time it is needed, and also called again
197 * every time the configuration is reloaded, because the charset or
198 * codepage might have changed.
200 void init_iconv(void)
204 /* so that charset_name() works we need to get the UNIX<->UCS2 going
206 if (!conv_handles[CH_UNIX][CH_UCS2])
207 conv_handles[CH_UNIX][CH_UCS2] = atalk_iconv_open("UCS-2LE", "ASCII");
209 if (!conv_handles[CH_UCS2][CH_UNIX])
210 conv_handles[CH_UCS2][CH_UNIX] = atalk_iconv_open("ASCII", "UCS-2LE");
212 for (c1=0;c1<NUM_CHARSETS;c1++) {
213 const char *name = charset_name((charset_t)c1);
215 conv_handles[c1][CH_UCS2] = atalk_iconv_open( charset_name(CH_UCS2), name);
216 if (conv_handles[c1][CH_UCS2] == (atalk_iconv_t)-1) {
217 LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
218 name, charset_name(CH_UCS2));
219 conv_handles[c1][CH_UCS2] = NULL;
222 conv_handles[CH_UCS2][c1] = atalk_iconv_open( name, charset_name(CH_UCS2));
223 if (conv_handles[CH_UCS2][1] == (atalk_iconv_t)-1) {
224 LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
225 charset_name(CH_UCS2), name);
226 conv_handles[c1][c1] = NULL;
229 charsets[c1] = get_charset_functions (c1);
234 * Convert string from one encoding to another, making error checking etc
236 * @param src pointer to source string (multibyte or singlebyte)
237 * @param srclen length of the source string in bytes
238 * @param dest pointer to destination string (multibyte or singlebyte)
239 * @param destlen maximal length allowed for string
240 * @returns the number of bytes occupied in the destination
242 static size_t convert_string_internal(charset_t from, charset_t to,
243 void const *src, size_t srclen,
244 void *dest, size_t destlen)
248 const char* inbuf = (const char*)src;
249 char* outbuf = (char*)dest;
250 atalk_iconv_t descriptor;
252 if (srclen == (size_t)-1)
253 srclen = strlen(src)+1;
255 lazy_initialize_conv();
257 descriptor = conv_handles[from][to];
259 if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
260 /* conversion not supported, use as is */
261 size_t len = MIN(srclen,destlen);
262 memcpy(dest,src,len);
268 retval = atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len);
269 if(retval==(size_t)-1) {
270 const char *reason="unknown error";
273 reason="Incomplete multibyte sequence";
276 reason="No more room";
277 LOG(log_debug, logtype_default, "convert_string: Required %d, available %d\n",
279 /* we are not sure we need srclen bytes,
280 may be more, may be less.
281 We only know we need more than destlen
285 reason="Illegal multibyte sequence";
289 /* smb_panic(reason); */
292 /* Terminate the string */
293 if (to == CH_UCS2 && destlen-o_len >= 2) {
297 else if ( destlen-o_len > 0)
300 return destlen-o_len;
304 size_t convert_string(charset_t from, charset_t to,
305 void const *src, size_t srclen,
306 void *dest, size_t destlen)
310 char buffer[MAXPATHLEN];
311 char buffer2[MAXPATHLEN];
314 lazy_initialize_conv();
316 /* convert from_set to UCS2 */
317 if ((size_t)(-1) == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen, buffer, MAXPATHLEN)) ) {
318 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from));
322 /* Do pre/decomposition */
323 if ( ((!(charsets[to]) || !(charsets[to]->flags & CHARSET_DECOMPOSED)) &&
324 (!(charsets[from]) || (charsets[from]->flags & CHARSET_DECOMPOSED))))
326 if ((charsets[to] && charsets[to]->flags & CHARSET_DECOMPOSED) )
332 switch (composition) {
338 if ( (size_t)-1 == (i_len = precompose_w((ucs2_t *)buffer, o_len, (ucs2_t *)u, &i_len)) )
342 if ( (size_t)-1 == (i_len = decompose_w((ucs2_t *)buffer, o_len, (ucs2_t *)u, &i_len)) )
347 /* Convert UCS2 to to_set */
348 if ((size_t)(-1) == ( o_len = convert_string_internal( CH_UCS2, to, u, i_len, dest, destlen)) ) {
349 LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
359 * Convert between character sets, allocating a new buffer for the result.
361 * @param srclen length of source buffer.
362 * @param dest always set at least to NULL
363 * @note -1 is not accepted for srclen.
365 * @returns Size in bytes of the converted string; or -1 in case of error.
368 static size_t convert_string_allocate_internal(charset_t from, charset_t to,
369 void const *src, size_t srclen, char **dest)
371 size_t i_len, o_len, destlen;
373 const char *inbuf = (const char *)src;
375 atalk_iconv_t descriptor;
379 if (src == NULL || srclen == (size_t)-1)
382 lazy_initialize_conv();
384 descriptor = conv_handles[from][to];
386 if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
387 /* conversion not supported, return -1*/
388 LOG(log_debug, logtype_default, "convert_string_allocate: conversion not supported!\n");
392 destlen = MAX(srclen, 512);
395 destlen = destlen * 2;
396 ob = (char *)realloc(outbuf, destlen);
398 LOG(log_debug, logtype_default,"convert_string_allocate: realloc failed!\n");
406 retval = atalk_iconv(descriptor,
409 if(retval == (size_t)-1) {
410 const char *reason="unknown error";
413 reason="Incomplete multibyte sequence";
418 reason="Illegal multibyte sequence";
421 LOG(log_debug, logtype_default,"Conversion error: %s(%s)\n",reason,inbuf);
422 /* smb_panic(reason); */
427 destlen = destlen - o_len;
429 /* Terminate the string */
430 if (to == CH_UCS2 && destlen-o_len >= 2) {
433 *dest = (char *)realloc(ob,destlen+2);
435 else if ( destlen-o_len > 0) {
437 *dest = (char *)realloc(ob,destlen+1);
440 //*dest = (char *)realloc(ob,destlen);
441 if (destlen && !*dest) {
442 LOG(log_debug, logtype_default, "convert_string_allocate: out of memory!\n");
451 size_t convert_string_allocate(charset_t from, charset_t to,
452 void const *src, size_t srclen,
457 char buffer[MAXPATHLEN];
458 char buffer2[MAXPATHLEN];
461 lazy_initialize_conv();
463 /* convert from_set to UCS2 */
464 if ((size_t)(-1) == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen, buffer, MAXPATHLEN)) ) {
465 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from));
469 /* Do pre/decomposition */
470 if ( ((!(charsets[to]) || !(charsets[to]->flags & CHARSET_DECOMPOSED)) &&
471 (!(charsets[from]) || (charsets[from]->flags & CHARSET_DECOMPOSED))))
473 if ((charsets[to] && charsets[to]->flags & CHARSET_DECOMPOSED) )
479 switch (composition) {
485 if ( (size_t)-1 == (i_len = precompose_w((ucs2_t *)buffer, o_len, (ucs2_t *)u, &i_len)) )
489 if ( (size_t)-1 == (i_len = decompose_w((ucs2_t *)buffer, o_len, (ucs2_t *)u, &i_len)) )
494 /* Convert UCS2 to to_set */
495 if ((size_t)(-1) == ( o_len = convert_string_allocate_internal( CH_UCS2, to, u, i_len, dest)) )
496 LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
502 size_t charset_strupper(charset_t ch, const char *src, size_t srclen, char *dest, size_t destlen)
507 size = convert_string_allocate_internal(ch, CH_UCS2, src, srclen,
509 if (size == (size_t)-1) {
513 if (!strupper_w((ucs2_t *)buffer) && (dest == src)) {
518 size = convert_string_internal(CH_UCS2, ch, buffer, size, dest, destlen);
523 size_t charset_strlower(charset_t ch, const char *src, size_t srclen, char *dest, size_t destlen)
528 size = convert_string_allocate_internal(ch, CH_UCS2, src, srclen,
530 if (size == (size_t)-1) {
534 if (!strlower_w((ucs2_t *)buffer) && (dest == src)) {
539 size = convert_string_internal(CH_UCS2, ch, buffer, size, dest, destlen);
545 size_t unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
547 return charset_strupper( CH_UNIX, src, srclen, dest, destlen);
550 size_t unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
552 return charset_strlower( CH_UNIX, src, srclen, dest, destlen);
555 size_t utf8_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
557 return charset_strupper( CH_UTF8, src, srclen, dest, destlen);
560 size_t utf8_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
562 return charset_strlower( CH_UTF8, src, srclen, dest, destlen);
566 * Copy a string from a charset_t char* src to a UCS2 destination, allocating a buffer
568 * @param dest always set at least to NULL
570 * @returns The number of bytes occupied by the string in the destination
571 * or -1 in case of error.
574 size_t charset_to_ucs2_allocate(charset_t ch, ucs2_t **dest, const char *src)
576 size_t src_len = strlen(src)+1;
579 return convert_string_allocate(ch, CH_UCS2, src, src_len, (char**) dest);
583 * Copy a string from a charset_t char* src to a UTF-8 destination, allocating a buffer
585 * @param dest always set at least to NULL
587 * @returns The number of bytes occupied by the string in the destination
590 size_t charset_to_utf8_allocate(charset_t ch, char **dest, const char *src)
592 size_t src_len = strlen(src)+1;
595 return convert_string_allocate(ch, CH_UTF8, src, src_len, dest);
599 * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer
601 * @param dest always set at least to NULL
603 * @returns The number of bytes occupied by the string in the destination
606 size_t ucs2_to_charset_allocate(charset_t ch, char **dest, const ucs2_t *src)
608 size_t src_len = (strlen_w(src)+1) * sizeof(ucs2_t);
610 return convert_string_allocate(CH_UCS2, ch, src, src_len, dest);
614 * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer
616 * @param dest always set at least to NULL
618 * @returns The number of bytes occupied by the string in the destination
621 size_t utf8_to_charset_allocate(charset_t ch, char **dest, const char *src)
623 size_t src_len = strlen(src);
625 return convert_string_allocate(CH_UTF8, ch, src, src_len, dest);
628 size_t charset_precompose ( charset_t ch, char * src, size_t inlen, char * dst, size_t outlen)
635 if ((size_t)(-1) == (len = convert_string_allocate_internal(ch, CH_UCS2, src, inlen, &buffer)) )
640 if ( (size_t)-1 == (ilen = precompose_w((ucs2_t *)buffer, len, (ucs2_t *)u, &ilen)) ) {
645 if ((size_t)(-1) == (len = convert_string_internal( CH_UCS2, ch, u, ilen, dst, outlen)) ) {
655 size_t charset_decompose ( charset_t ch, char * src, size_t inlen, char * dst, size_t outlen)
662 if ((size_t)(-1) == (len = convert_string_allocate_internal(ch, CH_UCS2, src, inlen, &buffer)) )
667 if ( (size_t)-1 == (ilen = decompose_w((ucs2_t *)buffer, len, (ucs2_t *)u, &ilen)) ) {
672 if ((size_t)(-1) == (len = convert_string_internal( CH_UCS2, ch, u, ilen, dst, outlen)) ) {
682 size_t utf8_precompose ( char * src, size_t inlen, char * dst, size_t outlen)
684 return charset_precompose ( CH_UTF8, src, inlen, dst, outlen);
687 size_t utf8_decompose ( char * src, size_t inlen, char * dst, size_t outlen)
689 return charset_decompose ( CH_UTF8, src, inlen, dst, outlen);
692 static char debugbuf[ MAXPATHLEN +1 ];
693 char * debug_out ( char * seq, size_t len)
699 p = (unsigned char*) seq;
702 for ( i = 0; i<=(len-1); i++)
704 sprintf(q, "%2.2x.", *p);
714 * Convert from MB to UCS2 charset
716 * CONV_UNESCAPEHEX: ':XXXX' will be converted to an UCS2 character
717 * CONV_IGNORE: unconvertable characters will be replaced with '_'
719 * This will *not* work if the destination charset is not multibyte, i.e. UCS2->UCS2 will fail
720 * The (un)escape scheme is not compatible to the old cap style escape. This is bad, we need it
721 * for e.g. HFS cdroms.
724 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)
726 size_t i_len, o_len, hlen;
727 size_t retval, j = 0;
728 const char* inbuf = (const char*)src;
729 char* outbuf = (char*)dest;
730 atalk_iconv_t descriptor;
731 atalk_iconv_t descriptor_cap;
736 if (srclen == (size_t)-1)
737 srclen = strlen(src)+1;
739 lazy_initialize_conv();
741 descriptor = conv_handles[from_set][CH_UCS2];
742 descriptor_cap = conv_handles[cap_charset][CH_UCS2];
744 if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
753 if ( flags && (*flags & CONV_UNESCAPEHEX)) {
754 if ( NULL != (s = strchr ( inbuf, ':'))) {
755 j = i_len - (s - inbuf);
756 if ( 0 == (i_len = (s - inbuf)))
761 retval = atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len);
762 if(retval==(size_t)-1) {
763 if (errno == EILSEQ && flags && (*flags & CONV_IGNORE)) {
768 o_save[destlen-o_len] = '_';
769 o_save[destlen-o_len+1] = 0x0;
771 outbuf = o_save + destlen - o_len;
774 *flags |= CONV_REQMANGLE;
775 goto conversion_loop;
782 if (j && flags && (*flags & CONV_UNESCAPEHEX )) {
783 /* we're at the start on an hex encoded ucs2 char */
789 isxdigit( *(inbuf+1)) && isxdigit( *(inbuf+2)) ) {
791 while ( *inbuf == ':' && j >=3 &&
792 isxdigit( *(inbuf+1)) && isxdigit( *(inbuf+2)) ) {
794 h[hlen] = hextoint( *inbuf ) << 4;
796 h[hlen++] |= hextoint( *inbuf );
800 h_buf = (const char*) h;
801 if ((size_t) -1 == (retval = atalk_iconv(descriptor_cap, &h_buf, &hlen, &outbuf, &o_len)) ) {
802 if (errno == EILSEQ && CHECK_FLAGS(flags, CONV_IGNORE)) {
812 *flags |= CONV_REQMANGLE;
821 /* We have an invalid :xx sequence */
822 if (CHECK_FLAGS(flags, CONV_IGNORE)) {
828 *flags |= CONV_REQMANGLE;
838 goto conversion_loop;
841 return destlen-o_len;
845 * Convert from UCS2 to MB charset
847 * CONV_ESCAPEDOTS: escape leading dots
848 * CONV_ESCAPEHEX: unconvertable characters and '/' will be escaped to :XXXX
849 * CONV_IGNORE: unconvertable characters will be replaced with '_'
851 * CONV_IGNORE and CONV_ESCAPEHEX can't work together. Should we check this ?
852 * This will *not* work if the destination charset is not multibyte, i.e. UCS2->UCS2 will fail
853 * The escape scheme is not compatible to the old cap style escape. This is bad, we need it
854 * for e.g. HFS cdroms.
858 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)
860 size_t i_len, o_len, i;
861 size_t retval, j = 0;
862 const char* inbuf = (const char*)src;
863 char* outbuf = (char*)dest;
864 atalk_iconv_t descriptor;
866 char *buf, *buf_save;
869 lazy_initialize_conv();
871 descriptor = conv_handles[CH_UCS2][to_set];
873 if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
881 if (*inbuf == '.' && flags && (*flags & CONV_ESCAPEDOTS)) {
893 if (flags) *flags |= CONV_REQESCAPE;
897 if ( flags && (*flags & CONV_ESCAPEHEX)) {
898 for ( i = 0; i < i_len; i+=2) {
899 if ( inbuf[i] == '/' ) {
901 if ( 0 == ( i_len = i))
908 retval = atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len);
909 if (retval==(size_t)-1) {
910 if (errno == EILSEQ && flags && (*flags & CONV_IGNORE)) {
915 o_save[destlen-o_len] = '_';
917 outbuf = o_save + destlen - o_len;
921 *flags |= CONV_REQMANGLE;
923 goto conversion_loop;
925 else if ( errno == EILSEQ && flags && (*flags & CONV_ESCAPEHEX)) {
930 if ((size_t) -1 == (buflen = convert_string_allocate_internal(CH_UCS2, cap_set, inbuf, 2, &buf)) )
939 *outbuf++ = hexdig[ ( *buf & 0xf0 ) >> 4 ];
940 *outbuf++ = hexdig[ *buf & 0x0f ];
948 if (flags) *flags |= CONV_REQESCAPE;
950 goto conversion_loop;
957 if (j && flags && (*flags & CONV_ESCAPEHEX)) {
962 o_save[destlen -o_len] = ':';
963 o_save[destlen -o_len+1] = '2';
964 o_save[destlen -o_len+2] = 'f';
971 goto conversion_loop;
973 return destlen -o_len;
976 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)
980 char buffer[MAXPATHLEN];
981 char buffer2[MAXPATHLEN];
984 lazy_initialize_conv();
986 /* convert from_set to UCS2 */
987 if ((size_t)(-1) == ( o_len = pull_charset_flags( from_set, cap_charset, src, src_len, buffer, MAXPATHLEN, flags)) ) {
988 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from_set));
992 /* Do pre/decomposition */
993 if (CHECK_FLAGS(flags, CONV_PRECOMPOSE) ||
994 ((!(charsets[to_set]) || !(charsets[to_set]->flags & CHARSET_DECOMPOSED)) &&
995 (!(charsets[from_set]) || (charsets[from_set]->flags & CHARSET_DECOMPOSED))))
997 if (CHECK_FLAGS(flags, CONV_DECOMPOSE) || (charsets[to_set] && charsets[to_set]->flags & CHARSET_DECOMPOSED) )
1003 switch (composition) {
1009 if ( (size_t)-1 == (i_len = precompose_w((ucs2_t *)buffer, o_len, (ucs2_t *)u, &i_len)) )
1010 return (size_t)(-1);
1013 if ( (size_t)-1 == (i_len = decompose_w((ucs2_t *)buffer, o_len, (ucs2_t *)u, &i_len)) )
1014 return (size_t)(-1);
1018 /* Do case conversions */
1019 if (CHECK_FLAGS(flags, CONV_TOUPPER)) {
1020 if (!strupper_w((ucs2_t *) u))
1021 return (size_t)(-1);
1023 if (CHECK_FLAGS(flags, CONV_TOLOWER)) {
1024 if (!strlower_w((ucs2_t *) u))
1025 return (size_t)(-1);
1028 /* Convert UCS2 to to_set */
1029 if ((size_t)(-1) == ( o_len = push_charset_flags( to_set, cap_charset, u, i_len, dest, dest_len, flags )) ) {
1030 LOG(log_error, logtype_default,
1031 "Conversion failed (CH_UCS2 to %s):%s", charset_name(to_set), strerror(errno));