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