It would be useful for an internationalization programing on Windows.
SVN-Revision: 3825
/* Use this if you know the filename. Note: NULL indicates stdin. */
__LA_DECL int archive_read_open_filename(struct archive *,
const char *_filename, size_t _block_size);
+__LA_DECL int archive_read_open_filename_w(struct archive *,
+ const wchar_t *_filename, size_t _block_size);
/* 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);
archive_close_callback *);
__LA_DECL int archive_write_open_fd(struct archive *, int _fd);
__LA_DECL int archive_write_open_filename(struct archive *, const char *_file);
+__LA_DECL int archive_write_open_filename_w(struct archive *,
+ const wchar_t *_file);
/* A deprecated synonym for archive_write_open_filename() */
__LA_DECL int archive_write_open_file(struct archive *, const char *_file);
__LA_DECL int archive_write_open_FILE(struct archive *, FILE *);
#endif
#include "archive.h"
+#include "archive_string.h"
#ifndef O_BINARY
#define O_BINARY 0
void *buffer;
mode_t st_mode; /* Mode bits for opened file. */
char use_lseek;
- char filename[1]; /* Must be last! */
+ enum fnt_e { FNT_STDIN, FNT_MBS, FNT_WCS } filename_type;
+ union {
+ char m[1];/* MBS filename. */
+ wchar_t w[1];/* WCS filename. */
+ } filename; /* Must be last! */
};
static int file_close(struct archive *, void *);
+static int file_open_filename(struct archive *, enum fnt_e, const void *,
+ size_t);
static ssize_t file_read(struct archive *, void *, const void **buff);
static int64_t file_seek(struct archive *, void *, int64_t request, int);
static int64_t file_skip(struct archive *, void *, int64_t request);
int
archive_read_open_filename(struct archive *a, const char *filename,
size_t block_size)
+{
+ enum fnt_e filename_type;
+
+ if (filename == NULL || filename[0] == '\0') {
+ filename_type = FNT_STDIN;
+ } else
+ filename_type = FNT_MBS;
+ return (file_open_filename(a, filename_type, filename, block_size));
+}
+
+int
+archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename,
+ size_t block_size)
+{
+ enum fnt_e filename_type;
+
+ if (wfilename == NULL || wfilename[0] == L'\0') {
+ filename_type = FNT_STDIN;
+ } else {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ filename_type = FNT_WCS;
+#else
+ /*
+ * POSIX system does not support a wchar_t interface for
+ * open() system call, so we have to translate a whcar_t
+ * filename to multi-byte one and use it.
+ */
+ struct archive_string fn;
+ int r;
+
+ archive_string_init(&fn);
+ if (archive_string_append_from_wcs(&fn, wfilename,
+ wcslen(wfilename)) != 0) {
+ archive_set_error(a, EINVAL,
+ "Failed to convert a wide-character filename to"
+ " a multi-byte filename");
+ archive_string_free(&fn);
+ return (ARCHIVE_FATAL);
+ }
+ r = file_open_filename(a, FNT_MBS, fn.s, block_size);
+ archive_string_free(&fn);
+ return (r);
+#endif
+ }
+ return (file_open_filename(a, filename_type, wfilename, block_size));
+}
+
+static int
+file_open_filename(struct archive *a, enum fnt_e filename_type,
+ const void *_filename, size_t block_size)
{
struct stat st;
struct read_file_data *mine;
void *buffer;
+ const char *filename = NULL;
+ const wchar_t *wfilename = NULL;
int fd;
int is_disk_like = 0;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#endif
archive_clear_error(a);
- if (filename == NULL || filename[0] == '\0') {
+ if (filename_type == FNT_STDIN) {
/* We used to delegate stdin support by
* directly calling archive_read_open_fd(a,0,block_size)
* here, but that doesn't (and shouldn't) handle the
* API is intended to be a little smarter for folks who
* want easy handling of the common case.
*/
- filename = ""; /* Normalize NULL to "" */
fd = 0;
#if defined(__CYGWIN__) || defined(_WIN32)
setmode(0, O_BINARY);
#endif
- } else {
+ filename = "";
+ } else if (filename_type == FNT_MBS) {
+ filename = (const char *)_filename;
fd = open(filename, O_RDONLY | O_BINARY);
if (fd < 0) {
archive_set_error(a, errno,
"Failed to open '%s'", filename);
return (ARCHIVE_FATAL);
}
+ } else {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ wfilename = (const wchar_t *)_filename;
+ fd = _wopen(wfilename, O_RDONLY | O_BINARY);
+ if (fd < 0 && errno == ENOENT) {
+ wchar_t *fullpath;
+ fullpath = __la_win_permissive_name_w(wfilename);
+ if (fullpath != NULL) {
+ fd = _wopen(fullpath, O_RDONLY | O_BINARY);
+ free(fullpath);
+ }
+ }
+ if (fd < 0) {
+ archive_set_error(a, errno,
+ "Failed to open '%S'", wfilename);
+ return (ARCHIVE_FATAL);
+ }
+#else
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unexpedted operation in archive_read_open_filename");
+ return (ARCHIVE_FATAL);
+#endif
}
if (fstat(fd, &st) != 0) {
- archive_set_error(a, errno, "Can't stat '%s'", filename);
+ if (filename_type == FNT_WCS)
+ archive_set_error(a, errno, "Can't stat '%S'",
+ wfilename);
+ else
+ archive_set_error(a, errno, "Can't stat '%s'",
+ filename);
return (ARCHIVE_FATAL);
}
#endif
/* TODO: Add an "is_tape_like" variable and appropriate tests. */
- mine = (struct read_file_data *)calloc(1,
- sizeof(*mine) + strlen(filename));
+ if (filename_type == FNT_WCS)
+ mine = (struct read_file_data *)calloc(1,
+ sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t));
+ else
+ mine = (struct read_file_data *)calloc(1,
+ sizeof(*mine) + strlen(filename));
/* Disk-like devices prefer power-of-two block sizes. */
/* Use provided block_size as a guide so users have some control. */
if (is_disk_like) {
free(buffer);
return (ARCHIVE_FATAL);
}
- strcpy(mine->filename, filename);
+ if (filename_type == FNT_WCS)
+ wcscpy(mine->filename.w, wfilename);
+ else
+ strcpy(mine->filename.m, filename);
+ mine->filename_type = filename_type;
mine->block_size = block_size;
mine->buffer = buffer;
mine->fd = fd;
if (bytes_read < 0) {
if (errno == EINTR)
continue;
- else if (mine->filename[0] == '\0')
- archive_set_error(a, errno, "Error reading stdin");
+ else if (mine->filename_type == FNT_STDIN)
+ archive_set_error(a, errno,
+ "Error reading stdin");
+ else if (mine->filename_type == FNT_MBS)
+ archive_set_error(a, errno,
+ "Error reading '%s'", mine->filename.m);
else
- archive_set_error(a, errno, "Error reading '%s'",
- mine->filename);
+ archive_set_error(a, errno,
+ "Error reading '%S'", mine->filename.w);
}
return (bytes_read);
}
return (0);
/* If the input is corrupted or truncated, fail. */
- if (mine->filename[0] == '\0')
+ if (mine->filename_type == FNT_STDIN)
archive_set_error(a, errno, "Error seeking in stdin");
- else
+ else if (mine->filename_type == FNT_MBS)
archive_set_error(a, errno, "Error seeking in '%s'",
- mine->filename);
+ mine->filename.m);
+ else
+ archive_set_error(a, errno, "Error seeking in '%S'",
+ mine->filename.w);
return (-1);
}
return r;
/* If the input is corrupted or truncated, fail. */
- if (mine->filename[0] == '\0')
+ if (mine->filename_type == FNT_STDIN)
archive_set_error(a, errno, "Error seeking in stdin");
- else
+ else if (mine->filename_type == FNT_MBS)
archive_set_error(a, errno, "Error seeking in '%s'",
- mine->filename);
+ mine->filename.m);
+ else
+ archive_set_error(a, errno, "Error seeking in '%S'",
+ mine->filename.w);
return (ARCHIVE_FATAL);
}
} while (bytesRead > 0);
}
/* If a named file was opened, then it needs to be closed. */
- if (mine->filename[0] != '\0')
+ if (mine->filename_type != FNT_STDIN)
close(mine->fd);
}
free(mine->buffer);
#endif
#include "archive.h"
+#include "archive_string.h"
#ifndef O_BINARY
#define O_BINARY 0
struct write_file_data {
int fd;
- char filename[1];
+ char mbs_filename;
+ union {
+ char m[1];
+ wchar_t w[1];
+ } filename; /* Must be last! */
};
static int file_close(struct archive *, void *);
archive_set_error(a, ENOMEM, "No memory");
return (ARCHIVE_FATAL);
}
- strcpy(mine->filename, filename);
+ strcpy(mine->filename.m, filename);
+ mine->mbs_filename = 1;
mine->fd = -1;
return (archive_write_open(a, mine,
file_open, file_write, file_close));
}
+int
+archive_write_open_filename_w(struct archive *a, const wchar_t *filename)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ struct write_file_data *mine;
+
+ if (filename == NULL || filename[0] == L'\0')
+ return (archive_write_open_fd(a, 1));
+
+ mine = malloc(sizeof(*mine) + wcslen(filename) * sizeof(wchar_t));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ wcscpy(mine->filename.w, filename);
+ mine->mbs_filename = 0;
+ mine->fd = -1;
+ return (archive_write_open(a, mine,
+ file_open, file_write, file_close));
+#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;
+ int r;
+
+ if (filename == NULL || filename[0] == L'\0')
+ return (archive_write_open_fd(a, 1));
+
+ archive_string_init(&fn);
+ if (archive_string_append_from_wcs(&fn, filename,
+ wcslen(filename)) != 0) {
+ archive_set_error(a, EINVAL,
+ "Failed to convert a wide-character filename to"
+ " a multi-byte filename");
+ archive_string_free(&fn);
+ return (ARCHIVE_FATAL);
+ }
+ r = archive_write_open_filename(a, fn.s);
+ archive_string_free(&fn);
+ return (r);
+#endif
+}
+
+
static int
file_open(struct archive *a, void *client_data)
{
/*
* Open the file.
*/
- mine->fd = open(mine->filename, flags, 0666);
- if (mine->fd < 0) {
- archive_set_error(a, errno, "Failed to open '%s'",
- mine->filename);
- return (ARCHIVE_FATAL);
- }
+ if (mine->mbs_filename) {
+ mine->fd = open(mine->filename.m, flags, 0666);
+ if (mine->fd < 0) {
+ archive_set_error(a, errno, "Failed to open '%s'",
+ mine->filename.m);
+ return (ARCHIVE_FATAL);
+ }
+
+ if (fstat(mine->fd, &st) != 0) {
+ archive_set_error(a, errno, "Couldn't stat '%s'",
+ mine->filename.m);
+ return (ARCHIVE_FATAL);
+ }
+ } else {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ mine->fd = _wopen(mine->filename.w, flags, 0666);
+ if (mine->fd < 0 && errno == ENOENT) {
+ wchar_t *fullpath;
+ fullpath = __la_win_permissive_name_w(mine->filename.w);
+ if (fullpath != NULL) {
+ mine->fd = _wopen(fullpath, flags, 0666);
+ free(fullpath);
+ }
+ }
+ if (mine->fd < 0) {
+ archive_set_error(a, errno, "Failed to open '%S'",
+ mine->filename.w);
+ return (ARCHIVE_FATAL);
+ }
- if (fstat(mine->fd, &st) != 0) {
- archive_set_error(a, errno, "Couldn't stat '%s'",
- mine->filename);
- return (ARCHIVE_FATAL);
+ if (fstat(mine->fd, &st) != 0) {
+ archive_set_error(a, errno, "Couldn't stat '%S'",
+ mine->filename.w);
+ return (ARCHIVE_FATAL);
+ }
+#else
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unexpedted operation in archive_write_open_filename");
+ return (ARCHIVE_FATAL);
+#endif
}
/*
#include "test.h"
__FBSDID("$FreeBSD: head/lib/libarchive/test/test_open_filename.c 191183 2009-04-17 01:06:31Z kientzle $");
-DEFINE_TEST(test_open_filename)
+static void
+test_open_filename_mbs(void)
{
char buff[64];
struct archive_entry *ae;
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
+
+static void
+test_open_filename_wcs(void)
+{
+ char buff[64];
+ struct archive_entry *ae;
+ struct archive *a;
+
+ /* Write an archive through this FILE *. */
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_compression_none(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_open_filename_w(a, L"test.tar"));
+
+ /*
+ * Write a file to it.
+ */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_set_mtime(ae, 1, 0);
+ archive_entry_copy_pathname_w(ae, L"file");
+ archive_entry_set_mode(ae, S_IFREG | 0755);
+ archive_entry_set_size(ae, 8);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ archive_entry_free(ae);
+ assertEqualIntA(a, 8, archive_write_data(a, "12345678", 9));
+
+ /*
+ * Write a second file to it.
+ */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname_w(ae, L"file2");
+ archive_entry_set_mode(ae, S_IFREG | 0755);
+ archive_entry_set_size(ae, 819200);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ archive_entry_free(ae);
+
+ /* Close out the archive. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+ /*
+ * Now, read the data back.
+ */
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_filename_w(a, L"test.tar", 512));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualInt(1, archive_entry_mtime(ae));
+ assertEqualInt(0, archive_entry_mtime_nsec(ae));
+ assertEqualInt(0, archive_entry_atime(ae));
+ assertEqualInt(0, archive_entry_ctime(ae));
+ assertEqualWString(L"file", archive_entry_pathname_w(ae));
+ assert((S_IFREG | 0755) == archive_entry_mode(ae));
+ assertEqualInt(8, archive_entry_size(ae));
+ assertEqualIntA(a, 8, archive_read_data(a, buff, 10));
+ assertEqualMem(buff, "12345678", 8);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualWString(L"file2", archive_entry_pathname_w(ae));
+ assert((S_IFREG | 0755) == archive_entry_mode(ae));
+ assertEqualInt(819200, archive_entry_size(ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_data_skip(a));
+
+ /* Verify the end of the archive. */
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ /*
+ * Verify some of the error handling.
+ */
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_FATAL,
+ archive_read_open_filename_w(a, L"nonexistent.tar", 512));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+}
+
+DEFINE_TEST(test_open_filename)
+{
+ test_open_filename_mbs();
+ test_open_filename_wcs();
+}