]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Add a new Windows-only public API, archive_read_open_filenames_w (#2016)
authorDustin L. Howett <dustin@howett.net>
Tue, 21 Nov 2023 20:26:46 +0000 (14:26 -0600)
committerGitHub <noreply@github.com>
Tue, 21 Nov 2023 20:26:46 +0000 (12:26 -0800)
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.

libarchive/archive.h
libarchive/archive_read_open_filename.c
libarchive/test/test_read_format_rar.c

index 8caabdf1256e72221b61243c63c0abee4314e75e..3b9eb51f50f744c5148d62374932f16fee854eb6 100644 (file)
@@ -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;
index b0583db2bd176b9c20bb55ea62f3c2f6890f1518..dd2e16022844b7dd2122577cac3800d089ab8aaf 100644 (file)
@@ -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
index 1425eb9a4570b48e3146ee0428270d80b925913b..ad7c307cc15478d668e51f654dafaeea7c61aae9 100644 (file)
@@ -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\"><BR>\n</P>\n</BODY>\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[] =