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