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