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