]> arthur.barton.de Git - netatalk.git/blob - libatalk/unicode/charcnv.c
fix macname cache, SF bug 1021642
[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         if (srclen == (size_t)-1)
298                 srclen = strlen(src)+1;
299
300         lazy_initialize_conv();
301
302         descriptor = conv_handles[from][to];
303
304         if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
305                 return (size_t) -1;
306         }
307
308         i_len=srclen;
309         o_len=destlen;
310         retval = atalk_iconv(descriptor,  &inbuf, &i_len, &outbuf, &o_len);
311         if(retval==(size_t)-1) {
312                 const char *reason="unknown error";
313                 switch(errno) {
314                         case EINVAL:
315                                 reason="Incomplete multibyte sequence";
316                                 break;
317                         case E2BIG:
318                                 reason="No more room"; 
319                                break;
320                         case EILSEQ:
321                                reason="Illegal multibyte sequence";
322                                break;
323                 }
324                 LOG(log_debug, logtype_default,"Conversion error: %s",reason);
325                 return (size_t)-1;
326         }
327
328         /* Terminate the string */
329         if (to == CH_UCS2 && destlen-o_len >= 2) {
330                 o_save[destlen-o_len]   = 0;
331                 o_save[destlen-o_len+1] = 0;
332         }
333         else if ( to != CH_UCS2 && destlen-o_len > 0 )
334                 o_save[destlen-o_len] = 0;
335         else {
336                 /* FIXME: what should we do here, string *might* be unterminated. E2BIG? */
337         }
338
339         return destlen-o_len;
340 }
341
342
343 size_t convert_string(charset_t from, charset_t to,
344                       void const *src, size_t srclen, 
345                       void *dest, size_t destlen)
346 {
347         size_t i_len, o_len;
348         ucs2_t *u;
349         ucs2_t buffer[MAXPATHLEN];
350         ucs2_t buffer2[MAXPATHLEN];
351         int composition = 0;
352
353         lazy_initialize_conv();
354
355         /* convert from_set to UCS2 */
356         if ((size_t)(-1) == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen, 
357                                                                (char*) buffer, sizeof(buffer))) ) {
358                 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from));
359                 return (size_t) -1;
360         }
361
362         /* Do pre/decomposition */
363         if ( ((!(charsets[to])   || !(charsets[to]->flags & CHARSET_DECOMPOSED)) && 
364                 (!(charsets[from]) || (charsets[from]->flags & CHARSET_DECOMPOSED))))
365             composition = 1;
366         if ((charsets[to] && charsets[to]->flags & CHARSET_DECOMPOSED) )
367             composition = 2;
368  
369         i_len = sizeof(buffer2);
370         u = buffer2;
371
372         switch (composition) {
373         case 0:
374             u = buffer;
375             i_len = o_len;
376             break;
377         case 1:
378             if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
379                 return (size_t)(-1);
380             break;
381         case 2:
382             if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
383                 return (size_t)(-1);
384             break;
385         }
386                 
387         /* Convert UCS2 to to_set */
388         if ((size_t)(-1) == ( o_len = convert_string_internal( CH_UCS2, to, (char*) u, i_len, dest, destlen)) ) {
389                 LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
390                 return (size_t) -1;
391         }
392
393         return o_len;
394 }       
395
396         
397
398 /**
399  * Convert between character sets, allocating a new buffer for the result.
400  *
401  * @param srclen length of source buffer.
402  * @param dest always set at least to NULL
403  * @note -1 is not accepted for srclen.
404  *
405  * @returns Size in bytes of the converted string; or -1 in case of error.
406  **/
407
408 static size_t convert_string_allocate_internal(charset_t from, charset_t to,
409                                void const *src, size_t srclen, char **dest)
410 {
411         size_t i_len, o_len, destlen;
412         size_t retval;
413         const char *inbuf = (const char *)src;
414         char *outbuf = NULL, *ob = NULL;
415         atalk_iconv_t descriptor;
416
417         *dest = NULL;
418
419         if (src == NULL || srclen == (size_t)-1)
420                 return (size_t)-1;
421
422         lazy_initialize_conv();
423
424         descriptor = conv_handles[from][to];
425
426         if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
427                 /* conversion not supported, return -1*/
428                 LOG(log_debug, logtype_default, "convert_string_allocate: conversion not supported!");
429                 return -1;
430         }
431
432         destlen = MAX(srclen, 512);
433 convert:
434         destlen = destlen * 2;
435         outbuf = (char *)realloc(ob, destlen);
436         if (!outbuf) {
437                 LOG(log_debug, logtype_default,"convert_string_allocate: realloc failed!");
438                 SAFE_FREE(ob);
439                 return (size_t)-1;
440         } else {
441                 ob = outbuf;
442         }
443         inbuf = src;   /* this restarts the whole conversion if buffer needed to be increased */
444         i_len = srclen;
445         o_len = destlen;
446         retval = atalk_iconv(descriptor,
447                            &inbuf, &i_len,
448                            &outbuf, &o_len);
449         if(retval == (size_t)-1)                {
450                 const char *reason="unknown error";
451                 switch(errno) {
452                         case EINVAL:
453                                 reason="Incomplete multibyte sequence";
454                                 break;
455                         case E2BIG:
456                                 goto convert;           
457                         case EILSEQ:
458                                 reason="Illegal multibyte sequence";
459                                 break;
460                 }
461                 LOG(log_debug, logtype_default,"Conversion error: %s(%s)",reason,inbuf);
462                 SAFE_FREE(ob);
463                 return (size_t)-1;
464         }
465
466         
467         destlen = destlen - o_len;
468
469         /* Terminate the string */
470         if (to == CH_UCS2 && o_len >= 2) {
471                 ob[destlen] = 0;
472                 ob[destlen+1] = 0;
473                 *dest = (char *)realloc(ob,destlen+2);
474         }
475         else if ( to != CH_UCS2 && o_len > 0 ) {
476                 ob[destlen] = 0;
477                 *dest = (char *)realloc(ob,destlen+1);
478         }
479         else {
480                 goto convert; /* realloc */
481         }
482
483         if (destlen && !*dest) {
484                 LOG(log_debug, logtype_default, "convert_string_allocate: out of memory!");
485                 SAFE_FREE(ob);
486                 return (size_t)-1;
487         }
488
489         return destlen;
490 }
491
492
493 size_t convert_string_allocate(charset_t from, charset_t to,
494                       void const *src, size_t srclen, 
495                       char ** dest)
496 {
497         size_t i_len, o_len;
498         ucs2_t *u;
499         ucs2_t buffer[MAXPATHLEN];
500         ucs2_t buffer2[MAXPATHLEN];
501         int composition = 0;
502
503         lazy_initialize_conv();
504
505         *dest = NULL;
506
507         /* convert from_set to UCS2 */
508         if ((size_t)(-1) == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen, 
509                                                                buffer, sizeof(buffer))) ) {
510                 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from));
511                 return (size_t) -1;
512         }
513
514         /* Do pre/decomposition */
515         if ( ((!(charsets[to])   || !(charsets[to]->flags & CHARSET_DECOMPOSED)) && 
516                 (!(charsets[from]) || (charsets[from]->flags & CHARSET_DECOMPOSED))))
517             composition = 1;
518         if ((charsets[to] && charsets[to]->flags & CHARSET_DECOMPOSED) )
519             composition = 2;
520  
521         i_len = sizeof(buffer2);
522         u = buffer2;
523
524         switch (composition) {
525         case 0:
526             u = buffer;
527             i_len = o_len;
528             break;
529         case 1:
530             if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
531                 return (size_t)(-1);
532             break;
533         case 2:
534             if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
535                 return (size_t)(-1);
536             break;
537         }
538                 
539         /* Convert UCS2 to to_set */
540         if ((size_t)(-1) == ( o_len = convert_string_allocate_internal( CH_UCS2, to, (char*)u, i_len, dest)) ) 
541                 LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
542                 
543         return o_len;
544
545 }
546
547 size_t charset_strupper(charset_t ch, const char *src, size_t srclen, char *dest, size_t destlen)
548 {
549         size_t size;
550         char *buffer;
551         
552         size = convert_string_allocate_internal(ch, CH_UCS2, src, srclen,
553                                        (char**) &buffer);
554         if (size == (size_t)-1) {
555                 SAFE_FREE(buffer);
556                 return size;
557         }
558         if (!strupper_w((ucs2_t *)buffer) && (dest == src)) {
559                 free(buffer);
560                 return srclen;
561         }
562         
563         size = convert_string_internal(CH_UCS2, ch, buffer, size, dest, destlen);
564         free(buffer);
565         return size;
566 }
567
568 size_t charset_strlower(charset_t ch, const char *src, size_t srclen, char *dest, size_t destlen)
569 {
570         size_t size;
571         char *buffer;
572         
573         size = convert_string_allocate_internal(ch, CH_UCS2, src, srclen,
574                                        (char **) &buffer);
575         if (size == (size_t)-1) {
576                 SAFE_FREE(buffer);
577                 return size;
578         }
579         if (!strlower_w((ucs2_t *)buffer) && (dest == src)) {
580                 free(buffer);
581                 return srclen;
582         }
583         
584         size = convert_string_internal(CH_UCS2, ch, buffer, size, dest, destlen);
585         free(buffer);
586         return size;
587 }
588
589
590 size_t unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
591 {
592         return charset_strupper( CH_UNIX, src, srclen, dest, destlen);
593 }
594
595 size_t unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
596 {
597         return charset_strlower( CH_UNIX, src, srclen, dest, destlen);
598 }
599
600 size_t utf8_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
601 {
602         return charset_strupper( CH_UTF8, src, srclen, dest, destlen);
603 }
604
605 size_t utf8_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
606 {
607         return charset_strlower( CH_UTF8, src, srclen, dest, destlen);
608 }
609
610 /**
611  * Copy a string from a charset_t char* src to a UCS2 destination, allocating a buffer
612  *
613  * @param dest always set at least to NULL 
614  *
615  * @returns The number of bytes occupied by the string in the destination
616  *         or -1 in case of error.
617  **/
618
619 size_t charset_to_ucs2_allocate(charset_t ch, ucs2_t **dest, const char *src)
620 {
621         size_t src_len = strlen(src);
622
623         *dest = NULL;
624         return convert_string_allocate(ch, CH_UCS2, src, src_len, (char**) dest);       
625 }
626
627 /**
628  * Copy a string from a charset_t char* src to a UTF-8 destination, allocating a buffer
629  *
630  * @param dest always set at least to NULL 
631  *
632  * @returns The number of bytes occupied by the string in the destination
633  **/
634
635 size_t charset_to_utf8_allocate(charset_t ch, char **dest, const char *src)
636 {
637         size_t src_len = strlen(src);
638
639         *dest = NULL;
640         return convert_string_allocate(ch, CH_UTF8, src, src_len, dest);        
641 }
642
643 /**
644  * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer
645  *
646  * @param dest always set at least to NULL 
647  *
648  * @returns The number of bytes occupied by the string in the destination
649  **/
650
651 size_t ucs2_to_charset(charset_t ch, const ucs2_t *src, char *dest, size_t destlen)
652 {
653         size_t src_len = (strlen_w(src)) * sizeof(ucs2_t);
654         return convert_string(CH_UCS2, ch, src, src_len, dest, destlen);        
655 }
656
657
658 size_t ucs2_to_charset_allocate(charset_t ch, char **dest, const ucs2_t *src)
659 {
660         size_t src_len = (strlen_w(src)) * sizeof(ucs2_t);
661         *dest = NULL;
662         return convert_string_allocate(CH_UCS2, ch, src, src_len, dest);        
663 }
664
665 /**
666  * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer
667  *
668  * @param dest always set at least to NULL 
669  *
670  * @returns The number of bytes occupied by the string in the destination
671  **/
672
673 size_t utf8_to_charset_allocate(charset_t ch, char **dest, const char *src)
674 {
675         size_t src_len = strlen(src);
676         *dest = NULL;
677         return convert_string_allocate(CH_UTF8, ch, src, src_len, dest);        
678 }
679
680 size_t charset_precompose ( charset_t ch, char * src, size_t inlen, char * dst, size_t outlen)
681 {
682         char *buffer;
683         ucs2_t u[MAXPATHLEN];
684         size_t len;
685         size_t ilen;
686
687         if ((size_t)(-1) == (len = convert_string_allocate_internal(ch, CH_UCS2, src, inlen, &buffer)) )
688             return len;
689
690         ilen=sizeof(u);
691
692         if ( (size_t)-1 == (ilen = precompose_w((ucs2_t *)buffer, len, u, &ilen)) ) {
693             free (buffer);
694             return (size_t)(-1);
695         }
696
697         if ((size_t)(-1) == (len = convert_string_internal( CH_UCS2, ch, (char*)u, ilen, dst, outlen)) ) {
698             free (buffer);
699             return (size_t)(-1);
700         }
701         
702         free(buffer);
703         dst[len] = 0;
704         return (len);
705 }
706
707 size_t charset_decompose ( charset_t ch, char * src, size_t inlen, char * dst, size_t outlen)
708 {
709         char *buffer;
710         ucs2_t u[MAXPATHLEN];
711         size_t len;
712         size_t ilen;
713
714         if ((size_t)(-1) == (len = convert_string_allocate_internal(ch, CH_UCS2, src, inlen, &buffer)) )
715             return len;
716
717         ilen=sizeof(u);
718
719         if ( (size_t)-1 == (ilen = decompose_w((ucs2_t *)buffer, len, u, &ilen)) ) {
720             free (buffer);
721             return (size_t)(-1);
722         }
723
724         if ((size_t)(-1) == (len = convert_string_internal( CH_UCS2, ch, (char*)u, ilen, dst, outlen)) ) {
725             free (buffer);
726             return (size_t)(-1);
727         }
728
729         free(buffer);
730         dst[len] = 0;
731         return (len);
732 }
733
734 size_t utf8_precompose ( char * src, size_t inlen, char * dst, size_t outlen)
735 {
736         return charset_precompose ( CH_UTF8, src, inlen, dst, outlen);
737 }
738
739 size_t utf8_decompose ( char * src, size_t inlen, char * dst, size_t outlen)
740 {
741         return charset_decompose ( CH_UTF8, src, inlen, dst, outlen);
742 }
743
744 #if 0
745 static char  debugbuf[ MAXPATHLEN +1 ];
746 char * debug_out ( char * seq, size_t len)
747 {
748         size_t i = 0;
749         unsigned char *p;
750         char *q;
751
752         p = (unsigned char*) seq;
753         q = debugbuf;
754
755         for ( i = 0; i<=(len-1); i++)
756         {
757                 sprintf(q, "%2.2x.", *p);
758                 q += 3;
759                 p++;
760         }
761         *q=0;
762         q = debugbuf;
763         return q;
764 }
765 #endif
766
767 /* 
768  * Convert from MB to UCS2 charset 
769  * Flags:
770  *              CONV_UNESCAPEHEX:        ':XX' will be converted to an UCS2 character
771  *              CONV_IGNORE:             return the first convertable characters.
772  * FIXME:
773  *              This will *not* work if the destination charset is not multibyte, i.e. UCS2->UCS2 will fail
774  *              The (un)escape scheme is not compatible to the old cap style escape. This is bad, we need it 
775  *              for e.g. HFS cdroms.
776  */
777
778 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)
779 {
780         size_t i_len, o_len, hlen;
781         size_t retval, j = 0;
782         const char* inbuf = (const char*)src;
783         char* outbuf = (char*)dest;
784         atalk_iconv_t descriptor;
785         atalk_iconv_t descriptor_cap;
786         char *o_save, *s;
787         char h[MAXPATHLEN];
788         const char *h_buf;
789
790         if (srclen == (size_t)-1)
791                 srclen = strlen(src)+1;
792
793         lazy_initialize_conv();
794
795         descriptor = conv_handles[from_set][CH_UCS2];
796         descriptor_cap = conv_handles[cap_charset][CH_UCS2];
797
798         if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
799                 return (size_t) -1;
800         }
801
802         i_len=srclen;
803         o_len=destlen;
804         o_save=outbuf;
805         
806 conversion_loop:
807         if ( flags && (*flags & CONV_UNESCAPEHEX)) {
808                 if ( NULL != (s = strchr ( inbuf, ':'))) {
809                         j = i_len - (s - inbuf);
810                         if ( 0 == (i_len = (s - inbuf)))
811                                 goto unhex_char;
812         }
813         }
814         
815         retval = atalk_iconv(descriptor,  &inbuf, &i_len, &outbuf, &o_len);
816         if(retval==(size_t)-1) {
817             if (errno == EILSEQ && flags && (*flags & CONV_IGNORE)) {
818                                 *flags |= CONV_REQMANGLE;
819                                 return destlen-o_len;
820             }
821             else
822                 return (size_t) -1;
823     }
824     
825 unhex_char:
826         if (j && flags && (*flags & CONV_UNESCAPEHEX )) {
827                 /* we're at the start on an hex encoded ucs2 char */
828                 if (o_len < 2) {
829                         errno = E2BIG;
830                         return (size_t) -1;
831                 }
832                 if ( j >= 3 && 
833                         isxdigit( *(inbuf+1)) && isxdigit( *(inbuf+2)) ) {
834                         hlen = 0;
835                         while ( *inbuf == ':' && j >=3 && 
836                                 isxdigit( *(inbuf+1)) && isxdigit( *(inbuf+2)) ) {
837                                 inbuf++;
838                                 h[hlen]   = hextoint( *inbuf ) << 4;
839                                 inbuf++;
840                                 h[hlen++] |= hextoint( *inbuf );                        
841                                 inbuf++;
842                                 j -= 3;
843                         }
844                         h_buf = (const char*) h;
845                         if ((size_t) -1 == (retval = atalk_iconv(descriptor_cap, &h_buf, &hlen, &outbuf, &o_len)) ) {
846                                 if (errno == EILSEQ && CHECK_FLAGS(flags, CONV_IGNORE)) {
847                                         *flags |= CONV_REQMANGLE;
848                                         return destlen-o_len;
849                                 }
850                                 else {
851                                         return retval;
852                                 }
853                         }
854                 }
855                 else {
856                         /* We have an invalid :xx sequence */
857                         if (CHECK_FLAGS(flags, CONV_IGNORE)) {
858                                 *flags |= CONV_REQMANGLE;
859                                 return destlen-o_len;
860                         }
861                         else {
862                                 errno=EILSEQ;
863                                 return (size_t) -1;
864                         }
865                 }
866                 i_len = j;
867                 j = 0;
868                 if (i_len > 0)
869                         goto conversion_loop;
870         }
871
872
873
874         return destlen-o_len;
875 }
876
877 /* 
878  * Convert from UCS2 to MB charset 
879  * Flags:
880  *              CONV_ESCAPEDOTS: escape leading dots
881  *              CONV_ESCAPEHEX:  unconvertable characters and '/' will be escaped to :XX
882  *              CONV_IGNORE:     unconvertable characters will be replaced with '_'
883  * FIXME:
884  *              CONV_IGNORE and CONV_ESCAPEHEX can't work together. Should we check this ?
885  *              This will *not* work if the destination charset is not multibyte, i.e. UCS2->UCS2 will fail
886  *              The escape scheme is not compatible to the old cap style escape. This is bad, we need it 
887  *              for e.g. HFS cdroms.
888  */
889
890
891 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)
892 {
893     size_t i_len, o_len, i;
894     size_t retval, j = 0;
895     const char* inbuf = (const char*)src;
896     char* outbuf = (char*)dest;
897     atalk_iconv_t descriptor;
898     char *o_save;
899     char *buf, *buf_save;
900     size_t buflen;
901     
902     lazy_initialize_conv();
903     
904     descriptor = conv_handles[CH_UCS2][to_set];
905     
906     if (descriptor == (atalk_iconv_t)-1 || descriptor == (atalk_iconv_t)0) {
907         return (size_t) -1;
908     }
909     
910     i_len=srclen;
911     o_len=destlen;
912     o_save=outbuf;
913     
914     if ( SVAL(inbuf,0) == 0x002e && flags && (*flags & CONV_ESCAPEDOTS)) { /* 0x002e = . */
915         if (o_len < 3) {
916             errno = E2BIG;
917             return (size_t) -1;
918         }
919         o_save[0] = ':';
920         o_save[1] = '2';
921         o_save[2] = 'e';
922         o_len -= 3;
923         inbuf += 2;
924         i_len -= 2;
925         outbuf = o_save + 3;
926         if (flags) *flags |= CONV_REQESCAPE;
927     }
928         
929 conversion_loop:
930     if ( flags && (*flags & CONV_ESCAPEHEX)) {
931         for ( i = 0; i < i_len; i+=2) {
932             if ( SVAL((inbuf+i),0) == 0x002f) { /* 0x002f = / */
933                 j = i_len - i;
934                 if ( 0 == ( i_len = i))
935                     goto escape_slash;
936                 break;
937             } else if ( SVAL(inbuf+i,0) == 0x003a) { /* 0x003a = : */
938                 errno = EILSEQ;
939                 return (size_t) -1;
940             }
941         }
942     }
943     
944     retval = atalk_iconv(descriptor,  &inbuf, &i_len, &outbuf, &o_len);
945     if (retval==(size_t)-1) {
946         if (errno == EILSEQ && CHECK_FLAGS(flags, CONV_IGNORE)) {
947             *flags |= CONV_REQMANGLE;
948             return destlen -o_len;
949         }
950         else if ( errno == EILSEQ && flags && (*flags & CONV_ESCAPEHEX)) {
951             if (o_len < 3) {
952                 errno = E2BIG;
953                 return (size_t) -1;
954             }
955             if ((size_t) -1 == (buflen = convert_string_allocate_internal(CH_UCS2, cap_set, inbuf, 2, &buf)) ) 
956                 return buflen;
957             buf_save = buf;
958             while (buflen > 0) {
959                 if ( o_len < 3) {
960                         errno = E2BIG;
961                         return (size_t) -1;
962                 }
963                 *outbuf++ = ':';
964                 *outbuf++ = hexdig[ ( *buf & 0xf0 ) >> 4 ];
965                 *outbuf++ = hexdig[ *buf & 0x0f ];
966                 buf++;
967                 buflen--;
968                 o_len -= 3;
969             }
970             SAFE_FREE(buf_save);
971             buflen = 0;
972             i_len -= 2;
973             inbuf += 2;
974             if (flags) *flags |= CONV_REQESCAPE;
975             if ( i_len > 0)
976                 goto conversion_loop;
977         }
978         else
979            return (size_t)(-1); 
980     }
981         
982 escape_slash:
983     if (j && flags && (*flags & CONV_ESCAPEHEX)) {
984         if (o_len < 3) {
985             errno = E2BIG;
986             return (size_t) -1;
987         }
988         o_save[destlen -o_len]   = ':';
989         o_save[destlen -o_len+1] = '2';
990         o_save[destlen -o_len+2] = 'f';
991         inbuf  += 2;
992         i_len   = j-2;
993         o_len  -= 3;
994         outbuf += 3;
995         j = 0;
996         if ( i_len > 0)
997                 goto conversion_loop;
998     }
999     return destlen -o_len;
1000 }
1001
1002 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)
1003 {
1004         size_t i_len, o_len;
1005         ucs2_t *u;
1006         ucs2_t buffer[MAXPATHLEN];
1007         ucs2_t buffer2[MAXPATHLEN];
1008         int composition = 0;
1009         
1010         lazy_initialize_conv();
1011
1012         /* convert from_set to UCS2 */
1013         if ((size_t)(-1) == ( o_len = pull_charset_flags( from_set, cap_charset, src, src_len, 
1014                                                           (char *) buffer, sizeof(buffer), flags)) ) {
1015                 LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from_set));
1016                 return (size_t) -1;
1017         }
1018
1019         if ( o_len == 0)
1020                 return o_len;
1021
1022         /* Do pre/decomposition */
1023         if (CHECK_FLAGS(flags, CONV_PRECOMPOSE) || 
1024                 ((!(charsets[to_set])   || !(charsets[to_set]->flags & CHARSET_DECOMPOSED)) && 
1025                 (!(charsets[from_set]) || (charsets[from_set]->flags & CHARSET_DECOMPOSED))))
1026             composition = 1;
1027         if (CHECK_FLAGS(flags, CONV_DECOMPOSE) || (charsets[to_set] && charsets[to_set]->flags & CHARSET_DECOMPOSED) )
1028             composition = 2;
1029  
1030         i_len = sizeof(buffer2);
1031         u = buffer2;
1032
1033         switch (composition) {
1034         case 0:
1035             u = buffer;
1036             i_len = o_len;
1037             break;
1038         case 1:
1039             if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
1040                 return (size_t)(-1);
1041             break;
1042         case 2:
1043             if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
1044                 return (size_t)(-1);
1045             break;
1046         }
1047                 
1048         /* Do case conversions */       
1049         if (CHECK_FLAGS(flags, CONV_TOUPPER)) {
1050             strupper_w(u);
1051         }
1052         if (CHECK_FLAGS(flags, CONV_TOLOWER)) {
1053             strlower_w(u);
1054         }
1055
1056         /* Convert UCS2 to to_set */
1057         if ((size_t)(-1) == ( o_len = push_charset_flags( to_set, cap_charset, (char *)u, i_len, dest, dest_len, flags )) ) {
1058                 LOG(log_error, logtype_default, 
1059                        "Conversion failed (CH_UCS2 to %s):%s", charset_name(to_set), strerror(errno));
1060                 return (size_t) -1;
1061         }
1062
1063         return o_len;
1064 }