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