]> arthur.barton.de Git - netatalk.git/blobdiff - libatalk/unicode/charcnv.c
New default character conversion wrt NOT hexencoding '/'
[netatalk.git] / libatalk / unicode / charcnv.c
index d592f9eb3ef838b285a46cb111d2f776c191fdfe..455a72622ef9fb0f2aa7543aec6bfe6feb651c57 100644 (file)
 #if HAVE_LANGINFO_H
 #include <langinfo.h>
 #endif
+#include <arpa/inet.h>
 
-#include <netatalk/endian.h>
 #include <atalk/logger.h>
 #include <atalk/unicode.h>
 #include <atalk/util.h>
+#include <atalk/compat.h>
+
 #include "byteorder.h"
 
 
@@ -76,29 +78,6 @@ static struct charset_functions* charsets[MAX_CHARSETS];
 static char hexdig[] = "0123456789abcdef";
 #define hextoint( c )   ( isdigit( c ) ? c - '0' : c + 10 - 'a' )
 
-static char* read_charsets_from_env(charset_t ch)
-{
-    char *name;
-
-    switch (ch) {
-    case CH_MAC:
-        if (( name = getenv( "ATALK_MAC_CHARSET" )) != NULL )
-            return name;
-        else
-            return "MAC_ROMAN";
-        break;
-    case CH_UNIX:
-        if (( name = getenv( "ATALK_UNIX_CHARSET" )) != NULL )
-            return name;
-        else
-            return "LOCALE";
-        break;
-    default:
-        break;
-    }
-    return "ASCII";
-}
-
 
 /**
  * Return the name of a charset to give to iconv().
@@ -121,7 +100,7 @@ static const char *charset_name(charset_t ch)
     else if (ch == CH_UTF8_MAC) ret = "UTF8-MAC";
     else if (ch == CH_UNIX) {
         if (unixname[0] == '\0') {
-            ret = read_charsets_from_env(CH_UNIX);
+            ret = "LOCALE";
             strlcpy(unixname, ret, sizeof(unixname));
         }
         else
@@ -129,7 +108,7 @@ static const char *charset_name(charset_t ch)
     }
     else if (ch == CH_MAC) {
         if (macname[0] == '\0') {
-            ret = read_charsets_from_env(CH_MAC);
+            ret = "MAC_ROMAN";
             strlcpy(macname, ret, sizeof(macname));
         }
         else
@@ -139,20 +118,19 @@ static const char *charset_name(charset_t ch)
     if (!ret)
         ret = charset_names[ch];
 
-#if defined(HAVE_NL_LANGINFO) && defined(CODESET)
+#if defined(CODESET)
     if (ret && strcasecmp(ret, "LOCALE") == 0) {
         const char *ln = NULL;
 
-#ifdef HAVE_SETLOCALE
         setlocale(LC_ALL, "");
-#endif
         ln = nl_langinfo(CODESET);
         if (ln) {
             /* Check whether the charset name is supported
                by iconv */
+            LOG(log_debug, logtype_default, "Locale charset is '%s'", ln);
             atalk_iconv_t handle = atalk_iconv_open(ln, "UCS-2");
             if (handle == (atalk_iconv_t) -1) {
-                LOG(log_debug, logtype_default, "Locale charset '%s' unsupported, using ASCII instead", ln);
+                LOG(log_warning, logtype_default, "Locale charset '%s' unsupported, using ASCII instead", ln);
                 ln = "ASCII";
             } else {
                 atalk_iconv_close(handle);
@@ -163,6 +141,7 @@ static const char *charset_name(charset_t ch)
         ret = ln;
     }
 #else /* system doesn't have LOCALE support */
+    LOG(log_warning, logtype_default, "system doesn't have LOCALE support");
     if (ch == CH_UNIX) ret = NULL;
 #endif
 
@@ -191,7 +170,7 @@ static void lazy_initialize_conv(void)
     }
 }
 
-charset_t add_charset(char* name)
+charset_t add_charset(const char* name)
 {
     static charset_t max_charset_t = NUM_CHARSETS-1;
     charset_t cur_charset_t = max_charset_t+1;
@@ -367,42 +346,29 @@ size_t convert_string(charset_t from, charset_t to,
     ucs2_t *u;
     ucs2_t buffer[MAXPATHLEN];
     ucs2_t buffer2[MAXPATHLEN];
-    int composition = 0;
-
-    lazy_initialize_conv();
 
     /* convert from_set to UCS2 */
-    if ((size_t)(-1) == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen,
+    if ((size_t)-1 == ( o_len = convert_string_internal( from, CH_UCS2, src, srclen,
                                                            (char*) buffer, sizeof(buffer))) ) {
         LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from));
         return (size_t) -1;
     }
 
     /* Do pre/decomposition */
-    if ( ((!(charsets[to])   || !(charsets[to]->flags & CHARSET_DECOMPOSED)) &&
-          (!(charsets[from]) || (charsets[from]->flags & CHARSET_DECOMPOSED))))
-        composition = 1;
-    if ((charsets[to] && charsets[to]->flags & CHARSET_DECOMPOSED) )
-        composition = 2;
-
     i_len = sizeof(buffer2);
     u = buffer2;
-
-    switch (composition) {
-    case 0:
+    if (charsets[to] && (charsets[to]->flags & CHARSET_DECOMPOSED) ) {
+        if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
+            return (size_t)-1;
+    }
+    else if (!charsets[from] || (charsets[from]->flags & CHARSET_DECOMPOSED)) {
+        if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
+            return (size_t)-1;
+    }
+    else {
         u = buffer;
         i_len = o_len;
-        break;
-    case 1:
-        if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
-            return (size_t)(-1);
-        break;
-    case 2:
-        if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
-            return (size_t)(-1);
-        break;
     }
-
     /* Convert UCS2 to to_set */
     if ((size_t)(-1) == ( o_len = convert_string_internal( CH_UCS2, to, (char*) u, i_len, dest, destlen)) ) {
         LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
@@ -517,9 +483,6 @@ size_t convert_string_allocate(charset_t from, charset_t to,
     ucs2_t *u;
     ucs2_t buffer[MAXPATHLEN];
     ucs2_t buffer2[MAXPATHLEN];
-    int composition = 0;
-
-    lazy_initialize_conv();
 
     *dest = NULL;
 
@@ -531,32 +494,23 @@ size_t convert_string_allocate(charset_t from, charset_t to,
     }
 
     /* Do pre/decomposition */
-    if ( ((!(charsets[to])   || !(charsets[to]->flags & CHARSET_DECOMPOSED)) &&
-          (!(charsets[from]) || (charsets[from]->flags & CHARSET_DECOMPOSED))))
-        composition = 1;
-    if ((charsets[to] && charsets[to]->flags & CHARSET_DECOMPOSED) )
-        composition = 2;
-
     i_len = sizeof(buffer2);
     u = buffer2;
-
-    switch (composition) {
-    case 0:
+    if (charsets[to] && (charsets[to]->flags & CHARSET_DECOMPOSED) ) {
+        if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
+            return (size_t)-1;
+    }
+    else if ( !charsets[from] || (charsets[from]->flags & CHARSET_DECOMPOSED) ) {
+        if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
+            return (size_t)-1;
+    }
+    else {
         u = buffer;
         i_len = o_len;
-        break;
-    case 1:
-        if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
-            return (size_t)(-1);
-        break;
-    case 2:
-        if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
-            return (size_t)(-1);
-        break;
     }
 
     /* Convert UCS2 to to_set */
-    if ((size_t)(-1) == ( o_len = convert_string_allocate_internal( CH_UCS2, to, (char*)u, i_len, dest)) )
+    if ((size_t)-1 == ( o_len = convert_string_allocate_internal( CH_UCS2, to, (char*)u, i_len, dest)) )
         LOG(log_error, logtype_default, "Conversion failed (CH_UCS2 to %s):%s", charset_name(to), strerror(errno));
 
     return o_len;
@@ -793,9 +747,9 @@ char * debug_out ( char * seq, size_t len)
  *      for e.g. HFS cdroms.
  */
 
-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)
+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)
 {
-    const u_int16_t option = (flags ? *flags : 0);
+    const uint16_t option = (flags ? *flags : 0);
     size_t i_len, o_len;
     size_t j = 0;
     const char* inbuf = (const char*)src;
@@ -806,8 +760,6 @@ static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const c
     if (srclen == (size_t)-1)
         srclen = strlen(src) + 1;
 
-    lazy_initialize_conv();
-
     descriptor = conv_handles[from_set][CH_UCS2];
     descriptor_cap = conv_handles[cap_set][CH_UCS2];
 
@@ -820,13 +772,11 @@ static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const c
     o_len=destlen;
 
     while (i_len > 0) {
-        if ((option & CONV_UNESCAPEHEX)) {
-            for (j = 0; j < i_len; ++j) {
-                if (inbuf[j] == ':') break;
-            }
-            j = i_len - j;
-            i_len -= j;
-        }
+        for (j = 0; j < i_len; ++j)
+            if (inbuf[j] == ':')
+                break;
+        j = i_len - j;
+        i_len -= j;
 
         if (i_len > 0 &&
             atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len) == (size_t)-1) {
@@ -854,36 +804,48 @@ static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const c
         }
 
         if (j) {
-            /* we're at the start on an hex encoded ucs2 char */
-            char h[MAXPATHLEN];
-            size_t hlen = 0;
-
+            /* we have a ':' */
             i_len = j, j = 0;
-            while (i_len >= 3 && inbuf[0] == ':' &&
-                   isxdigit(inbuf[1]) && isxdigit(inbuf[2])) {
-                h[hlen++] = (hextoint(inbuf[1]) << 4) | hextoint(inbuf[2]);
-                inbuf += 3;
-                i_len -= 3;
-            }
-            if (hlen) {
-                const char *h_buf = h;
-                if (atalk_iconv(descriptor_cap, &h_buf, &hlen, &outbuf, &o_len) == (size_t)-1) {
-                    i_len += hlen * 3;
-                    inbuf -= hlen * 3;
-                    if (errno == EILSEQ && (option & CONV_IGNORE)) {
+
+            if ((option & CONV_UNESCAPEHEX)) {
+                /* treat it as a CAP hex encoded char */
+                char h[MAXPATHLEN];
+                size_t hlen = 0;
+
+                while (i_len >= 3 && inbuf[0] == ':' &&
+                       isxdigit(inbuf[1]) && isxdigit(inbuf[2])) {
+                    h[hlen++] = (hextoint(inbuf[1]) << 4) | hextoint(inbuf[2]);
+                    inbuf += 3;
+                    i_len -= 3;
+                }
+                if (hlen) {
+                    const char *h_buf = h;
+                    if (atalk_iconv(descriptor_cap, &h_buf, &hlen, &outbuf, &o_len) == (size_t)-1) {
+                        i_len += hlen * 3;
+                        inbuf -= hlen * 3;
+                        if (errno == EILSEQ && (option & CONV_IGNORE)) {
+                            *flags |= CONV_REQMANGLE;
+                            return destlen - o_len;
+                        }
+                        goto end;
+                    }
+                } else {
+                    /* We have an invalid :xx sequence */
+                    errno = EILSEQ;
+                    if ((option & CONV_IGNORE)) {
                         *flags |= CONV_REQMANGLE;
                         return destlen - o_len;
                     }
                     goto end;
                 }
             } else {
-                /* We have an invalid :xx sequence */
-                errno = EILSEQ;
-                if ((option & CONV_IGNORE)) {
-                    *flags |= CONV_REQMANGLE;
-                    return destlen - o_len;
-                }
-                goto end;
+                /* a ':' that we just convert to a '/' */
+                ucs2_t slash = 0x002f;
+                memcpy(outbuf, &slash, sizeof(ucs2_t));
+                outbuf += 2;
+                o_len -= 2;
+                inbuf++;
+                i_len--;
             }
         }
     }
@@ -907,17 +869,16 @@ end:
  */
 
 
-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)
+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)
 {
-    const u_int16_t option = (flags ? *flags : 0);
+    const uint16_t option = (flags ? *flags : 0);
     size_t i_len, o_len, i;
     size_t j = 0;
     const char* inbuf = (const char*)src;
     char* outbuf = (char*)dest;
     atalk_iconv_t descriptor;
     atalk_iconv_t descriptor_cap;
-
-    lazy_initialize_conv();
+    char escch;                 /* 150210: uninitialized OK, depends on j */
 
     descriptor = conv_handles[CH_UCS2][to_set];
     descriptor_cap = conv_handles[CH_UCS2][cap_set];
@@ -946,17 +907,23 @@ static size_t push_charset_flags (charset_t to_set, charset_t cap_set, char* src
     }
 
     while (i_len >= 2) {
-        if ((option & CONV_ESCAPEHEX)) {
-            for (i = 0; i < i_len; i += 2) {
-                ucs2_t c = SVAL(inbuf, i);
-                if (c == 0x002f) { /* 0x002f = / */
-                    j = i_len - i;
-                    i_len = i;
-                    break;
-                } else if (c == 0x003a) { /* 0x003a = : */
+        for (i = 0; i < i_len; i += 2) {
+            ucs2_t c = SVAL(inbuf, i);
+            switch (c) {
+            case 0x003a: /* 0x003a = ':' */
+                if ( ! (option & CONV_ALLOW_COLON)) {
                     errno = EILSEQ;
                     goto end;
                 }
+                escch = c;
+                j = i_len - i;
+                i_len = i;
+                break;
+            case 0x002f: /* 0x002f = '/' */
+                escch = c;
+                j = i_len - i;
+                i_len = i;
+                break;
             }
         }
         while (i_len > 0 &&
@@ -1009,17 +976,53 @@ static size_t push_charset_flags (charset_t to_set, charset_t cap_set, char* src
         }
 
         if (j) {
+            /* we have a ':' or '/' */
             i_len = j, j = 0;
-            if (o_len < 3) {
-                errno = E2BIG;
-                goto end;
+
+            if ((option & CONV_ESCAPEHEX)) {
+                /* CAP hex encode it */
+                if (o_len < 3) {
+                    errno = E2BIG;
+                    goto end;
+                }
+                switch (escch) {
+                case '/':
+                    *outbuf++ = ':';
+                    *outbuf++ = '2';
+                    *outbuf++ = 'f';
+                    break;
+                case ':':
+                    *outbuf++ = ':';
+                    *outbuf++ = '3';
+                    *outbuf++ = 'a';
+                    break;
+                default:
+                    /*
+                     *  THIS SHOULD NEVER BE REACHED !!!
+                     *  As a safety net I put in a ' ' here
+                     */
+                    *outbuf++ = ':';
+                    *outbuf++ = '2';
+                    *outbuf++ = '0';
+                    break;
+                }
+                o_len -= 3;
+                inbuf += 2;
+                i_len -= 2;
+            } else {
+                switch (escch) {
+                case '/':
+                case ':':
+                    *outbuf++ = ':';
+                    break;
+                default: /* should never be reached */
+                    *outbuf++ = ' ';
+                    break;
+                }
+                o_len--;
+                inbuf += 2;
+                i_len -= 2;
             }
-            *outbuf++ = ':';
-            *outbuf++ = '2';
-            *outbuf++ = 'f';
-            o_len -= 3;
-            inbuf += 2;
-            i_len -= 2;
         }
     }
     if (i_len > 0) errno = EINVAL;
@@ -1031,13 +1034,12 @@ end:
  * FIXME the size is a mess we really need a malloc/free logic
  *`dest size must be dest_len +2
  */
-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)
+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)
 {
     size_t i_len, o_len;
     ucs2_t *u;
     ucs2_t buffer[MAXPATHLEN +2];
     ucs2_t buffer2[MAXPATHLEN +2];
-    int composition = 0;
 
     lazy_initialize_conv();
 
@@ -1052,29 +1054,19 @@ size_t convert_charset ( charset_t from_set, charset_t to_set, charset_t cap_cha
         return o_len;
 
     /* Do pre/decomposition */
-    if (CHECK_FLAGS(flags, CONV_PRECOMPOSE) ||
-        ((!(charsets[to_set])   || !(charsets[to_set]->flags & CHARSET_DECOMPOSED)) &&
-         (!(charsets[from_set]) || (charsets[from_set]->flags & CHARSET_DECOMPOSED))))
-        composition = 1;
-    if (CHECK_FLAGS(flags, CONV_DECOMPOSE) || (charsets[to_set] && charsets[to_set]->flags & CHARSET_DECOMPOSED) )
-        composition = 2;
-
     i_len = sizeof(buffer2) -2;
     u = buffer2;
-
-    switch (composition) {
-    case 0:
-        u = buffer;
-        i_len = o_len;
-        break;
-    case 1:
-        if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
-            return (size_t)(-1);
-        break;
-    case 2:
+    if (CHECK_FLAGS(flags, CONV_DECOMPOSE) || (charsets[to_set] && (charsets[to_set]->flags & CHARSET_DECOMPOSED)) ) {
         if ( (size_t)-1 == (i_len = decompose_w(buffer, o_len, u, &i_len)) )
             return (size_t)(-1);
-        break;
+    }
+    else if (CHECK_FLAGS(flags, CONV_PRECOMPOSE) || !charsets[from_set] || (charsets[from_set]->flags & CHARSET_DECOMPOSED)) {
+        if ( (size_t)-1 == (i_len = precompose_w(buffer, o_len, u, &i_len)) )
+            return (size_t)(-1);
+    }
+    else {
+        u = buffer;
+        i_len = o_len;
     }
     /* null terminate */
     u[i_len] = 0;
@@ -1084,7 +1076,7 @@ size_t convert_charset ( charset_t from_set, charset_t to_set, charset_t cap_cha
     if (CHECK_FLAGS(flags, CONV_TOUPPER)) {
         strupper_w(u);
     }
-    if (CHECK_FLAGS(flags, CONV_TOLOWER)) {
+    else if (CHECK_FLAGS(flags, CONV_TOLOWER)) {
         strlower_w(u);
     }