]> arthur.barton.de Git - netatalk.git/blob - libatalk/unicode/charcnv.c
- merge branch-netatalk-afp-3x-dev, HEAD was tagged before
[netatalk.git] / libatalk / unicode / charcnv.c
1 /* 
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
8    
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.
13    
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.
18    
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.
22
23 */
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif /* HAVE_CONFIG_H */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <sys/stat.h>
35 #include <sys/param.h>
36 #ifdef HAVE_USABLE_ICONV
37 #include <iconv.h>
38 #endif
39 #if HAVE_LOCALE_H
40 #include <locale.h>
41 #endif
42 #if HAVE_LANGINFO_H
43 #include <langinfo.h>
44 #endif
45
46 #include <netatalk/endian.h>
47 #include <atalk/logger.h>
48 #include <atalk/unicode.h>
49 #include <atalk/util.h>
50 #include "byteorder.h"
51
52
53 /**
54  * @file
55  *
56  * @brief Character-set conversion routines built on our iconv.
57  * 
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.
64  *
65  * @sa lib/iconv.c
66  */
67
68
69 #define MAX_CHARSETS 10
70
71 #define CHECK_FLAGS(a,b) (((a)!=NULL) ? (*(a) & (b)) : 0 )
72
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' )
78
79 static char* read_charsets_from_env(charset_t ch) 
80 {
81         char *name;
82
83         switch (ch) {
84             case CH_MAC:
85                 if (( name = getenv( "ATALK_MAC_CHARSET" )) != NULL ) 
86                     return name;
87                 else
88                     return "MAC_ROMAN";
89                 break;
90             case CH_UNIX:
91                 if (( name = getenv( "ATALK_UNIX_CHARSET" )) != NULL ) 
92                     return name;
93                 else
94                     return "LOCALE";
95                 break;
96             default:
97                 break;
98         }
99         return "ASCII";
100
101            
102
103 /**
104  * Return the name of a charset to give to iconv().
105  **/
106 static const char *charset_name(charset_t ch)
107 {
108         const char *ret = NULL;
109         static int first = 1;
110         static char macname[128];
111         static char unixname[128];
112
113         if (first) {
114                 memset(macname, 0, sizeof(macname));
115                 memset(unixname, 0, sizeof(unixname));
116                 first = 0;
117         }
118
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));
126                 }
127                 else
128                         ret = unixname;
129         }
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));
134                 }
135                 else
136                         ret = macname;
137         }
138
139         if (!ret)
140                 ret = charset_names[ch];
141
142 #if defined(HAVE_NL_LANGINFO) && defined(CODESET)
143         if (ret && strcasecmp(ret, "LOCALE") == 0) {
144                 const char *ln = NULL;
145
146 #ifdef HAVE_SETLOCALE
147                 setlocale(LC_ALL, "");
148 #endif
149                 ln = nl_langinfo(CODESET);
150                 if (ln) {
151                         /* Check whether the charset name is supported
152                            by iconv */
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);
156                                 ln = "ASCII";
157                         } else {
158                                 atalk_iconv_close(handle);
159                         }
160                         if (ch==CH_UNIX)
161                                 strlcpy(unixname, ln, sizeof(unixname));
162                 }
163                 ret = ln;
164         }
165 #else /* system doesn't have LOCALE support */
166 if (ch == CH_UNIX) ret = NULL;
167 #endif
168
169         if (!ret || !*ret) ret = "ASCII";
170         return ret;
171 }
172
173 struct charset_functions* get_charset_functions (charset_t ch)
174 {
175         if (charsets[ch] != NULL)
176                 return charsets[ch];
177
178         charsets[ch] = find_charset_functions(charset_name(ch));
179
180         return charsets[ch];
181 }
182         
183
184 void lazy_initialize_conv(void)
185 {
186         static int initialized = 0;
187
188         if (!initialized) {
189                 initialized = 1;
190                 init_iconv();
191         }
192 }
193
194 charset_t add_charset(char* name)
195 {
196         static charset_t max_charset_t = NUM_CHARSETS-1;
197         charset_t cur_charset_t = max_charset_t+1;
198         unsigned int c1;
199
200         lazy_initialize_conv();
201
202         for (c1=0; c1<=max_charset_t;c1++) {
203                 if ( strcasecmp(name, charset_name(c1)) == 0)
204                         return (c1);
205         }
206
207         if ( cur_charset_t >= MAX_CHARSETS )  {
208                 LOG (log_debug, logtype_default, "Adding charset %s failed, too many charsets (max. %u allowed)", 
209                         name, MAX_CHARSETS);
210                 return (charset_t) -1;
211         }
212
213         /* First try to setup the required conversions */
214
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;
221         }
222
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;
229         }
230
231         /* register the new charset_t name */
232         charset_names[cur_charset_t] = strdup(name);
233
234         charsets[cur_charset_t] = get_charset_functions (cur_charset_t);
235         max_charset_t++;
236
237 #ifdef DEBUG
238         LOG(log_debug, logtype_default, "Added charset %s with handle %u", name, cur_charset_t);
239 #endif /* DEBUG */
240         return (cur_charset_t);
241 }
242
243 /**
244  * Initialize iconv conversion descriptors.
245  *
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.
249  **/
250 void init_iconv(void)
251 {
252         int c1;
253
254         for (c1=0;c1<NUM_CHARSETS;c1++) {
255                 const char *name = charset_name((charset_t)c1);
256
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;
262                 }
263
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;
270                         }
271                 }
272                 
273                 charsets[c1] = get_charset_functions (c1);
274         }
275 }
276
277 /**
278  * Convert string from one encoding to another, making error checking etc
279  *
280  * @param src pointer to source string (multibyte or singlebyte)
281  * @param srclen length of the source string in bytes
282  * @param dest pointer to destination string (multibyte or singlebyte)
283  * @param destlen maximal length allowed for string
284  * @returns the number of bytes occupied in the destination
285  **/
286 static size_t convert_string_internal(charset_t from, charset_t to,
287                       void const *src, size_t srclen, 
288                       void *dest, size_t destlen)
289 {
290         size_t i_len, o_len;
291         size_t retval;
292         const char* inbuf = (const char*)src;
293         char* outbuf = (char*)dest;
294         char* o_save = outbuf;
295         atalk_iconv_t descriptor;
296
297         /* Fixed based on Samba 3.0.6 */
298         if (srclen == (size_t)-1) {
299                 if (from == CH_UCS2) {
300                         srclen = (strlen_w((const ucs2_t *)src)+1) * 2;
301                 } else {
302                         srclen = strlen((const char *)src)+1;
303                 }
304         }
305
306
307         lazy_initialize_conv();
308
309         descriptor = conv_handles[from][to];
310
311         if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
312                 return (size_t) -1;
313         }
314
315         i_len=srclen;
316         o_len=destlen;
317         retval = atalk_iconv(descriptor,  &inbuf, &i_len, &outbuf, &o_len);
318         if(retval==(size_t)-1) {
319                 const char *reason="unknown error";
320                 switch(errno) {
321                         case EINVAL:
322                                 reason="Incomplete multibyte sequence";
323                                 break;
324                         case E2BIG:
325                                 reason="No more room"; 
326                                break;
327                         case EILSEQ:
328                                reason="Illegal multibyte sequence";
329                                break;
330                 }
331                 LOG(log_debug, logtype_default,"Conversion error: %s",reason);
332                 return (size_t)-1;
333         }
334
335         /* Terminate the string */
336         if (to == CH_UCS2 && destlen-o_len >= 2) {
337                 o_save[destlen-o_len]   = 0;
338                 o_save[destlen-o_len+1] = 0;
339         }
340         else if ( to != CH_UCS2 && destlen-o_len > 0 )
341                 o_save[destlen-o_len] = 0;
342         else {
343                 /* FIXME: what should we do here, string *might* be unterminated. E2BIG? */
344         }
345
346         return destlen-o_len;
347 }
348
349
350 size_t convert_string(charset_t from, charset_t to,
351                       void const *src, size_t srclen, 
352                       void *dest, size_t destlen)
353 {
354         size_t i_len, o_len;
355         ucs2_t *u;
356         ucs2_t buffer[MAXPATHLEN];
357         ucs2_t buffer2[MAXPATHLEN];
358         int composition = 0;
359
360         lazy_initialize_conv();
361
362         /* convert from_set to UCS2 */
363         if ((size_t)(-1) == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen, 
364                                                                (char*) buffer, sizeof(buffer))) ) {
365                 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from));
366                 return (size_t) -1;
367         }
368
369         /* Do pre/decomposition */
370         if ( ((!(charsets[to])   || !(charsets[to]->flags & CHARSET_DECOMPOSED)) && 
371                 (!(charsets[from]) || (charsets[from]->flags & CHARSET_DECOMPOSED))))
372             composition = 1;
373         if ((charsets[to] && charsets[to]->flags & CHARSET_DECOMPOSED) )
374             composition = 2;
375  
376         i_len = sizeof(buffer2);
377         u = buffer2;
378
379         switch (composition) {
380         case 0:
381             u = buffer;
382             i_len = o_len;
383             break;
384         case 1:
385             if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
386                 return (size_t)(-1);
387             break;
388         case 2:
389             if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
390                 return (size_t)(-1);
391             break;
392         }
393                 
394         /* Convert UCS2 to to_set */
395         if ((size_t)(-1) == ( o_len = convert_string_internal( CH_UCS2, to, (char*) u, i_len, dest, destlen)) ) {
396                 LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
397                 return (size_t) -1;
398         }
399
400         return o_len;
401 }       
402
403         
404
405 /**
406  * Convert between character sets, allocating a new buffer for the result.
407  *
408  * @param srclen length of source buffer.
409  * @param dest always set at least to NULL
410  * @note -1 is not accepted for srclen.
411  *
412  * @returns Size in bytes of the converted string; or -1 in case of error.
413  **/
414
415 static size_t convert_string_allocate_internal(charset_t from, charset_t to,
416                                void const *src, size_t srclen, char **dest)
417 {
418         size_t i_len, o_len, destlen;
419         size_t retval;
420         const char *inbuf = (const char *)src;
421         char *outbuf = NULL, *ob = NULL;
422         atalk_iconv_t descriptor;
423
424         *dest = NULL;
425
426         if (src == NULL || srclen == (size_t)-1)
427                 return (size_t)-1;
428
429         lazy_initialize_conv();
430
431         descriptor = conv_handles[from][to];
432
433         if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
434                 /* conversion not supported, return -1*/
435                 LOG(log_debug, logtype_default, "convert_string_allocate: conversion not supported!");
436                 return -1;
437         }
438
439         destlen = MAX(srclen, 512);
440 convert:
441         destlen = destlen * 2;
442         outbuf = (char *)realloc(ob, destlen);
443         if (!outbuf) {
444                 LOG(log_debug, logtype_default,"convert_string_allocate: realloc failed!");
445                 SAFE_FREE(ob);
446                 return (size_t)-1;
447         } else {
448                 ob = outbuf;
449         }
450         inbuf = src;   /* this restarts the whole conversion if buffer needed to be increased */
451         i_len = srclen;
452         o_len = destlen;
453         retval = atalk_iconv(descriptor,
454                            &inbuf, &i_len,
455                            &outbuf, &o_len);
456         if(retval == (size_t)-1)                {
457                 const char *reason="unknown error";
458                 switch(errno) {
459                         case EINVAL:
460                                 reason="Incomplete multibyte sequence";
461                                 break;
462                         case E2BIG:
463                                 goto convert;           
464                         case EILSEQ:
465                                 reason="Illegal multibyte sequence";
466                                 break;
467                 }
468                 LOG(log_debug, logtype_default,"Conversion error: %s(%s)",reason,inbuf);
469                 SAFE_FREE(ob);
470                 return (size_t)-1;
471         }
472
473         
474         destlen = destlen - o_len;
475
476         /* Terminate the string */
477         if (to == CH_UCS2 && o_len >= 2) {
478                 ob[destlen] = 0;
479                 ob[destlen+1] = 0;
480                 *dest = (char *)realloc(ob,destlen+2);
481         }
482         else if ( to != CH_UCS2 && o_len > 0 ) {
483                 ob[destlen] = 0;
484                 *dest = (char *)realloc(ob,destlen+1);
485         }
486         else {
487                 goto convert; /* realloc */
488         }
489
490         if (destlen && !*dest) {
491                 LOG(log_debug, logtype_default, "convert_string_allocate: out of memory!");
492                 SAFE_FREE(ob);
493                 return (size_t)-1;
494         }
495
496         return destlen;
497 }
498
499
500 size_t convert_string_allocate(charset_t from, charset_t to,
501                       void const *src, size_t srclen, 
502                       char ** dest)
503 {
504         size_t i_len, o_len;
505         ucs2_t *u;
506         ucs2_t buffer[MAXPATHLEN];
507         ucs2_t buffer2[MAXPATHLEN];
508         int composition = 0;
509
510         lazy_initialize_conv();
511
512         *dest = NULL;
513
514         /* convert from_set to UCS2 */
515         if ((size_t)(-1) == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen, 
516                                                                buffer, sizeof(buffer))) ) {
517                 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from));
518                 return (size_t) -1;
519         }
520
521         /* Do pre/decomposition */
522         if ( ((!(charsets[to])   || !(charsets[to]->flags & CHARSET_DECOMPOSED)) && 
523                 (!(charsets[from]) || (charsets[from]->flags & CHARSET_DECOMPOSED))))
524             composition = 1;
525         if ((charsets[to] && charsets[to]->flags & CHARSET_DECOMPOSED) )
526             composition = 2;
527  
528         i_len = sizeof(buffer2);
529         u = buffer2;
530
531         switch (composition) {
532         case 0:
533             u = buffer;
534             i_len = o_len;
535             break;
536         case 1:
537             if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
538                 return (size_t)(-1);
539             break;
540         case 2:
541             if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
542                 return (size_t)(-1);
543             break;
544         }
545                 
546         /* Convert UCS2 to to_set */
547         if ((size_t)(-1) == ( o_len = convert_string_allocate_internal( CH_UCS2, to, (char*)u, i_len, dest)) ) 
548                 LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
549                 
550         return o_len;
551
552 }
553
554 size_t charset_strupper(charset_t ch, const char *src, size_t srclen, char *dest, size_t destlen)
555 {
556         size_t size;
557         char *buffer;
558         
559         size = convert_string_allocate_internal(ch, CH_UCS2, src, srclen,
560                                        (char**) &buffer);
561         if (size == (size_t)-1) {
562                 SAFE_FREE(buffer);
563                 return size;
564         }
565         if (!strupper_w((ucs2_t *)buffer) && (dest == src)) {
566                 free(buffer);
567                 return srclen;
568         }
569         
570         size = convert_string_internal(CH_UCS2, ch, buffer, size, dest, destlen);
571         free(buffer);
572         return size;
573 }
574
575 size_t charset_strlower(charset_t ch, const char *src, size_t srclen, char *dest, size_t destlen)
576 {
577         size_t size;
578         char *buffer;
579         
580         size = convert_string_allocate_internal(ch, CH_UCS2, src, srclen,
581                                        (char **) &buffer);
582         if (size == (size_t)-1) {
583                 SAFE_FREE(buffer);
584                 return size;
585         }
586         if (!strlower_w((ucs2_t *)buffer) && (dest == src)) {
587                 free(buffer);
588                 return srclen;
589         }
590         
591         size = convert_string_internal(CH_UCS2, ch, buffer, size, dest, destlen);
592         free(buffer);
593         return size;
594 }
595
596
597 size_t unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
598 {
599         return charset_strupper( CH_UNIX, src, srclen, dest, destlen);
600 }
601
602 size_t unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
603 {
604         return charset_strlower( CH_UNIX, src, srclen, dest, destlen);
605 }
606
607 size_t utf8_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
608 {
609         return charset_strupper( CH_UTF8, src, srclen, dest, destlen);
610 }
611
612 size_t utf8_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
613 {
614         return charset_strlower( CH_UTF8, src, srclen, dest, destlen);
615 }
616
617 /**
618  * Copy a string from a charset_t char* src to a UCS2 destination, allocating a buffer
619  *
620  * @param dest always set at least to NULL 
621  *
622  * @returns The number of bytes occupied by the string in the destination
623  *         or -1 in case of error.
624  **/
625
626 size_t charset_to_ucs2_allocate(charset_t ch, ucs2_t **dest, const char *src)
627 {
628         size_t src_len = strlen(src);
629
630         *dest = NULL;
631         return convert_string_allocate(ch, CH_UCS2, src, src_len, (char**) dest);       
632 }
633
634 /** -----------------------------------
635  * Copy a string from a charset_t char* src to a UTF-8 destination, allocating a buffer
636  *
637  * @param dest always set at least to NULL 
638  *
639  * @returns The number of bytes occupied by the string in the destination
640  **/
641
642 size_t charset_to_utf8_allocate(charset_t ch, char **dest, const char *src)
643 {
644         size_t src_len = strlen(src);
645
646         *dest = NULL;
647         return convert_string_allocate(ch, CH_UTF8, src, src_len, dest);        
648 }
649
650 /** -----------------------------------
651  * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer
652  *
653  * @param dest always set at least to NULL 
654  *
655  * @returns The number of bytes occupied by the string in the destination
656  **/
657
658 size_t ucs2_to_charset(charset_t ch, const ucs2_t *src, char *dest, size_t destlen)
659 {
660         size_t src_len = (strlen_w(src)) * sizeof(ucs2_t);
661         return convert_string(CH_UCS2, ch, src, src_len, dest, destlen);        
662 }
663
664 /* --------------------------------- */
665 size_t ucs2_to_charset_allocate(charset_t ch, char **dest, const ucs2_t *src)
666 {
667         size_t src_len = (strlen_w(src)) * sizeof(ucs2_t);
668         *dest = NULL;
669         return convert_string_allocate(CH_UCS2, ch, src, src_len, dest);        
670 }
671
672 /** ---------------------------------
673  * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer
674  *
675  * @param dest always set at least to NULL 
676  *
677  * @returns The number of bytes occupied by the string in the destination
678  **/
679
680 size_t utf8_to_charset_allocate(charset_t ch, char **dest, const char *src)
681 {
682         size_t src_len = strlen(src);
683         *dest = NULL;
684         return convert_string_allocate(CH_UTF8, ch, src, src_len, dest);        
685 }
686
687 size_t charset_precompose ( charset_t ch, char * src, size_t inlen, char * dst, size_t outlen)
688 {
689         char *buffer;
690         ucs2_t u[MAXPATHLEN];
691         size_t len;
692         size_t ilen;
693
694         if ((size_t)(-1) == (len = convert_string_allocate_internal(ch, CH_UCS2, src, inlen, &buffer)) )
695             return len;
696
697         ilen=sizeof(u);
698
699         if ( (size_t)-1 == (ilen = precompose_w((ucs2_t *)buffer, len, u, &ilen)) ) {
700             free (buffer);
701             return (size_t)(-1);
702         }
703
704         if ((size_t)(-1) == (len = convert_string_internal( CH_UCS2, ch, (char*)u, ilen, dst, outlen)) ) {
705             free (buffer);
706             return (size_t)(-1);
707         }
708         
709         free(buffer);
710         dst[len] = 0;
711         return (len);
712 }
713
714 size_t charset_decompose ( charset_t ch, char * src, size_t inlen, char * dst, size_t outlen)
715 {
716         char *buffer;
717         ucs2_t u[MAXPATHLEN];
718         size_t len;
719         size_t ilen;
720
721         if ((size_t)(-1) == (len = convert_string_allocate_internal(ch, CH_UCS2, src, inlen, &buffer)) )
722             return len;
723
724         ilen=sizeof(u);
725
726         if ( (size_t)-1 == (ilen = decompose_w((ucs2_t *)buffer, len, u, &ilen)) ) {
727             free (buffer);
728             return (size_t)(-1);
729         }
730
731         if ((size_t)(-1) == (len = convert_string_internal( CH_UCS2, ch, (char*)u, ilen, dst, outlen)) ) {
732             free (buffer);
733             return (size_t)(-1);
734         }
735
736         free(buffer);
737         dst[len] = 0;
738         return (len);
739 }
740
741 size_t utf8_precompose ( char * src, size_t inlen, char * dst, size_t outlen)
742 {
743         return charset_precompose ( CH_UTF8, src, inlen, dst, outlen);
744 }
745
746 size_t utf8_decompose ( char * src, size_t inlen, char * dst, size_t outlen)
747 {
748         return charset_decompose ( CH_UTF8, src, inlen, dst, outlen);
749 }
750
751 #if 0
752 static char  debugbuf[ MAXPATHLEN +1 ];
753 char * debug_out ( char * seq, size_t len)
754 {
755         size_t i = 0;
756         unsigned char *p;
757         char *q;
758
759         p = (unsigned char*) seq;
760         q = debugbuf;
761
762         for ( i = 0; i<=(len-1); i++)
763         {
764                 sprintf(q, "%2.2x.", *p);
765                 q += 3;
766                 p++;
767         }
768         *q=0;
769         q = debugbuf;
770         return q;
771 }
772 #endif
773
774 /* 
775  * Convert from MB to UCS2 charset 
776  * Flags:
777  *              CONV_UNESCAPEHEX:        ':XX' will be converted to an UCS2 character
778  *              CONV_IGNORE:             return the first convertable characters.
779  *              CONV_FORCE:      force convertion
780  * FIXME:
781  *              This will *not* work if the destination charset is not multibyte, i.e. UCS2->UCS2 will fail
782  *              The (un)escape scheme is not compatible to the old cap style escape. This is bad, we need it 
783  *              for e.g. HFS cdroms.
784  */
785
786 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)
787 {
788   const u_int16_t option = (flags ? *flags : 0);
789   size_t i_len, o_len;
790   size_t j = 0;
791   const char* inbuf = (const char*)src;
792   char* outbuf = dest;
793   atalk_iconv_t descriptor;
794   atalk_iconv_t descriptor_cap;
795
796   if (srclen == (size_t)-1)
797     srclen = strlen(src) + 1;
798
799   lazy_initialize_conv();
800
801   descriptor = conv_handles[from_set][CH_UCS2];
802   descriptor_cap = conv_handles[cap_set][CH_UCS2];
803
804   if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
805     errno = EINVAL;
806     return (size_t)-1;
807   }
808
809   i_len=srclen;
810   o_len=destlen;
811
812   while (i_len > 0) {
813     if ((option & CONV_UNESCAPEHEX)) {
814       for (j = 0; j < i_len; ++j) {
815         if (inbuf[j] == ':') break;
816       }
817       j = i_len - j;
818       i_len -= j;
819     }
820
821     if (i_len > 0 &&
822         atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len) == (size_t)-1) {
823       if (errno == EILSEQ || errno == EINVAL) {
824         errno = EILSEQ;
825         if ((option & CONV_IGNORE)) {
826             *flags |= CONV_REQMANGLE;
827             return destlen - o_len;
828         }
829         if ((option & CONV__EILSEQ)) {
830             if (o_len < 2) {
831                 errno = E2BIG;
832                 goto end;
833             }
834             *((ucs2_t *)outbuf) = (ucs2_t) IGNORE_CHAR; /**inbuf */
835             inbuf++;
836             i_len--;
837             outbuf += 2;
838             o_len -= 2;
839             /* FIXME reset stat ? */
840             continue;
841         }
842       }
843       goto end;
844     }
845
846     if (j) {
847       /* we're at the start on an hex encoded ucs2 char */
848       char h[MAXPATHLEN];
849       size_t hlen = 0;
850
851       i_len = j, j = 0;
852       while (i_len >= 3 && inbuf[0] == ':' &&
853              isxdigit(inbuf[1]) && isxdigit(inbuf[2])) {
854         h[hlen++] = (hextoint(inbuf[1]) << 4) | hextoint(inbuf[2]);
855         inbuf += 3;
856         i_len -= 3;
857       }
858       if (hlen) {
859         const char *h_buf = h;
860         if (atalk_iconv(descriptor_cap, &h_buf, &hlen, &outbuf, &o_len) == (size_t)-1) {
861           i_len += hlen * 3;
862           inbuf -= hlen * 3;
863           if (errno == EILSEQ && (option & CONV_IGNORE)) {
864             *flags |= CONV_REQMANGLE;
865             return destlen - o_len;
866           }
867           goto end;
868         }
869       } else {
870         /* We have an invalid :xx sequence */
871         errno = EILSEQ;
872         if ((option & CONV_IGNORE)) {
873           *flags |= CONV_REQMANGLE;
874           return destlen - o_len;
875         }
876         goto end;
877       }
878     }
879   }
880  end:
881   return (i_len + j == 0 || (option & CONV_FORCE)) ? destlen - o_len : (size_t)-1;
882 }
883
884 /* 
885  * Convert from UCS2 to MB charset 
886  * Flags:
887  *              CONV_ESCAPEDOTS: escape leading dots
888  *              CONV_ESCAPEHEX:  unconvertable characters and '/' will be escaped to :XX
889  *              CONV_IGNORE:     return the first convertable characters.
890  *              CONV__EILSEQ:    unconvertable characters will be replaced with '_'
891  *              CONV_FORCE:      force convertion
892  * FIXME:
893  *              CONV_IGNORE and CONV_ESCAPEHEX can't work together. Should we check this ?
894  *              This will *not* work if the destination charset is not multibyte, i.e. UCS2->UCS2 will fail
895  *              The escape scheme is not compatible to the old cap style escape. This is bad, we need it 
896  *              for e.g. HFS cdroms.
897  */
898
899
900 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)
901 {
902   const u_int16_t option = (flags ? *flags : 0);
903   size_t i_len, o_len, i;
904   size_t j = 0;
905   const char* inbuf = (const char*)src;
906   char* outbuf = (char*)dest;
907   atalk_iconv_t descriptor;
908   atalk_iconv_t descriptor_cap;
909
910   lazy_initialize_conv();
911
912   descriptor = conv_handles[CH_UCS2][to_set];
913   descriptor_cap = conv_handles[CH_UCS2][cap_set];
914
915   if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
916     errno = EINVAL;
917     return (size_t) -1;
918   }
919
920   i_len=srclen;
921   o_len=destlen;
922
923   if ((option & CONV_ESCAPEDOTS) &&
924       i_len >= 2 && SVAL(inbuf, 0) == 0x002e) { /* 0x002e = . */
925     if (o_len < 3) {
926       errno = E2BIG;
927       goto end;
928     }
929     *outbuf++ = ':';
930     *outbuf++ = '2';
931     *outbuf++ = 'e';
932     o_len -= 3;
933     inbuf += 2;
934     i_len -= 2;
935     *flags |= CONV_REQESCAPE;
936   }
937
938   while (i_len >= 2) {
939     if ((option & CONV_ESCAPEHEX)) {
940       for (i = 0; i < i_len; i += 2) {
941         ucs2_t c = SVAL(inbuf, i);
942         if (c == 0x002f) { /* 0x002f = / */
943           j = i_len - i;
944           i_len = i;
945           break;
946         } else if (c == 0x003a) { /* 0x003a = : */
947           errno = EILSEQ;
948           goto end;
949         }
950       }
951     }
952     while (i_len > 0 &&
953            atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len) == (size_t)-1) {
954       if (errno == EILSEQ) {
955         if ((option & CONV_IGNORE)) {
956           *flags |= CONV_REQMANGLE;
957           return destlen - o_len;
958         }
959         if ((option & CONV_ESCAPEHEX)) {
960           const size_t bufsiz = o_len / 3 + 1;
961           char *buf = malloc(bufsiz);
962           size_t buflen;
963
964           if (!buf) 
965               goto end;
966           i = i_len;
967           for (buflen = 1; buflen <= bufsiz; ++buflen) {
968             char *b = buf;
969             size_t o = buflen;
970             if (atalk_iconv(descriptor_cap, &inbuf, &i, &b, &o) != (size_t)-1) {
971               buflen -= o;
972               break;
973             } else if (errno != E2BIG) {
974               SAFE_FREE(buf);
975               goto end;
976             }
977           }
978           if (o_len < buflen * 3) {
979             SAFE_FREE(buf);
980             errno = E2BIG;
981             goto end;
982           }
983           o_len -= buflen * 3;
984           i_len = i;
985           for (i = 0; i < buflen; ++i) {
986             *outbuf++ = ':';
987             *outbuf++ = hexdig[(buf[i] >> 4) & 0x0f];
988             *outbuf++ = hexdig[buf[i] & 0x0f];
989           }
990           SAFE_FREE(buf);
991           *flags |= CONV_REQESCAPE;
992           continue;
993         }
994       }
995       goto end;
996     }
997
998     if (j) {
999       i_len = j, j = 0;
1000       if (o_len < 3) {
1001         errno = E2BIG;
1002         goto end;
1003       }
1004       *outbuf++ = ':';
1005       *outbuf++ = '2';
1006       *outbuf++ = 'f';
1007       o_len -= 3;
1008       inbuf += 2;
1009       i_len -= 2;
1010     }
1011   }
1012   if (i_len > 0) errno = EINVAL;
1013  end:
1014   return (i_len + j == 0 || (option & CONV_FORCE)) ? destlen - o_len : (size_t)-1;
1015 }
1016
1017 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)
1018 {
1019         size_t i_len, o_len;
1020         ucs2_t *u;
1021         ucs2_t buffer[MAXPATHLEN];
1022         ucs2_t buffer2[MAXPATHLEN];
1023         int composition = 0;
1024         
1025         lazy_initialize_conv();
1026
1027         /* convert from_set to UCS2 */
1028         if ((size_t)(-1) == ( o_len = pull_charset_flags( from_set, cap_charset, src, src_len, 
1029                                                           (char *) buffer, sizeof(buffer), flags)) ) {
1030                 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from_set));
1031                 return (size_t) -1;
1032         }
1033
1034         if ( o_len == 0)
1035                 return o_len;
1036
1037         /* Do pre/decomposition */
1038         if (CHECK_FLAGS(flags, CONV_PRECOMPOSE) || 
1039                 ((!(charsets[to_set])   || !(charsets[to_set]->flags & CHARSET_DECOMPOSED)) && 
1040                 (!(charsets[from_set]) || (charsets[from_set]->flags & CHARSET_DECOMPOSED))))
1041             composition = 1;
1042         if (CHECK_FLAGS(flags, CONV_DECOMPOSE) || (charsets[to_set] && charsets[to_set]->flags & CHARSET_DECOMPOSED) )
1043             composition = 2;
1044  
1045         i_len = sizeof(buffer2);
1046         u = buffer2;
1047
1048         switch (composition) {
1049         case 0:
1050             u = buffer;
1051             i_len = o_len;
1052             break;
1053         case 1:
1054             if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
1055                 return (size_t)(-1);
1056             break;
1057         case 2:
1058             if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
1059                 return (size_t)(-1);
1060             break;
1061         }
1062                 
1063         /* Do case conversions */       
1064         if (CHECK_FLAGS(flags, CONV_TOUPPER)) {
1065             strupper_w(u);
1066         }
1067         if (CHECK_FLAGS(flags, CONV_TOLOWER)) {
1068             strlower_w(u);
1069         }
1070
1071         /* Convert UCS2 to to_set */
1072         if ((size_t)(-1) == ( o_len = push_charset_flags( to_set, cap_charset, (char *)u, i_len, dest, dest_len, flags )) ) {
1073                 LOG(log_error, logtype_default, 
1074                        "Conversion failed (CH_UCS2 to %s):%s", charset_name(to_set), strerror(errno));
1075                 return (size_t) -1;
1076         }
1077
1078         return o_len;
1079 }