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