From: Dustin L. Howett Date: Tue, 21 Nov 2023 20:26:46 +0000 (-0600) Subject: Add a new Windows-only public API, archive_read_open_filenames_w (#2016) X-Git-Tag: v3.7.3~45 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=872a9dfd3be29451d7662c17430c35c248b258c9;p=thirdparty%2Flibarchive.git Add a new Windows-only public API, archive_read_open_filenames_w (#2016) There is a discrepancy between the w and non-w filename APIs, wherein a consumer of libarchive can open a multi-volume set with names in the current locale (on Windows) but not with UTF-16 names. This patch addresses that issue. archive_read_open_filename_w delegates its work to archive_read_open_filenames_w. Fixes #1728 Tested passing on Windows. In the meantime, I will also test on Linux. I am hoping that the build agents can help me determine FreeBSD and macOS coverage. --- diff --git a/libarchive/archive.h b/libarchive/archive.h index 8caabdf12..3b9eb51f5 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -533,6 +533,10 @@ __LA_DECL int archive_read_open_filenames(struct archive *, const char **_filenames, size_t _block_size); __LA_DECL int archive_read_open_filename_w(struct archive *, const wchar_t *_filename, size_t _block_size); +#if defined(_WIN32) && !defined(__CYGWIN__) +__LA_DECL int archive_read_open_filenames_w(struct archive *, + const wchar_t **_filenames, size_t _block_size); +#endif /* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */ __LA_DECL int archive_read_open_file(struct archive *, const char *_filename, size_t _block_size) __LA_DEPRECATED; diff --git a/libarchive/archive_read_open_filename.c b/libarchive/archive_read_open_filename.c index b0583db2b..dd2e16022 100644 --- a/libarchive/archive_read_open_filename.c +++ b/libarchive/archive_read_open_filename.c @@ -154,55 +154,73 @@ no_memory: return (ARCHIVE_FATAL); } +/* + * This function is an implementation detail of archive_read_open_filename_w, + * which is exposed as a separate API on Windows. + */ +#if !defined(_WIN32) || defined(__CYGWIN__) +static +#endif int -archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename, +archive_read_open_filenames_w(struct archive *a, const wchar_t **wfilenames, size_t block_size) { - struct read_file_data *mine = (struct read_file_data *)calloc(1, - sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t)); - if (!mine) + struct read_file_data *mine; + const wchar_t *wfilename = NULL; + if (wfilenames) + wfilename = *(wfilenames++); + + archive_clear_error(a); + do { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - mine->fd = -1; - mine->block_size = block_size; + if (wfilename == NULL) + wfilename = L""; + mine = (struct read_file_data *)calloc(1, + sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t)); + if (mine == NULL) + goto no_memory; + mine->block_size = block_size; + mine->fd = -1; - if (wfilename == NULL || wfilename[0] == L'\0') { - mine->filename_type = FNT_STDIN; - } else { + if (wfilename == NULL || wfilename[0] == L'\0') { + mine->filename_type = FNT_STDIN; + } else { #if defined(_WIN32) && !defined(__CYGWIN__) - mine->filename_type = FNT_WCS; - wcscpy(mine->filename.w, wfilename); + mine->filename_type = FNT_WCS; + wcscpy(mine->filename.w, wfilename); #else - /* - * POSIX system does not support a wchar_t interface for - * open() system call, so we have to translate a wchar_t - * filename to multi-byte one and use it. - */ - struct archive_string fn; - - archive_string_init(&fn); - if (archive_string_append_from_wcs(&fn, wfilename, - wcslen(wfilename)) != 0) { - if (errno == ENOMEM) - archive_set_error(a, errno, - "Can't allocate memory"); - else - archive_set_error(a, EINVAL, - "Failed to convert a wide-character" - " filename to a multi-byte filename"); + /* + * POSIX system does not support a wchar_t interface for + * open() system call, so we have to translate a wchar_t + * filename to multi-byte one and use it. + */ + struct archive_string fn; + + archive_string_init(&fn); + if (archive_string_append_from_wcs(&fn, wfilename, + wcslen(wfilename)) != 0) { + if (errno == ENOMEM) + archive_set_error(a, errno, + "Can't allocate memory"); + else + archive_set_error(a, EINVAL, + "Failed to convert a wide-character" + " filename to a multi-byte filename"); + archive_string_free(&fn); + free(mine); + return (ARCHIVE_FATAL); + } + mine->filename_type = FNT_MBS; + strcpy(mine->filename.m, fn.s); archive_string_free(&fn); - free(mine); - return (ARCHIVE_FATAL); - } - mine->filename_type = FNT_MBS; - strcpy(mine->filename.m, fn.s); - archive_string_free(&fn); #endif - } - if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK)) - return (ARCHIVE_FATAL); + } + if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK)) + return (ARCHIVE_FATAL); + if (wfilenames == NULL) + break; + wfilename = *(wfilenames++); + } while (wfilename != NULL && wfilename[0] != '\0'); archive_read_set_open_callback(a, file_open); archive_read_set_read_callback(a, file_read); archive_read_set_skip_callback(a, file_skip); @@ -211,6 +229,19 @@ archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename, archive_read_set_seek_callback(a, file_seek); return (archive_read_open1(a)); +no_memory: + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); +} + +int +archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename, + size_t block_size) +{ + const wchar_t *wfilenames[2]; + wfilenames[0] = wfilename; + wfilenames[1] = NULL; + return archive_read_open_filenames_w(a, wfilenames, block_size); } static int diff --git a/libarchive/test/test_read_format_rar.c b/libarchive/test/test_read_format_rar.c index 1425eb9a4..ad7c307cc 100644 --- a/libarchive/test/test_read_format_rar.c +++ b/libarchive/test/test_read_format_rar.c @@ -879,16 +879,9 @@ DEFINE_TEST(test_read_format_rar_windows) assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } -DEFINE_TEST(test_read_format_rar_multivolume) +static void +test_read_format_rar_multivolume_test_body_helper(struct archive *a) { - const char *reffiles[] = - { - "test_read_format_rar_multivolume.part0001.rar", - "test_read_format_rar_multivolume.part0002.rar", - "test_read_format_rar_multivolume.part0003.rar", - "test_read_format_rar_multivolume.part0004.rar", - NULL - }; int file1_size = 241647978, offset = 0; char buff[64]; const char file1_test_txt[] = "gin-bottom: 0in\">
\n

\n\n" @@ -903,13 +896,6 @@ DEFINE_TEST(test_read_format_rar_multivolume) int file3_size = sizeof(file3_buff); const char file3_test_txt[] = "test text document\r\n"; struct archive_entry *ae; - struct archive *a; - - extract_reference_files(reffiles); - assert((a = archive_read_new()) != NULL); - assertA(0 == archive_read_support_filter_all(a)); - assertA(0 == archive_read_support_format_all(a)); - assertA(0 == archive_read_open_filenames(a, reffiles, 10240)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); @@ -1016,6 +1002,65 @@ DEFINE_TEST(test_read_format_rar_multivolume) assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } +DEFINE_TEST(test_read_format_rar_multivolume) +{ + const char *reffiles[] = + { + "test_read_format_rar_multivolume.part0001.rar", + "test_read_format_rar_multivolume.part0002.rar", + "test_read_format_rar_multivolume.part0003.rar", + "test_read_format_rar_multivolume.part0004.rar", + NULL + }; + + struct archive *a; + + extract_reference_files(reffiles); + assert((a = archive_read_new()) != NULL); + assertA(0 == archive_read_support_filter_all(a)); + assertA(0 == archive_read_support_format_all(a)); + assertA(0 == archive_read_open_filenames(a, reffiles, 10240)); + + test_read_format_rar_multivolume_test_body_helper(a); +} + +/* As above, but using read_open_filenames_w */ +DEFINE_TEST(test_read_format_rar_multivolume_w) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + const char *reffiles[] = + { + "test_read_format_rar_multivolume.part0001.rar", + "test_read_format_rar_multivolume.part0002.rar", + "test_read_format_rar_multivolume.part0003.rar", + "test_read_format_rar_multivolume.part0004.rar", + NULL + }; + + const wchar_t *wreffiles[] = + { + L"test_read_format_rar_multivolume.part0001.rar", + L"test_read_format_rar_multivolume.part0002.rar", + L"test_read_format_rar_multivolume.part0003.rar", + L"test_read_format_rar_multivolume.part0004.rar", + NULL + }; + + struct archive *a; + + extract_reference_files(reffiles); + assert((a = archive_read_new()) != NULL); + assertA(0 == archive_read_support_filter_all(a)); + assertA(0 == archive_read_support_format_all(a)); + assertA(0 == archive_read_open_filenames_w(a, wreffiles, 10240)); + + test_read_format_rar_multivolume_test_body_helper(a); +#else + skipping("archive_read_open_filenames_w is not available on this platform"); + return; +#endif +} + DEFINE_TEST(test_read_format_rar_multivolume_skip) { const char *reffiles[] =