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