]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Fix the automatic filename translation on Windows and add a test for it.
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Sun, 10 Apr 2011 07:16:21 +0000 (03:16 -0400)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Sun, 10 Apr 2011 07:16:21 +0000 (03:16 -0400)
When reading CP866 filenames in the zip file on Russian Windows platform,
which its ACP is not equal to its OEMCP, We should automatically translate
CP866(OEMCP of Russian) filenames to CP1251(ACP of Russian) filenames because
other archiver application on Windows have stored the filenames in CP866
through WideCharToMultiByte() with CP_OEMCP.

SVN-Revision: 3192

libarchive/archive_string.c
libarchive/test/test_zip_filename_encoding.c

index 333cdf13fde6e1b7f087a54e6b8e24bb6282f644..3dd06a757c3a28432e0c30311ae815796361ddcc 100644 (file)
@@ -1075,11 +1075,20 @@ struct archive_string_conv *
 archive_string_default_conversion_for_read(struct archive *a)
 {
        const char *cur_charset = get_current_charset(a);
-
-       if (a->current_codepage == CP_C_LOCALE ||
-           a->current_codepage == a->current_oemcp)
+       char oemcp[16];
+
+       /* NOTE: a check of cur_charset is unneeded but we need
+        * that get_current_charset() has been surely called at
+        * this time whatever C compiler optimized. */
+       if (cur_charset != NULL &&
+           (a->current_codepage == CP_C_LOCALE ||
+            a->current_codepage == a->current_oemcp))
                return (NULL);/* no conversion. */
-       return (get_sconv_object(a, "CP_OEMCP", cur_charset,
+
+       _snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);
+       /* Make sure a null termination must be set. */
+       oemcp[sizeof(oemcp)-1] = '\0';
+       return (get_sconv_object(a, oemcp, cur_charset,
            SCONV_FROM_CHARSET));
 }
 
@@ -1087,11 +1096,20 @@ struct archive_string_conv *
 archive_string_default_conversion_for_write(struct archive *a)
 {
        const char *cur_charset = get_current_charset(a);
-
-       if (a->current_codepage == CP_C_LOCALE ||
-           a->current_codepage == a->current_oemcp)
+       char oemcp[16];
+
+       /* NOTE: a check of cur_charset is unneeded but we need
+        * that get_current_charset() has been surely called at
+        * this time whatever C compiler optimized. */
+       if (cur_charset != NULL &&
+           (a->current_codepage == CP_C_LOCALE ||
+            a->current_codepage == a->current_oemcp))
                return (NULL);/* no conversion. */
-       return (get_sconv_object(a, "CP_OEMCP", cur_charset,
+
+       _snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);
+       /* Make sure a null termination must be set. */
+       oemcp[sizeof(oemcp)-1] = '\0';
+       return (get_sconv_object(a, cur_charset, oemcp,
            SCONV_TO_CHARSET));
 }
 #else
index b6fc4e38449d151a2fff5b9ea70d1e59c460837f..71fd896889a78f1ed4b3a3849150fe61ab468594 100644 (file)
@@ -36,7 +36,7 @@ test_zip_filename_encoding_UTF8()
        size_t used;
 
        if (NULL == setlocale(LC_ALL, "ru_RU.UTF-8")) {
-               skipping("KOI8-R locale not available on this system.");
+               skipping("ru_RU.UTF-8 locale not available on this system.");
                return;
        }
 
@@ -56,6 +56,7 @@ test_zip_filename_encoding_UTF8()
            archive_write_open_memory(a, buff, sizeof(buff), &used));
 
        entry = archive_entry_new2(a);
+       /* Set a UTF-8 filename. */
        archive_entry_set_pathname(entry, "\xD0\xBF\xD1\x80\xD0\xB8");
        archive_entry_set_filetype(entry, AE_IFREG);
        archive_entry_set_size(entry, 0);
@@ -66,8 +67,6 @@ test_zip_filename_encoding_UTF8()
        /* A bit 11 of general purpos flag should be 0x08,
         * which indicates the filename charset is UTF-8. */
        assertEqualInt(0x08, buff[7]);
-       /* Above three characters in KOI8-R should translate to the following
-        * three characters (two bytes each) in UTF-8. */
        assertEqualMem(buff + 30, "\xD0\xBF\xD1\x80\xD0\xB8", 6);
 
        /*
@@ -80,6 +79,7 @@ test_zip_filename_encoding_UTF8()
            archive_write_open_memory(a, buff, sizeof(buff), &used));
 
        entry = archive_entry_new2(a);
+       /* Set a UTF-8 filename. */
        archive_entry_set_pathname(entry, "\xD0\xBF\xD1\x80\xD0\xB8");
        archive_entry_set_filetype(entry, AE_IFREG);
        archive_entry_set_size(entry, 0);
@@ -90,8 +90,6 @@ test_zip_filename_encoding_UTF8()
        /* A bit 11 of general purpos flag should be 0x08,
         * which indicates the filename charset is UTF-8. */
        assertEqualInt(0x08, buff[7]);
-       /* Above three characters in KOI8-R should translate to the following
-        * three characters (two bytes each) in UTF-8. */
        assertEqualMem(buff + 30, "\xD0\xBF\xD1\x80\xD0\xB8", 6);
 
        /*
@@ -104,6 +102,7 @@ test_zip_filename_encoding_UTF8()
            archive_write_open_memory(a, buff, sizeof(buff), &used));
 
        entry = archive_entry_new2(a);
+       /* Set an ASCII filename. */
        archive_entry_set_pathname(entry, "abcABC");
        archive_entry_set_filetype(entry, AE_IFREG);
        archive_entry_set_size(entry, 0);
@@ -145,6 +144,7 @@ test_zip_filename_encoding_KOI8R()
            archive_write_open_memory(a, buff, sizeof(buff), &used));
 
        entry = archive_entry_new2(a);
+       /* Set a KOI8-R filename. */
        archive_entry_set_pathname(entry, "\xD0\xD2\xC9");
        archive_entry_set_filetype(entry, AE_IFREG);
        archive_entry_set_size(entry, 0);
@@ -168,6 +168,7 @@ test_zip_filename_encoding_KOI8R()
            archive_write_open_memory(a, buff, sizeof(buff), &used));
 
        entry = archive_entry_new2(a);
+       /* Set a KOI8-R filename. */
        archive_entry_set_pathname(entry, "\xD0\xD2\xC9");
        archive_entry_set_filetype(entry, AE_IFREG);
        archive_entry_set_size(entry, 0);
@@ -199,6 +200,7 @@ test_zip_filename_encoding_KOI8R()
            archive_write_open_memory(a, buff, sizeof(buff), &used));
 
        entry = archive_entry_new2(a);
+       /* Set an ASCII filename. */
        archive_entry_set_pathname(entry, "abcABC");
        archive_entry_set_filetype(entry, AE_IFREG);
        archive_entry_set_size(entry, 0);
@@ -212,6 +214,81 @@ test_zip_filename_encoding_KOI8R()
        assertEqualMem(buff + 30, "abcABC", 6);
 }
 
+/*
+ * Other archiver applications on Windows translate CP1251 filenames
+ * into CP866 filenames and store it in the zip file.
+ * Test above behavior works well.
+ */
+static void
+test_zip_filename_encoding_Russian_Russia()
+{
+       struct archive *a;
+       struct archive_entry *entry;
+       char buff[4096];
+       size_t used;
+
+       if (NULL == setlocale(LC_ALL, "Russian_Russia")) {
+               skipping("Russian_Russia locale not available on this system.");
+               return;
+       }
+
+       /*
+        * Verify that Russian_Russia(CP1251) filenames are correctly translated
+        * to UTF-8.
+        */
+       a = archive_write_new();
+       assertEqualInt(ARCHIVE_OK, archive_write_set_format_zip(a));
+       if (archive_write_set_options(a, "hdrcharset=UTF-8") != ARCHIVE_OK) {
+               skipping("This system cannot convert character-set"
+                   " from Russian_Russia.CP1251 to UTF-8.");
+               archive_write_free(a);
+               return;
+       }
+       assertEqualInt(ARCHIVE_OK,
+           archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+       entry = archive_entry_new2(a);
+       /* Set a CP1251 filename. */
+       archive_entry_set_pathname(entry, "\xEF\xF0\xE8");
+       archive_entry_set_filetype(entry, AE_IFREG);
+       archive_entry_set_size(entry, 0);
+       assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
+       archive_entry_free(entry);
+       assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+       /* A bit 11 of general purpos flag should be 0x08,
+        * which indicates the filename charset is UTF-8. */
+       assertEqualInt(0x08, buff[7]);
+       /* Above three characters in CP1251 should translate to the following
+        * three characters (two bytes each) in UTF-8. */
+       assertEqualMem(buff + 30, "\xD0\xBF\xD1\x80\xD0\xB8", 6);
+
+       /*
+        * Verify that Russian_Russia(CP1251) filenames are correctly translated
+        * to CP866.
+        */
+       a = archive_write_new();
+       assertEqualInt(ARCHIVE_OK, archive_write_set_format_zip(a));
+       assertEqualInt(ARCHIVE_OK,
+           archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+       entry = archive_entry_new2(a);
+       /* Set a CP1251 filename. */
+       archive_entry_set_pathname(entry, "\xEF\xF0\xE8");
+       archive_entry_set_filetype(entry, AE_IFREG);
+       archive_entry_set_size(entry, 0);
+       assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
+       archive_entry_free(entry);
+       assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+       /* A bit 11 of general purpos flag should be 0,
+        * which indicates the filename charset is unkown. */
+       assertEqualInt(0, buff[7]);
+       /* Above three characters in CP1251 should translate to the following
+        * three characters in CP866. */
+       assertEqualMem(buff + 30, "\xAF\xE0\xA8", 3);
+}
+
 static void
 test_zip_filename_encoding_EUCJP()
 {
@@ -240,6 +317,7 @@ test_zip_filename_encoding_EUCJP()
            archive_write_open_memory(a, buff, sizeof(buff), &used));
 
        entry = archive_entry_new2(a);
+       /* Set an EUC-JP filename. */
        archive_entry_set_pathname(entry, "\xC9\xBD.txt");
        /* Check the Unicode version. */
        archive_entry_set_filetype(entry, AE_IFREG);
@@ -263,6 +341,7 @@ test_zip_filename_encoding_EUCJP()
            archive_write_open_memory(a, buff, sizeof(buff), &used));
 
        entry = archive_entry_new2(a);
+       /* Set an EUC-JP filename. */
        archive_entry_set_pathname(entry, "\xC9\xBD.txt");
        /* Check the Unicode version. */
        archive_entry_set_filetype(entry, AE_IFREG);
@@ -295,6 +374,7 @@ test_zip_filename_encoding_EUCJP()
            archive_write_open_memory(a, buff, sizeof(buff), &used));
 
        entry = archive_entry_new2(a);
+       /* Set an ASCII filename. */
        archive_entry_set_pathname(entry, "abcABC");
        /* Check the Unicode version. */
        archive_entry_set_filetype(entry, AE_IFREG);
@@ -313,5 +393,6 @@ DEFINE_TEST(test_zip_filename_encoding)
 {
        test_zip_filename_encoding_UTF8();
        test_zip_filename_encoding_KOI8R();
+       test_zip_filename_encoding_Russian_Russia();
        test_zip_filename_encoding_EUCJP();
 }