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