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 */
35 #include <sys/param.h>
36 #ifdef HAVE_USABLE_ICONV
46 #include <netatalk/endian.h>
47 #include <atalk/logger.h>
48 #include <atalk/unicode.h>
49 #include <atalk/util.h>
50 #include "byteorder.h"
56 * @brief Character-set conversion routines built on our iconv.
58 * @note Samba's internal character set (at least in the 3.0 series)
59 * is always the same as the one for the Unix filesystem. It is
60 * <b>not</b> necessarily UTF-8 and may be different on machines that
61 * need i18n filenames to be compatible with Unix software. It does
62 * have to be a superset of ASCII. All multibyte sequences must start
63 * with a byte with the high bit set.
69 #define MAX_CHARSETS 20
71 #define CHECK_FLAGS(a,b) (((a)!=NULL) ? (*(a) & (b)) : 0 )
73 static atalk_iconv_t conv_handles[MAX_CHARSETS][MAX_CHARSETS];
74 static char* charset_names[MAX_CHARSETS];
75 static struct charset_functions* charsets[MAX_CHARSETS];
76 static char hexdig[] = "0123456789abcdef";
77 #define hextoint( c ) ( isdigit( c ) ? c - '0' : c + 10 - 'a' )
79 static char* read_charsets_from_env(charset_t ch)
85 if (( name = getenv( "ATALK_MAC_CHARSET" )) != NULL )
91 if (( name = getenv( "ATALK_UNIX_CHARSET" )) != NULL )
104 * Return the name of a charset to give to iconv().
106 static const char *charset_name(charset_t ch)
108 const char *ret = NULL;
109 static int first = 1;
110 static char macname[128];
111 static char unixname[128];
114 memset(macname, 0, sizeof(macname));
115 memset(unixname, 0, sizeof(unixname));
119 if (ch == CH_UCS2) ret = "UCS-2";
120 else if (ch == CH_UTF8) ret = "UTF8";
121 else if (ch == CH_UTF8_MAC) ret = "UTF8-MAC";
122 else if (ch == CH_UNIX) {
123 if (unixname[0] == '\0') {
124 ret = read_charsets_from_env(CH_UNIX);
125 strlcpy(unixname, ret, sizeof(unixname));
130 else if (ch == CH_MAC) {
131 if (macname[0] == '\0') {
132 ret = read_charsets_from_env(CH_MAC);
133 strlcpy(macname, ret, sizeof(macname));
140 ret = charset_names[ch];
142 #if defined(HAVE_NL_LANGINFO) && defined(CODESET)
143 if (ret && strcasecmp(ret, "LOCALE") == 0) {
144 const char *ln = NULL;
146 #ifdef HAVE_SETLOCALE
147 setlocale(LC_ALL, "");
149 ln = nl_langinfo(CODESET);
151 /* Check whether the charset name is supported
153 atalk_iconv_t handle = atalk_iconv_open(ln, "UCS-2");
154 if (handle == (atalk_iconv_t) -1) {
155 LOG(log_debug, logtype_default, "Locale charset '%s' unsupported, using ASCII instead", ln);
158 atalk_iconv_close(handle);
161 strlcpy(unixname, ln, sizeof(unixname));
165 #else /* system doesn't have LOCALE support */
166 if (ch == CH_UNIX) ret = NULL;
169 if (!ret || !*ret) ret = "ASCII";
173 static struct charset_functions* get_charset_functions (charset_t ch)
175 if (charsets[ch] != NULL)
178 charsets[ch] = find_charset_functions(charset_name(ch));
184 static void lazy_initialize_conv(void)
186 static int initialized = 0;
194 charset_t add_charset(char* name)
196 static charset_t max_charset_t = NUM_CHARSETS-1;
197 charset_t cur_charset_t = max_charset_t+1;
200 lazy_initialize_conv();
202 for (c1=0; c1<=max_charset_t;c1++) {
203 if ( strcasecmp(name, charset_name(c1)) == 0)
207 if ( cur_charset_t >= MAX_CHARSETS ) {
208 LOG (log_debug, logtype_default, "Adding charset %s failed, too many charsets (max. %u allowed)",
210 return (charset_t) -1;
213 /* First try to setup the required conversions */
215 conv_handles[cur_charset_t][CH_UCS2] = atalk_iconv_open( charset_name(CH_UCS2), name);
216 if (conv_handles[cur_charset_t][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[cur_charset_t][CH_UCS2] = NULL;
220 return (charset_t) -1;
223 conv_handles[CH_UCS2][cur_charset_t] = atalk_iconv_open( name, charset_name(CH_UCS2));
224 if (conv_handles[CH_UCS2][cur_charset_t] == (atalk_iconv_t)-1) {
225 LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
226 charset_name(CH_UCS2), name);
227 conv_handles[CH_UCS2][cur_charset_t] = NULL;
228 return (charset_t) -1;
231 /* register the new charset_t name */
232 charset_names[cur_charset_t] = strdup(name);
234 charsets[cur_charset_t] = get_charset_functions (cur_charset_t);
238 LOG(log_debug, logtype_default, "Added charset %s with handle %u", name, cur_charset_t);
240 return (cur_charset_t);
244 * Initialize iconv conversion descriptors.
246 * This is called the first time it is needed, and also called again
247 * every time the configuration is reloaded, because the charset or
248 * codepage might have changed.
250 void init_iconv(void)
254 for (c1=0;c1<NUM_CHARSETS;c1++) {
255 const char *name = charset_name((charset_t)c1);
257 conv_handles[c1][CH_UCS2] = atalk_iconv_open( charset_name(CH_UCS2), name);
258 if (conv_handles[c1][CH_UCS2] == (atalk_iconv_t)-1) {
259 LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
260 name, charset_name(CH_UCS2));
261 conv_handles[c1][CH_UCS2] = NULL;
264 if (c1 != CH_UCS2) { /* avoid lost memory, make valgrind happy */
265 conv_handles[CH_UCS2][c1] = atalk_iconv_open( name, charset_name(CH_UCS2));
266 if (conv_handles[CH_UCS2][c1] == (atalk_iconv_t)-1) {
267 LOG(log_error, logtype_default, "Required conversion from %s to %s not supported",
268 charset_name(CH_UCS2), name);
269 conv_handles[CH_UCS2][c1] = NULL;
273 charsets[c1] = get_charset_functions (c1);
280 static size_t add_null(charset_t to, char *buf, size_t bytesleft, size_t len)
282 /* Terminate the string */
283 if (to == CH_UCS2 && bytesleft >= 2) {
288 else if ( to != CH_UCS2 && bytesleft > 0 )
300 * Convert string from one encoding to another, making error checking etc
302 * @param src pointer to source string (multibyte or singlebyte)
303 * @param srclen length of the source string in bytes
304 * @param dest pointer to destination string (multibyte or singlebyte)
305 * @param destlen maximal length allowed for string
306 * @returns the number of bytes occupied in the destination
308 static size_t convert_string_internal(charset_t from, charset_t to,
309 void const *src, size_t srclen,
310 void *dest, size_t destlen)
314 const char* inbuf = (const char*)src;
315 char* outbuf = (char*)dest;
316 char* o_save = outbuf;
317 atalk_iconv_t descriptor;
319 /* Fixed based on Samba 3.0.6 */
320 if (srclen == (size_t)-1) {
321 if (from == CH_UCS2) {
322 srclen = (strlen_w((const ucs2_t *)src)+1) * 2;
324 srclen = strlen((const char *)src)+1;
329 lazy_initialize_conv();
331 descriptor = conv_handles[from][to];
333 if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
339 retval = atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len);
340 if(retval==(size_t)-1) {
341 const char *reason="unknown error";
344 reason="Incomplete multibyte sequence";
347 reason="No more room";
350 reason="Illegal multibyte sequence";
353 LOG(log_debug, logtype_default,"Conversion error: %s",reason);
357 /* Terminate the string */
358 return add_null( to, o_save, o_len, destlen -o_len);
362 size_t convert_string(charset_t from, charset_t to,
363 void const *src, size_t srclen,
364 void *dest, size_t destlen)
368 ucs2_t buffer[MAXPATHLEN];
369 ucs2_t buffer2[MAXPATHLEN];
372 lazy_initialize_conv();
374 /* convert from_set to UCS2 */
375 if ((size_t)(-1) == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen,
376 (char*) buffer, sizeof(buffer))) ) {
377 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from));
381 /* Do pre/decomposition */
382 if ( ((!(charsets[to]) || !(charsets[to]->flags & CHARSET_DECOMPOSED)) &&
383 (!(charsets[from]) || (charsets[from]->flags & CHARSET_DECOMPOSED))))
385 if ((charsets[to] && charsets[to]->flags & CHARSET_DECOMPOSED) )
388 i_len = sizeof(buffer2);
391 switch (composition) {
397 if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
401 if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
406 /* Convert UCS2 to to_set */
407 if ((size_t)(-1) == ( o_len = convert_string_internal( CH_UCS2, to, (char*) u, i_len, dest, destlen)) ) {
408 LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
418 * Convert between character sets, allocating a new buffer for the result.
420 * @param srclen length of source buffer.
421 * @param dest always set at least to NULL
422 * @note -1 is not accepted for srclen.
424 * @returns Size in bytes of the converted string; or -1 in case of error.
427 static size_t convert_string_allocate_internal(charset_t from, charset_t to,
428 void const *src, size_t srclen, char **dest)
430 size_t i_len, o_len, destlen;
432 const char *inbuf = (const char *)src;
433 char *outbuf = NULL, *ob = NULL;
434 atalk_iconv_t descriptor;
438 if (src == NULL || srclen == (size_t)-1)
441 lazy_initialize_conv();
443 descriptor = conv_handles[from][to];
445 if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
446 /* conversion not supported, return -1*/
447 LOG(log_debug, logtype_default, "convert_string_allocate: conversion not supported!");
451 destlen = MAX(srclen, 512);
453 destlen = destlen * 2;
454 outbuf = (char *)realloc(ob, destlen);
456 LOG(log_debug, logtype_default,"convert_string_allocate: realloc failed!");
462 inbuf = src; /* this restarts the whole conversion if buffer needed to be increased */
465 retval = atalk_iconv(descriptor,
468 if(retval == (size_t)-1) {
469 const char *reason="unknown error";
472 reason="Incomplete multibyte sequence";
477 reason="Illegal multibyte sequence";
480 LOG(log_debug, logtype_default,"Conversion error: %s(%s)",reason,inbuf);
486 destlen = destlen - o_len;
488 /* Terminate the string */
489 if (to == CH_UCS2 && o_len >= 2) {
492 *dest = (char *)realloc(ob,destlen+2);
494 else if ( to != CH_UCS2 && o_len > 0 ) {
496 *dest = (char *)realloc(ob,destlen+1);
499 goto convert; /* realloc */
502 if (destlen && !*dest) {
503 LOG(log_debug, logtype_default, "convert_string_allocate: out of memory!");
512 size_t convert_string_allocate(charset_t from, charset_t to,
513 void const *src, size_t srclen,
518 ucs2_t buffer[MAXPATHLEN];
519 ucs2_t buffer2[MAXPATHLEN];
522 lazy_initialize_conv();
526 /* convert from_set to UCS2 */
527 if ((size_t)(-1) == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen,
528 buffer, sizeof(buffer))) ) {
529 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from));
533 /* Do pre/decomposition */
534 if ( ((!(charsets[to]) || !(charsets[to]->flags & CHARSET_DECOMPOSED)) &&
535 (!(charsets[from]) || (charsets[from]->flags & CHARSET_DECOMPOSED))))
537 if ((charsets[to] && charsets[to]->flags & CHARSET_DECOMPOSED) )
540 i_len = sizeof(buffer2);
543 switch (composition) {
549 if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
553 if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
558 /* Convert UCS2 to to_set */
559 if ((size_t)(-1) == ( o_len = convert_string_allocate_internal( CH_UCS2, to, (char*)u, i_len, dest)) )
560 LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
566 size_t charset_strupper(charset_t ch, const char *src, size_t srclen, char *dest, size_t destlen)
571 size = convert_string_allocate_internal(ch, CH_UCS2, src, srclen,
573 if (size == (size_t)-1) {
577 if (!strupper_w((ucs2_t *)buffer) && (dest == src)) {
582 size = convert_string_internal(CH_UCS2, ch, buffer, size, dest, destlen);
587 size_t charset_strlower(charset_t ch, const char *src, size_t srclen, char *dest, size_t destlen)
592 size = convert_string_allocate_internal(ch, CH_UCS2, src, srclen,
594 if (size == (size_t)-1) {
598 if (!strlower_w((ucs2_t *)buffer) && (dest == src)) {
603 size = convert_string_internal(CH_UCS2, ch, buffer, size, dest, destlen);
609 size_t unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
611 return charset_strupper( CH_UNIX, src, srclen, dest, destlen);
614 size_t unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
616 return charset_strlower( CH_UNIX, src, srclen, dest, destlen);
619 size_t utf8_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
621 return charset_strupper( CH_UTF8, src, srclen, dest, destlen);
624 size_t utf8_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
626 return charset_strlower( CH_UTF8, src, srclen, dest, destlen);
630 * Copy a string from a charset_t char* src to a UCS2 destination, allocating a buffer
632 * @param dest always set at least to NULL
634 * @returns The number of bytes occupied by the string in the destination
635 * or -1 in case of error.
638 size_t charset_to_ucs2_allocate(charset_t ch, ucs2_t **dest, const char *src)
640 size_t src_len = strlen(src);
643 return convert_string_allocate(ch, CH_UCS2, src, src_len, (char**) dest);
646 /** -----------------------------------
647 * Copy a string from a charset_t char* src to a UTF-8 destination, allocating a buffer
649 * @param dest always set at least to NULL
651 * @returns The number of bytes occupied by the string in the destination
654 size_t charset_to_utf8_allocate(charset_t ch, char **dest, const char *src)
656 size_t src_len = strlen(src);
659 return convert_string_allocate(ch, CH_UTF8, src, src_len, dest);
662 /** -----------------------------------
663 * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer
665 * @param dest always set at least to NULL
667 * @returns The number of bytes occupied by the string in the destination
670 size_t ucs2_to_charset(charset_t ch, const ucs2_t *src, char *dest, size_t destlen)
672 size_t src_len = (strlen_w(src)) * sizeof(ucs2_t);
673 return convert_string(CH_UCS2, ch, src, src_len, dest, destlen);
676 /* --------------------------------- */
677 size_t ucs2_to_charset_allocate(charset_t ch, char **dest, const ucs2_t *src)
679 size_t src_len = (strlen_w(src)) * sizeof(ucs2_t);
681 return convert_string_allocate(CH_UCS2, ch, src, src_len, dest);
684 /** ---------------------------------
685 * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer
687 * @param dest always set at least to NULL
689 * @returns The number of bytes occupied by the string in the destination
692 size_t utf8_to_charset_allocate(charset_t ch, char **dest, const char *src)
694 size_t src_len = strlen(src);
696 return convert_string_allocate(CH_UTF8, ch, src, src_len, dest);
699 size_t charset_precompose ( charset_t ch, char * src, size_t inlen, char * dst, size_t outlen)
702 ucs2_t u[MAXPATHLEN];
706 if ((size_t)(-1) == (len = convert_string_allocate_internal(ch, CH_UCS2, src, inlen, &buffer)) )
711 if ( (size_t)-1 == (ilen = precompose_w((ucs2_t *)buffer, len, u, &ilen)) ) {
716 if ((size_t)(-1) == (len = convert_string_internal( CH_UCS2, ch, (char*)u, ilen, dst, outlen)) ) {
725 size_t charset_decompose ( charset_t ch, char * src, size_t inlen, char * dst, size_t outlen)
728 ucs2_t u[MAXPATHLEN];
732 if ((size_t)(-1) == (len = convert_string_allocate_internal(ch, CH_UCS2, src, inlen, &buffer)) )
737 if ( (size_t)-1 == (ilen = decompose_w((ucs2_t *)buffer, len, u, &ilen)) ) {
742 if ((size_t)(-1) == (len = convert_string_internal( CH_UCS2, ch, (char*)u, ilen, dst, outlen)) ) {
751 size_t utf8_precompose ( char * src, size_t inlen, char * dst, size_t outlen)
753 return charset_precompose ( CH_UTF8, src, inlen, dst, outlen);
756 size_t utf8_decompose ( char * src, size_t inlen, char * dst, size_t outlen)
758 return charset_decompose ( CH_UTF8, src, inlen, dst, outlen);
762 static char debugbuf[ MAXPATHLEN +1 ];
763 char * debug_out ( char * seq, size_t len)
769 p = (unsigned char*) seq;
772 for ( i = 0; i<=(len-1); i++)
774 sprintf(q, "%2.2x.", *p);
785 * Convert from MB to UCS2 charset
787 * CONV_UNESCAPEHEX: ':XX' will be converted to an UCS2 character
788 * CONV_IGNORE: return the first convertable characters.
789 * CONV_FORCE: force convertion
791 * This will *not* work if the destination charset is not multibyte, i.e. UCS2->UCS2 will fail
792 * The (un)escape scheme is not compatible to the old cap style escape. This is bad, we need it
793 * for e.g. HFS cdroms.
796 static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, char* src, size_t srclen, char* dest, size_t destlen, u_int16_t *flags)
798 const u_int16_t option = (flags ? *flags : 0);
801 const char* inbuf = (const char*)src;
803 atalk_iconv_t descriptor;
804 atalk_iconv_t descriptor_cap;
806 if (srclen == (size_t)-1)
807 srclen = strlen(src) + 1;
809 lazy_initialize_conv();
811 descriptor = conv_handles[from_set][CH_UCS2];
812 descriptor_cap = conv_handles[cap_set][CH_UCS2];
814 if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
823 if ((option & CONV_UNESCAPEHEX)) {
824 for (j = 0; j < i_len; ++j) {
825 if (inbuf[j] == ':') break;
832 atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len) == (size_t)-1) {
833 if (errno == EILSEQ || errno == EINVAL) {
835 if ((option & CONV_IGNORE)) {
836 *flags |= CONV_REQMANGLE;
837 return destlen - o_len;
839 if ((option & CONV__EILSEQ)) {
844 *((ucs2_t *)outbuf) = (ucs2_t) IGNORE_CHAR; /**inbuf */
849 /* FIXME reset stat ? */
857 /* we're at the start on an hex encoded ucs2 char */
862 while (i_len >= 3 && inbuf[0] == ':' &&
863 isxdigit(inbuf[1]) && isxdigit(inbuf[2])) {
864 h[hlen++] = (hextoint(inbuf[1]) << 4) | hextoint(inbuf[2]);
869 const char *h_buf = h;
870 if (atalk_iconv(descriptor_cap, &h_buf, &hlen, &outbuf, &o_len) == (size_t)-1) {
873 if (errno == EILSEQ && (option & CONV_IGNORE)) {
874 *flags |= CONV_REQMANGLE;
875 return destlen - o_len;
880 /* We have an invalid :xx sequence */
882 if ((option & CONV_IGNORE)) {
883 *flags |= CONV_REQMANGLE;
884 return destlen - o_len;
891 return (i_len + j == 0 || (option & CONV_FORCE)) ? destlen - o_len : (size_t)-1;
895 * Convert from UCS2 to MB charset
897 * CONV_ESCAPEDOTS: escape leading dots
898 * CONV_ESCAPEHEX: unconvertable characters and '/' will be escaped to :XX
899 * CONV_IGNORE: return the first convertable characters.
900 * CONV__EILSEQ: unconvertable characters will be replaced with '_'
901 * CONV_FORCE: force convertion
903 * CONV_IGNORE and CONV_ESCAPEHEX can't work together. Should we check this ?
904 * This will *not* work if the destination charset is not multibyte, i.e. UCS2->UCS2 will fail
905 * The escape scheme is not compatible to the old cap style escape. This is bad, we need it
906 * for e.g. HFS cdroms.
910 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)
912 const u_int16_t option = (flags ? *flags : 0);
913 size_t i_len, o_len, i;
915 const char* inbuf = (const char*)src;
916 char* outbuf = (char*)dest;
917 atalk_iconv_t descriptor;
918 atalk_iconv_t descriptor_cap;
920 lazy_initialize_conv();
922 descriptor = conv_handles[CH_UCS2][to_set];
923 descriptor_cap = conv_handles[CH_UCS2][cap_set];
925 if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
933 if ((option & CONV_ESCAPEDOTS) &&
934 i_len >= 2 && SVAL(inbuf, 0) == 0x002e) { /* 0x002e = . */
945 *flags |= CONV_REQESCAPE;
949 if ((option & CONV_ESCAPEHEX)) {
950 for (i = 0; i < i_len; i += 2) {
951 ucs2_t c = SVAL(inbuf, i);
952 if (c == 0x002f) { /* 0x002f = / */
956 } else if (c == 0x003a) { /* 0x003a = : */
963 atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len) == (size_t)-1) {
964 if (errno == EILSEQ) {
965 if ((option & CONV_IGNORE)) {
966 *flags |= CONV_REQMANGLE;
967 return destlen - o_len;
969 if ((option & CONV_ESCAPEHEX)) {
970 const size_t bufsiz = o_len / 3 + 1;
971 char *buf = malloc(bufsiz);
977 for (buflen = 1; buflen <= bufsiz; ++buflen) {
980 if (atalk_iconv(descriptor_cap, &inbuf, &i, &b, &o) != (size_t)-1) {
983 } else if (errno != E2BIG) {
986 } else if (o < buflen) {
991 if (o_len < buflen * 3) {
998 for (i = 0; i < buflen; ++i) {
1000 *outbuf++ = hexdig[(buf[i] >> 4) & 0x0f];
1001 *outbuf++ = hexdig[buf[i] & 0x0f];
1004 *flags |= CONV_REQESCAPE;
1025 if (i_len > 0) errno = EINVAL;
1027 return (i_len + j == 0 || (option & CONV_FORCE)) ? destlen - o_len : (size_t)-1;
1031 * FIXME the size is a mess we really need a malloc/free logic
1032 *`dest size must be dest_len +2
1034 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)
1036 size_t i_len, o_len;
1038 ucs2_t buffer[MAXPATHLEN +2];
1039 ucs2_t buffer2[MAXPATHLEN +2];
1040 int composition = 0;
1042 lazy_initialize_conv();
1044 /* convert from_set to UCS2 */
1045 if ((size_t)(-1) == ( o_len = pull_charset_flags( from_set, cap_charset, src, src_len,
1046 (char *) buffer, sizeof(buffer) -2, flags)) ) {
1047 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from_set));
1054 /* Do pre/decomposition */
1055 if (CHECK_FLAGS(flags, CONV_PRECOMPOSE) ||
1056 ((!(charsets[to_set]) || !(charsets[to_set]->flags & CHARSET_DECOMPOSED)) &&
1057 (!(charsets[from_set]) || (charsets[from_set]->flags & CHARSET_DECOMPOSED))))
1059 if (CHECK_FLAGS(flags, CONV_DECOMPOSE) || (charsets[to_set] && charsets[to_set]->flags & CHARSET_DECOMPOSED) )
1062 i_len = sizeof(buffer2) -2;
1065 switch (composition) {
1071 if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
1072 return (size_t)(-1);
1075 if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
1076 return (size_t)(-1);
1079 /* null terminate */
1083 /* Do case conversions */
1084 if (CHECK_FLAGS(flags, CONV_TOUPPER)) {
1087 if (CHECK_FLAGS(flags, CONV_TOLOWER)) {
1091 /* Convert UCS2 to to_set */
1092 if ((size_t)(-1) == ( o_len = push_charset_flags( to_set, cap_charset, (char *)u, i_len, dest, dest_len, flags )) ) {
1093 LOG(log_error, logtype_default,
1094 "Conversion failed (CH_UCS2 to %s):%s", charset_name(to_set), strerror(errno));
1097 /* null terminate */