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