848c7d2
From ca0212ba19b64488b9e8459a762c11ecd6e7d0bd Mon Sep 17 00:00:00 2001
848c7d2
From: Petr Stodulka <pstodulk@redhat.com>
848c7d2
Date: Tue, 24 Nov 2015 17:56:11 +0100
848c7d2
Subject: [PATCH] print correctly non-ascii filenames
848c7d2
848c7d2
---
848c7d2
 extract.c | 289 ++++++++++++++++++++++++++++++++++++++++++++++++--------------
848c7d2
 unzpriv.h |   7 ++
848c7d2
 2 files changed, 233 insertions(+), 63 deletions(-)
848c7d2
848c7d2
diff --git a/extract.c b/extract.c
848c7d2
index 0ee4e93..741b7e0 100644
848c7d2
--- a/extract.c
848c7d2
+++ b/extract.c
848c7d2
@@ -2648,8 +2648,21 @@ static void set_deferred_symlink(__G__ slnk_entry)
848c7d2
 } /* end function set_deferred_symlink() */
848c7d2
 #endif /* SYMLINKS */
848c7d2
 
848c7d2
+/*
848c7d2
+ * If Unicode is supported, assume we have what we need to do this
848c7d2
+ * check using wide characters, avoiding MBCS issues.
848c7d2
+ */
848c7d2
 
848c7d2
-
848c7d2
+#ifndef UZ_FNFILTER_REPLACECHAR
848c7d2
+        /* A convenient choice for the replacement of unprintable char codes is
848c7d2
+         * the "single char wildcard", as this character is quite unlikely to
848c7d2
+         * appear in filenames by itself.  The following default definition
848c7d2
+         * sets the replacement char to a question mark as the most common
848c7d2
+         * "single char wildcard"; this setting should be overridden in the
848c7d2
+         * appropiate system-specific configuration header when needed.
848c7d2
+         */
848c7d2
+# define UZ_FNFILTER_REPLACECHAR      '?'
848c7d2
+#endif
848c7d2
 
848c7d2
 /*************************/
848c7d2
 /*  Function fnfilter()  */        /* here instead of in list.c for SFX */
848c7d2
@@ -2661,48 +2674,168 @@ char *fnfilter(raw, space, size)   /* convert name to safely printable form */
848c7d2
     extent size;
848c7d2
 {
848c7d2
 #ifndef NATIVE   /* ASCII:  filter ANSI escape codes, etc. */
848c7d2
-    ZCONST uch *r=(ZCONST uch *)raw;
848c7d2
+    ZCONST uch *r; // =(ZCONST uch *)raw;
848c7d2
     uch *s=space;
848c7d2
     uch *slim=NULL;
848c7d2
     uch *se=NULL;
848c7d2
     int have_overflow = FALSE;
848c7d2
 
848c7d2
-    if (size > 0) {
848c7d2
-        slim = space + size
848c7d2
-#ifdef _MBCS
848c7d2
-                     - (MB_CUR_MAX - 1)
848c7d2
-#endif
848c7d2
-                     - 4;
848c7d2
+# if defined( UNICODE_SUPPORT) && defined( _MBCS)
848c7d2
+/* If Unicode support is enabled, and we have multi-byte characters,
848c7d2
+ * then do the isprint() checks by first converting to wide characters
848c7d2
+ * and checking those.  This avoids our having to parse multi-byte
848c7d2
+ * characters for ourselves.  After the wide-char replacements have been
848c7d2
+ * made, the wide string is converted back to the local character set.
848c7d2
+ */
848c7d2
+    wchar_t *wstring;    /* wchar_t version of raw */
848c7d2
+    size_t wslen;        /* length of wstring */
848c7d2
+    wchar_t *wostring;   /* wchar_t version of output string */
848c7d2
+    size_t woslen;       /* length of wostring */
848c7d2
+    char *newraw;        /* new raw */
848c7d2
+
848c7d2
+    /* 2012-11-06 SMS.
848c7d2
+     * Changed to check the value returned by mbstowcs(), and bypass the
848c7d2
+     * Unicode processing if it fails.  This seems to fix a problem
848c7d2
+     * reported in the SourceForge forum, but it's not clear that we
848c7d2
+     * should be doing any Unicode processing without some evidence that
848c7d2
+     * the name actually is Unicode.  (Check bit 11 in the flags before
848c7d2
+     * coming here?)
848c7d2
+     * http://sourceforge.net/p/infozip/bugs/40/
848c7d2
+     */
848c7d2
+
848c7d2
+    if (MB_CUR_MAX <= 1)
848c7d2
+    {
848c7d2
+        /* There's no point to converting multi-byte chars if there are
848c7d2
+         * no multi-byte chars.
848c7d2
+         */
848c7d2
+        wslen = (size_t)-1;
848c7d2
     }
848c7d2
-    while (*r) {
848c7d2
-        if (size > 0 && s >= slim && se == NULL) {
848c7d2
-            se = s;
848c7d2
+    else
848c7d2
+    {
848c7d2
+        /* Get Unicode wide character count (for storage allocation). */
848c7d2
+        wslen = mbstowcs( NULL, raw, 0);
848c7d2
+    }
848c7d2
+
848c7d2
+    if (wslen != (size_t)-1)
848c7d2
+    {
848c7d2
+        /* Apparently valid Unicode.  Allocate wide-char storage. */
85a1bbc
+        wstring = (wchar_t *)malloc((wslen + 1) * sizeof(wchar_t));
848c7d2
+        if (wstring == NULL) {
848c7d2
+            strcpy( (char *)space, raw);
848c7d2
+            return (char *)space;
848c7d2
         }
848c7d2
-#ifdef QDOS
848c7d2
-        if (qlflag & 2) {
848c7d2
-            if (*r == '/' || *r == '.') {
85a1bbc
+        wostring = (wchar_t *)malloc(2 * (wslen + 1) * sizeof(wchar_t));
848c7d2
+        if (wostring == NULL) {
85a1bbc
+            free(wstring);
848c7d2
+            strcpy( (char *)space, raw);
848c7d2
+            return (char *)space;
848c7d2
+        }
848c7d2
+
848c7d2
+        /* Convert the multi-byte Unicode to wide chars. */
848c7d2
+        wslen = mbstowcs(wstring, raw, wslen + 1);
848c7d2
+
848c7d2
+        /* Filter the wide-character string. */
848c7d2
+        fnfilterw( wstring, wostring, (2 * (wslen + 1) * sizeof(wchar_t)));
848c7d2
+
848c7d2
+        /* Convert filtered wide chars back to multi-byte. */
848c7d2
+        woslen = wcstombs( NULL, wostring, 0);
85a1bbc
+        if ((newraw = malloc(woslen + 1)) == NULL) {
85a1bbc
+            free(wstring);
85a1bbc
+            free(wostring);
848c7d2
+            strcpy( (char *)space, raw);
848c7d2
+            return (char *)space;
848c7d2
+        }
848c7d2
+        woslen = wcstombs( newraw, wostring, (woslen * MB_CUR_MAX) + 1);
848c7d2
+
848c7d2
+        if (size > 0) {
848c7d2
+            slim = space + size - 4;
848c7d2
+        }
848c7d2
+        r = (ZCONST uch *)newraw;
848c7d2
+        while (*r) {
848c7d2
+            if (size > 0 && s >= slim && se == NULL) {
848c7d2
+                se = s;
848c7d2
+            }
848c7d2
+#  ifdef QDOS
848c7d2
+            if (qlflag & 2) {
848c7d2
+                if (*r == '/' || *r == '.') {
848c7d2
+                    if (se != NULL && (s > (space + (size-3)))) {
848c7d2
+                        have_overflow = TRUE;
848c7d2
+                        break;
848c7d2
+                    }
848c7d2
+                    ++r;
848c7d2
+                    *s++ = '_';
848c7d2
+                    continue;
848c7d2
+                }
848c7d2
+            } else
848c7d2
+#  endif
848c7d2
+            {
848c7d2
                 if (se != NULL && (s > (space + (size-3)))) {
848c7d2
                     have_overflow = TRUE;
848c7d2
                     break;
848c7d2
                 }
848c7d2
-                ++r;
848c7d2
-                *s++ = '_';
848c7d2
-                continue;
848c7d2
+                *s++ = *r++;
848c7d2
             }
848c7d2
-        } else
848c7d2
+        }
848c7d2
+        if (have_overflow) {
848c7d2
+            strcpy((char *)se, "...");
848c7d2
+        } else {
848c7d2
+            *s = '\0';
848c7d2
+        }
848c7d2
+
85a1bbc
+        free(wstring);
85a1bbc
+        free(wostring);
85a1bbc
+        free(newraw);
848c7d2
+    }
848c7d2
+    else
848c7d2
+# endif /* defined( UNICODE_SUPPORT) && defined( _MBCS) */
848c7d2
+    {
848c7d2
+        /* No Unicode support, or apparently invalid Unicode. */
848c7d2
+        r = (ZCONST uch *)raw;
848c7d2
+
848c7d2
+        if (size > 0) {
848c7d2
+            slim = space + size
848c7d2
+#ifdef _MBCS
848c7d2
+                         - (MB_CUR_MAX - 1)
848c7d2
+#endif
848c7d2
+                         - 4;
848c7d2
+        }
848c7d2
+        while (*r) {
848c7d2
+            if (size > 0 && s >= slim && se == NULL) {
848c7d2
+                se = s;
848c7d2
+            }
848c7d2
+#ifdef QDOS
848c7d2
+            if (qlflag & 2) {
848c7d2
+                if (*r == '/' || *r == '.') {
848c7d2
+                    if (se != NULL && (s > (space + (size-3)))) {
848c7d2
+                        have_overflow = TRUE;
848c7d2
+                        break;
848c7d2
+                    }
848c7d2
+                    ++r;
848c7d2
+                    *s++ = '_';
848c7d2
+                    continue;
848c7d2
+                }
848c7d2
+            } else
848c7d2
 #endif
848c7d2
 #ifdef HAVE_WORKING_ISPRINT
848c7d2
-# ifndef UZ_FNFILTER_REPLACECHAR
848c7d2
-    /* A convenient choice for the replacement of unprintable char codes is
848c7d2
-     * the "single char wildcard", as this character is quite unlikely to
848c7d2
-     * appear in filenames by itself.  The following default definition
848c7d2
-     * sets the replacement char to a question mark as the most common
848c7d2
-     * "single char wildcard"; this setting should be overridden in the
848c7d2
-     * appropiate system-specific configuration header when needed.
848c7d2
-     */
848c7d2
-#   define UZ_FNFILTER_REPLACECHAR      '?'
848c7d2
-# endif
848c7d2
-        if (!isprint(*r)) {
848c7d2
+            if (!isprint(*r)) {
848c7d2
+                if (*r < 32) {
848c7d2
+                    /* ASCII control codes are escaped as "^{letter}". */
848c7d2
+                    if (se != NULL && (s > (space + (size-4)))) {
848c7d2
+                        have_overflow = TRUE;
848c7d2
+                        break;
848c7d2
+                    }
848c7d2
+                    *s++ = '^', *s++ = (uch)(64 + *r++);
848c7d2
+                } else {
848c7d2
+                    /* Other unprintable codes are replaced by the
848c7d2
+                     * placeholder character. */
848c7d2
+                    if (se != NULL && (s > (space + (size-3)))) {
848c7d2
+                        have_overflow = TRUE;
848c7d2
+                        break;
848c7d2
+                    }
848c7d2
+                    *s++ = UZ_FNFILTER_REPLACECHAR;
848c7d2
+                    INCSTR(r);
848c7d2
+                }
848c7d2
+#else /* !HAVE_WORKING_ISPRINT */
848c7d2
             if (*r < 32) {
848c7d2
                 /* ASCII control codes are escaped as "^{letter}". */
848c7d2
                 if (se != NULL && (s > (space + (size-4)))) {
848c7d2
@@ -2710,47 +2843,30 @@ char *fnfilter(raw, space, size)   /* convert name to safely printable form */
848c7d2
                     break;
848c7d2
                 }
848c7d2
                 *s++ = '^', *s++ = (uch)(64 + *r++);
848c7d2
+#endif /* ?HAVE_WORKING_ISPRINT */
848c7d2
             } else {
848c7d2
-                /* Other unprintable codes are replaced by the
848c7d2
-                 * placeholder character. */
848c7d2
+#ifdef _MBCS
848c7d2
+                unsigned i = CLEN(r);
848c7d2
+                if (se != NULL && (s > (space + (size-i-2)))) {
848c7d2
+                    have_overflow = TRUE;
848c7d2
+                    break;
848c7d2
+                }
848c7d2
+                for (; i > 0; i--)
848c7d2
+                    *s++ = *r++;
848c7d2
+#else
848c7d2
                 if (se != NULL && (s > (space + (size-3)))) {
848c7d2
                     have_overflow = TRUE;
848c7d2
                     break;
848c7d2
                 }
848c7d2
-                *s++ = UZ_FNFILTER_REPLACECHAR;
848c7d2
-                INCSTR(r);
848c7d2
-            }
848c7d2
-#else /* !HAVE_WORKING_ISPRINT */
848c7d2
-        if (*r < 32) {
848c7d2
-            /* ASCII control codes are escaped as "^{letter}". */
848c7d2
-            if (se != NULL && (s > (space + (size-4)))) {
848c7d2
-                have_overflow = TRUE;
848c7d2
-                break;
848c7d2
-            }
848c7d2
-            *s++ = '^', *s++ = (uch)(64 + *r++);
848c7d2
-#endif /* ?HAVE_WORKING_ISPRINT */
848c7d2
-        } else {
848c7d2
-#ifdef _MBCS
848c7d2
-            unsigned i = CLEN(r);
848c7d2
-            if (se != NULL && (s > (space + (size-i-2)))) {
848c7d2
-                have_overflow = TRUE;
848c7d2
-                break;
848c7d2
-            }
848c7d2
-            for (; i > 0; i--)
848c7d2
                 *s++ = *r++;
848c7d2
-#else
848c7d2
-            if (se != NULL && (s > (space + (size-3)))) {
848c7d2
-                have_overflow = TRUE;
848c7d2
-                break;
848c7d2
-            }
848c7d2
-            *s++ = *r++;
848c7d2
 #endif
848c7d2
-         }
848c7d2
-    }
848c7d2
-    if (have_overflow) {
848c7d2
-        strcpy((char *)se, "...");
848c7d2
-    } else {
848c7d2
-        *s = '\0';
848c7d2
+             }
848c7d2
+        }
848c7d2
+        if (have_overflow) {
848c7d2
+            strcpy((char *)se, "...");
848c7d2
+        } else {
848c7d2
+            *s = '\0';
848c7d2
+        }
848c7d2
     }
848c7d2
 
848c7d2
 #ifdef WINDLL
848c7d2
@@ -2772,6 +2888,53 @@ char *fnfilter(raw, space, size)   /* convert name to safely printable form */
848c7d2
 } /* end function fnfilter() */
848c7d2
 
848c7d2
 
848c7d2
+#if defined( UNICODE_SUPPORT) && defined( _MBCS)
848c7d2
+
848c7d2
+/****************************/
848c7d2
+/*  Function fnfilter[w]()  */  /* (Here instead of in list.c for SFX.) */
848c7d2
+/****************************/
848c7d2
+
848c7d2
+/* fnfilterw() - Convert wide name to safely printable form. */
848c7d2
+
848c7d2
+/* fnfilterw() - Convert wide-character name to safely printable form. */
848c7d2
+
848c7d2
+wchar_t *fnfilterw( src, dst, siz)
848c7d2
+    ZCONST wchar_t *src;        /* Pointer to source char (string). */
848c7d2
+    wchar_t *dst;               /* Pointer to destination char (string). */
848c7d2
+    extent siz;                 /* Not used (!). */
848c7d2
+{
848c7d2
+    wchar_t *dsx = dst;
848c7d2
+
848c7d2
+    /* Filter the wide chars. */
848c7d2
+    while (*src)
848c7d2
+    {
848c7d2
+        if (iswprint( *src))
848c7d2
+        {
848c7d2
+            /* Printable code.  Copy it. */
848c7d2
+            *dst++ = *src;
848c7d2
+        }
848c7d2
+        else
848c7d2
+        {
848c7d2
+            /* Unprintable code.  Substitute something printable for it. */
848c7d2
+            if (*src < 32)
848c7d2
+            {
848c7d2
+                /* Replace ASCII control code with "^{letter}". */
848c7d2
+                *dst++ = (wchar_t)'^';
848c7d2
+                *dst++ = (wchar_t)(64 + *src);
848c7d2
+            }
848c7d2
+            else
848c7d2
+            {
848c7d2
+                /* Replace other unprintable code with the placeholder. */
848c7d2
+                *dst++ = (wchar_t)UZ_FNFILTER_REPLACECHAR;
848c7d2
+            }
848c7d2
+        }
848c7d2
+        src++;
848c7d2
+    }
848c7d2
+    *dst = (wchar_t)0;  /* NUL-terminate the destination string. */
848c7d2
+    return dsx;
848c7d2
+} /* fnfilterw(). */
848c7d2
+
848c7d2
+#endif /* defined( UNICODE_SUPPORT) && defined( _MBCS) */
848c7d2
 
848c7d2
 
848c7d2
 #ifdef SET_DIR_ATTRIB
848c7d2
diff --git a/unzpriv.h b/unzpriv.h
848c7d2
index 22d3923..e48a652 100644
848c7d2
--- a/unzpriv.h
848c7d2
+++ b/unzpriv.h
848c7d2
@@ -1212,6 +1212,7 @@
848c7d2
 # ifdef UNICODE_WCHAR
848c7d2
 #  if !(defined(_WIN32_WCE) || defined(POCKET_UNZIP))
848c7d2
 #   include <wchar.h>
848c7d2
+#   include <wctype.h>
848c7d2
 #  endif
848c7d2
 # endif
848c7d2
 # ifndef _MBCS  /* no need to include <locale.h> twice, see below */
848c7d2
@@ -2410,6 +2411,12 @@ int    memflush                  OF((__GPRO__ ZCONST uch *rawbuf, ulg size));
848c7d2
 char  *fnfilter                  OF((ZCONST char *raw, uch *space,
848c7d2
                                      extent size));
848c7d2
 
848c7d2
+# if defined( UNICODE_SUPPORT) && defined( _MBCS)
848c7d2
+wchar_t *fnfilterw               OF((ZCONST wchar_t *src, wchar_t *dst,
848c7d2
+                                     extent siz));
848c7d2
+#endif
848c7d2
+
848c7d2
+
848c7d2
 /*---------------------------------------------------------------------------
848c7d2
     Decompression functions:
848c7d2
   ---------------------------------------------------------------------------*/
848c7d2
-- 
848c7d2
2.4.3
848c7d2