]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Add support for charset option to lha format reader.
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Sat, 19 Mar 2011 06:24:09 +0000 (02:24 -0400)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Sat, 19 Mar 2011 06:24:09 +0000 (02:24 -0400)
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

Makefile.am
libarchive/archive_read_support_format_lha.c
libarchive/test/CMakeLists.txt
libarchive/test/test_read_format_lha_cp932.lzh.uu [new file with mode: 0644]
libarchive/test/test_read_format_lha_filename.c [new file with mode: 0644]

index c11b197687f39eaf233ed192c592a67b9337cb8a..de636fef88306fc692d373e7845144dcbe3a2b13 100644 (file)
@@ -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                  \
index 03365fd29c6c83e9b62fb8681e3a80416dee2127..07bd536df1cfa29eec7c3a2ecedac95ed75c3a25 100644 (file)
@@ -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));
index 3f4d70d42fd09dcae964882aeab4460b69dc9013..50c01fd24923ac87571ddd9643a8e9681a0ee90f 100644 (file)
@@ -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 (file)
index 0000000..1517014
--- /dev/null
@@ -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<L!!@``T2,'``"*OXZ:@JF"R$L`+6QH
+M,"T$````!````#RD@TT@`NA;30<`1J0#```)``&57"YT>'0;`$&:91,VFN7+
+>`:#K$3V:Y<L!H.L1/9KERP$&``#L+0<``&QI<W0`
+`
+end
diff --git a/libarchive/test/test_read_format_lha_filename.c b/libarchive/test/test_read_format_lha_filename.c
new file mode 100644 (file)
index 0000000..6927e26
--- /dev/null
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 2011 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD");
+
+#include <locale.h>
+
+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));
+}
+