From: Michihiro NAKAJIMA Date: Sat, 19 Mar 2011 06:24:09 +0000 (-0400) Subject: Add support for charset option to lha format reader. X-Git-Tag: v3.0.0a~640 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=79a8745dec9adcc326df6903e03d4583bcbbd26f;p=thirdparty%2Flibarchive.git Add support for charset option to lha format reader. Automatically convert filenames in a lha archive if a well-known codepage is specified in its header, but it will not overwrite a character-set specified by the charset option. SVN-Revision: 3032 --- diff --git a/Makefile.am b/Makefile.am index c11b19768..de636fef8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -320,6 +320,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_format_isorr_rr_moved.c \ libarchive/test/test_read_format_isozisofs_bz2.c \ libarchive/test/test_read_format_lha.c \ + libarchive/test/test_read_format_lha_filename.c \ libarchive/test/test_read_format_mtree.c \ libarchive/test/test_read_format_pax_bz2.c \ libarchive/test/test_read_format_raw.c \ @@ -447,6 +448,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_lha_lh6.lzh.uu \ libarchive/test/test_read_format_lha_lh7.lzh.uu \ libarchive/test/test_read_format_lha_withjunk.lzh.uu \ + libarchive/test/test_read_format_lha_cp932.lzh.uu \ libarchive/test/test_read_format_mtree.mtree.uu \ libarchive/test/test_read_format_mtree_nomagic.mtree.uu \ libarchive/test/test_read_format_raw.data.Z.uu \ diff --git a/libarchive/archive_read_support_format_lha.c b/libarchive/archive_read_support_format_lha.c index 03365fd29..07bd536df 100644 --- a/libarchive/archive_read_support_format_lha.c +++ b/libarchive/archive_read_support_format_lha.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2008-2010 Michihiro NAKAJIMA + * Copyright (c) 2008-2011 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -168,6 +168,8 @@ struct lha { struct archive_string gname; uint16_t header_crc; uint16_t crc; + struct archive_string opt_charset; + const char *charset; struct archive_string dirname; struct archive_string filename; @@ -239,6 +241,8 @@ static const uint16_t crc16tbl[256] = { }; static int archive_read_format_lha_bid(struct archive_read *); +static int archive_read_format_lha_options(struct archive_read *, + const char *, const char *); static int archive_read_format_lha_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_lha_read_data(struct archive_read *, @@ -246,6 +250,8 @@ static int archive_read_format_lha_read_data(struct archive_read *, static int archive_read_format_lha_read_data_skip(struct archive_read *); static int archive_read_format_lha_cleanup(struct archive_read *); +static void lha_replace_path_separator(struct archive_read *, struct lha *, + struct archive_string *); static int lha_read_file_header_0(struct archive_read *, struct lha *); static int lha_read_file_header_1(struct archive_read *, struct lha *); static int lha_read_file_header_2(struct archive_read *, struct lha *); @@ -299,7 +305,7 @@ archive_read_support_format_lha(struct archive *_a) lha, "lha", archive_read_format_lha_bid, - NULL, + archive_read_format_lha_options, archive_read_format_lha_read_header, archive_read_format_lha_read_data, archive_read_format_lha_read_data_skip, @@ -403,6 +409,29 @@ archive_read_format_lha_bid(struct archive_read *a) return (0); } +static int +archive_read_format_lha_options(struct archive_read *a, + const char *key, const char *val) +{ + struct lha *lha; + int ret = ARCHIVE_FAILED; + + lha = (struct lha *)(a->format->data); + if (strcmp(key, "charset") == 0) { + if (val == NULL || val[0] == 0) + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "lha: charset option needs a character-set name"); + else { + archive_strcpy(&lha->opt_charset, val); + ret = ARCHIVE_OK; + } + } else + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "lha: unknown keyword ``%s''", key); + + return (ret); +} + static int lha_skip_sfx(struct archive_read *a) { @@ -545,6 +574,10 @@ archive_read_format_lha_read_header(struct archive_read *a, archive_string_empty(&lha->dirname); archive_string_empty(&lha->filename); lha->dos_attr = 0; + if (archive_strlen(&lha->opt_charset) > 0) + lha->charset = lha->opt_charset.s; + else + lha->charset = NULL; switch (p[H_LEVEL_OFFSET]) { case 0: @@ -576,9 +609,18 @@ archive_read_format_lha_read_header(struct archive_read *a, /* * Make a pathname from a dirname and a filename. */ + archive_string_concat(&lha->dirname, &lha->filename); archive_string_init(&pathname); - archive_string_copy(&pathname, &lha->dirname); - archive_string_concat(&pathname, &lha->filename); + archive_strncpy_from_specific_locale(&a->archive, &pathname, + lha->dirname.s, lha->dirname.length, lha->charset); + + /* + * When a header level is 0, there is a possibilty that + * a pathname has '\' character, a directory separator in + * DOS/Windows. So we should convert it to '/'. + */ + if (p[H_LEVEL_OFFSET] == 0) + lha_replace_path_separator(a, lha, &pathname); if ((lha->mode & AE_IFMT) == AE_IFLNK) { /* @@ -669,7 +711,8 @@ archive_read_format_lha_read_header(struct archive_read *a, * set for a filename in an archive. */ static void -lha_replace_path_separator(struct archive_read *a, struct lha *lha, struct archive_string *fn) +lha_replace_path_separator(struct archive_read *a, struct lha *lha, + struct archive_string *fn) { size_t i; @@ -688,7 +731,8 @@ lha_replace_path_separator(struct archive_read *a, struct lha *lha, struct archi */ /* If converting to wide character failed, force a replacement. */ - if (!archive_wstring_append_from_mbs(&a->archive, &(lha->ws), fn->s, fn->length)) { + if (!archive_wstring_append_from_mbs(&a->archive, &(lha->ws), + fn->s, fn->length)) { for (i = 0; i < archive_strlen(fn); i++) { if (fn->s[i] == '\\') fn->s[i] = '/'; @@ -705,7 +749,8 @@ lha_replace_path_separator(struct archive_read *a, struct lha *lha, struct archi * Sanity check that we surely did not break a filename. */ archive_string_empty(&(lha->mbs)); - archive_string_append_from_unicode_to_mbs(&a->archive, &(lha->mbs), lha->ws.s, lha->ws.length); + archive_string_append_from_unicode_to_mbs(&a->archive, &(lha->mbs), + lha->ws.s, lha->ws.length); /* If mbs length is different to fn, we broke the * filename and we shouldn't use it. */ if (archive_strlen(&(lha->mbs)) == archive_strlen(fn)) @@ -768,7 +813,6 @@ lha_read_file_header_0(struct archive_read *a, struct lha *lha) return (truncated_error(a)); archive_strncpy(&lha->filename, p + H0_FILE_NAME_OFFSET, namelen); - lha_replace_path_separator(a, lha, &lha->filename); lha->crc = archive_le16dec(p + H0_FILE_NAME_OFFSET + namelen); sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2); @@ -1207,6 +1251,23 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, lha->origsize = archive_le64dec(extdheader); } break; + case EXT_CODEPAGE: + /* Get a archived filename charset from codepage, + * but you cannot overwrite a specified charset. */ + if (datasize == sizeof(uint32_t) && + lha->charset == NULL) { + switch (archive_le32dec(extdheader)) { + case 932: + lha->charset = "CP932"; + break; + case 65001: /* UTF-8 */ + lha->charset = "UTF-8"; + break; + default: + break; + } + } + break; case EXT_UNIX_MODE: if (datasize == sizeof(uint16_t)) { lha->mode = archive_le16dec(extdheader); @@ -1261,7 +1322,6 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, case EXT_TIMEZONE: /* Not supported */ case EXT_UTF16_FILENAME: /* Not supported */ case EXT_UTF16_DIRECTORY: /* Not supported */ - case EXT_CODEPAGE: /* Not supported */ default: break; } @@ -1509,6 +1569,7 @@ archive_read_format_lha_cleanup(struct archive_read *a) lzh_decode_free(&(lha->strm)); free(lha->uncompressed_buffer); + archive_string_free(&(lha->opt_charset)); archive_string_free(&(lha->dirname)); archive_string_free(&(lha->filename)); archive_string_free(&(lha->uname)); diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt index 3f4d70d42..50c01fd24 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -91,6 +91,7 @@ IF(ENABLE_TEST) test_read_format_isorr_rr_moved.c test_read_format_isozisofs_bz2.c test_read_format_lha.c + test_read_format_lha_filename.c test_read_format_mtree.c test_read_format_pax_bz2.c test_read_format_raw.c diff --git a/libarchive/test/test_read_format_lha_cp932.lzh.uu b/libarchive/test/test_read_format_lha_cp932.lzh.uu new file mode 100644 index 000000000..151701460 --- /dev/null +++ b/libarchive/test/test_read_format_lha_cp932.lzh.uu @@ -0,0 +1,7 @@ +begin 644 test_read_format_lha_cp932.lzh +M30`M;&@P+0@````(````*:2#32`"&4A-!P!&I`,```L``8J_CIHN='AT&P!! +M-'"`))KERP%TJNDQFN7+`72JZ3&:Y'0;`$&:91,VFN7+ +>`:#K$3V:Y + +DEFINE_TEST(test_read_format_lha_filename) +{ + struct archive *a; + struct archive_entry *ae; + + /* A sample file was created with LHA32.EXE through UNLHA.DLL. */ + const char *refname = "test_read_format_lha_cp932.lzh"; + + /* + * Read LHA filename in ja_JP.eucJP. + */ + if (NULL == setlocale(LC_ALL, "ja_JP.eucJP")) { + skipping("ja_JP.eucJP locale not available on this system."); + return; + } + + extract_reference_file(refname); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_compression_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + /* Verify regular file. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("\xB4\xC1\xBB\xFA\x2E\x74\x78\x74", + archive_entry_pathname(ae)); + assertEqualInt(8, archive_entry_size(ae)); + + /* Verify regular file. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("\xC9\xBD\x2E\x74\x78\x74", archive_entry_pathname(ae)); + assertEqualInt(4, archive_entry_size(ae)); + + + /* End of archive. */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + /* Verify archive format. */ + assertEqualIntA(a, ARCHIVE_COMPRESSION_NONE, archive_compression(a)); + assertEqualIntA(a, ARCHIVE_FORMAT_LHA, archive_format(a)); + + /* Close the archive. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + + /* + * Read LHA filename in ja_JP.UTF-8. + */ + if (NULL == setlocale(LC_ALL, "ja_JP.UTF-8")) { + skipping("ja_JP.UTF-8 locale not available on this system."); + return; + } + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_compression_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + /* Verify regular file. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("\xE6\xBC\xA2\xE5\xAD\x97\x2E\x74\x78\x74", + archive_entry_pathname(ae)); + assertEqualInt(8, archive_entry_size(ae)); + + /* Verify regular file. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("\xE8\xA1\xA8\x2E\x74\x78\x74", + archive_entry_pathname(ae)); + assertEqualInt(4, archive_entry_size(ae)); + + + /* End of archive. */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + /* Verify archive format. */ + assertEqualIntA(a, ARCHIVE_COMPRESSION_NONE, archive_compression(a)); + assertEqualIntA(a, ARCHIVE_FORMAT_LHA, archive_format(a)); + + /* Close the archive. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} +