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