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