]> arthur.barton.de Git - netatalk.git/blobdiff - libatalk/unicode/charcnv.c
Merge 3.0.2 release branch
[netatalk.git] / libatalk / unicode / charcnv.c
index 785e47720f1d7ab2e74ce2be611c93164386fdb9..4e772e165a16e4f60f0f7dd331d176bf542645da 100644 (file)
@@ -95,6 +95,16 @@ int set_charset_name(charset_t ch, const char *name)
     return 0;
 }
 
+void free_charset_names(void)
+{
+    for (int ch = 0; ch < MAX_CHARSETS; ch++) {
+        if (charset_names[ch]) {
+            free(charset_names[ch]);
+            charset_names[ch] = NULL;
+        }
+    }
+}
+
 static struct charset_functions* get_charset_functions (charset_t ch)
 {
     if (charsets[ch] != NULL)
@@ -693,7 +703,7 @@ 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, uint16_t *flags)
+static size_t pull_charset_flags (charset_t from_set, charset_t to_set, charset_t cap_set, const char *src, size_t srclen, char* dest, size_t destlen, uint16_t *flags)
 {
     const uint16_t option = (flags ? *flags : 0);
     size_t i_len, o_len;
@@ -702,6 +712,7 @@ static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const c
     char* outbuf = dest;
     atalk_iconv_t descriptor;
     atalk_iconv_t descriptor_cap;
+    char escch;                 /* 150210: uninitialized OK, depends on j */
 
     if (srclen == (size_t)-1)
         srclen = strlen(src) + 1;
@@ -717,10 +728,30 @@ static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const c
     i_len=srclen;
     o_len=destlen;
 
+    if ((option & CONV_ESCAPEDOTS) && i_len >= 2 && inbuf[0] == '.') {
+        if (o_len < 6) {
+            errno = E2BIG;
+            goto end;
+        }
+        ucs2_t ucs2 = ':';
+        memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+        ucs2 = '2';
+        memcpy(outbuf + sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+        ucs2 = 'e';
+        memcpy(outbuf + 2 * sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+        outbuf += 6;
+        o_len -= 6;
+        inbuf++;
+        i_len--;
+        *flags |= CONV_REQESCAPE;
+    }
+
     while (i_len > 0) {
         for (j = 0; j < i_len; ++j)
-            if (inbuf[j] == ':')
+            if (inbuf[j] == ':' || inbuf[j] == '/') {
+                escch = inbuf[j];
                 break;
+            }
         j = i_len - j;
         i_len -= j;
 
@@ -750,48 +781,108 @@ static size_t pull_charset_flags (charset_t from_set, charset_t cap_set, const c
         }
 
         if (j) {
-            /* we have a ':' */
+            /* we have a ':' or '/' */
             i_len = j, j = 0;
 
-            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)) {
+            if (escch == ':') {
+                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;
+                } else if (option & CONV_ESCAPEHEX) {
+                    if (o_len < 6) {
+                        errno = E2BIG;
+                        goto end;
                     }
-                    goto end;
+                    ucs2_t ucs2 = ':';
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    ucs2 = '3';
+                    memcpy(outbuf + sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+                    ucs2 = 'a';
+                    memcpy(outbuf + 2 * sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+                    outbuf += 6;
+                    o_len -= 6;
+                    inbuf++;
+                    i_len--;
+                } else if (to_set == CH_UTF8_MAC || to_set == CH_MAC) {
+                    /* convert to a '/' */
+                    ucs2_t slash = 0x002f;
+                    memcpy(outbuf, &slash, sizeof(ucs2_t));
+                    outbuf += 2;
+                    o_len -= 2;
+                    inbuf++;
+                    i_len--;
+                } else {
+                    /* keep as ':' */
+                    ucs2_t ucs2 = 0x003a;
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    outbuf += 2;
+                    o_len -= 2;
+                    inbuf++;
+                    i_len--;
                 }
             } else {
-                /* 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--;
+                /* '/' */
+                if (option & CONV_ESCAPEHEX) {
+                    if (o_len < 6) {
+                        errno = E2BIG;
+                        goto end;
+                    }
+                    ucs2_t ucs2 = ':';
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    ucs2 = '2';
+                    memcpy(outbuf + sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+                    ucs2 = 'f';
+                    memcpy(outbuf + 2 * sizeof(ucs2_t), &ucs2, sizeof(ucs2_t));
+                    outbuf += 6;
+                    o_len -= 6;
+                    inbuf++;
+                    i_len--;
+                } else if ((from_set == CH_UTF8_MAC || from_set == CH_MAC)
+                           && (to_set != CH_UTF8_MAC  || to_set != CH_MAC)) {
+                    /* convert to ':' */
+                    ucs2_t ucs2 = 0x003a;
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    outbuf += 2;
+                    o_len -= 2;
+                    inbuf++;
+                    i_len--;
+                } else {
+                    /* keep as '/' */
+                    ucs2_t ucs2 = 0x002f;
+                    memcpy(outbuf, &ucs2, sizeof(ucs2_t));
+                    outbuf += 2;
+                    o_len -= 2;
+                    inbuf++;
+                    i_len--;
+                }
             }
         }
     }
@@ -824,7 +915,6 @@ static size_t push_charset_flags (charset_t to_set, charset_t cap_set, char* src
     char* outbuf = (char*)dest;
     atalk_iconv_t descriptor;
     atalk_iconv_t descriptor_cap;
-    char escch;                 /* 150210: uninitialized OK, depends on j */
 
     descriptor = conv_handles[CH_UCS2][to_set];
     descriptor_cap = conv_handles[CH_UCS2][cap_set];
@@ -837,42 +927,7 @@ static size_t push_charset_flags (charset_t to_set, charset_t cap_set, char* src
     i_len=srclen;
     o_len=destlen;
 
-    if ((option & CONV_ESCAPEDOTS) &&
-        i_len >= 2 && SVAL(inbuf, 0) == 0x002e) { /* 0x002e = . */
-        if (o_len < 3) {
-            errno = E2BIG;
-            goto end;
-        }
-        *outbuf++ = ':';
-        *outbuf++ = '2';
-        *outbuf++ = 'e';
-        o_len -= 3;
-        inbuf += 2;
-        i_len -= 2;
-        *flags |= CONV_REQESCAPE;
-    }
-
     while (i_len >= 2) {
-        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 = '/' */
-                if (option & CONV_ALLOW_SLASH) break;
-                escch = c;
-                j = i_len - i;
-                i_len = i;
-                break;
-            }
-        }
         while (i_len > 0 &&
                atalk_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len) == (size_t)-1) {
             if (errno == EILSEQ) {
@@ -921,57 +976,8 @@ static size_t push_charset_flags (charset_t to_set, charset_t cap_set, char* src
             }
             goto end;
         }
+    } /* while (i_len >= 2) */
 
-        if (j) {
-            /* we have a ':' or '/' */
-            i_len = j, j = 0;
-
-            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;
-            }
-        }
-    }
     if (i_len > 0) errno = EINVAL;
 end:
     return (i_len + j == 0 || (option & CONV_FORCE)) ? destlen - o_len : (size_t)-1;
@@ -991,7 +997,7 @@ size_t convert_charset ( charset_t from_set, charset_t to_set, charset_t cap_cha
     lazy_initialize_conv();
 
     /* convert from_set to UCS2 */
-    if ((size_t)(-1) == ( o_len = pull_charset_flags( from_set, cap_charset, src, src_len,
+    if ((size_t)(-1) == ( o_len = pull_charset_flags( from_set, to_set, cap_charset, src, src_len,
                                                       (char *) buffer, sizeof(buffer) -2, flags)) ) {
         LOG(log_error, logtype_default, "Conversion failed ( %s to CH_UCS2 )", charset_name(from_set));
         return (size_t) -1;