From: Michihiro NAKAJIMA Date: Sun, 10 Apr 2011 07:16:21 +0000 (-0400) Subject: Fix the automatic filename translation on Windows and add a test for it. X-Git-Tag: v3.0.0a~494 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7cf057572c99fda8f32f077fa17ff3d4adfc90e4;p=thirdparty%2Flibarchive.git Fix the automatic filename translation on Windows and add a test for it. 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 --- diff --git a/libarchive/archive_string.c b/libarchive/archive_string.c index 333cdf13f..3dd06a757 100644 --- a/libarchive/archive_string.c +++ b/libarchive/archive_string.c @@ -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 diff --git a/libarchive/test/test_zip_filename_encoding.c b/libarchive/test/test_zip_filename_encoding.c index b6fc4e384..71fd89688 100644 --- a/libarchive/test/test_zip_filename_encoding.c +++ b/libarchive/test/test_zip_filename_encoding.c @@ -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(); }