diff --git a/Makefile.am b/Makefile.am
index e04fa400..9563e12a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -128,6 +128,7 @@ noinst_HEADERS = \
src/bin/write/mod_xlsx.h \
src/bin/write/module.h \
src/bin/write/module_util.h \
+ src/bin/util/main.h \
src/bin/util/file_format.h \
src/bin/util/quote_and_escape.h \
src/bin/util/readstat_dta_days.h \
diff --git a/VS17/ReadStat_App/ReadStat_App.vcxproj b/VS17/ReadStat_App/ReadStat_App.vcxproj
index 775a1606..b89f422f 100644
--- a/VS17/ReadStat_App/ReadStat_App.vcxproj
+++ b/VS17/ReadStat_App/ReadStat_App.vcxproj
@@ -57,6 +57,7 @@
+
diff --git a/VS17/ReadStat_App/ReadStat_App.vcxproj.filters b/VS17/ReadStat_App/ReadStat_App.vcxproj.filters
index 15473541..155fe62f 100644
--- a/VS17/ReadStat_App/ReadStat_App.vcxproj.filters
+++ b/VS17/ReadStat_App/ReadStat_App.vcxproj.filters
@@ -131,6 +131,9 @@
Header Files
+
+ Header Files
+
Header Files
diff --git a/src/bin/extract_metadata.c b/src/bin/extract_metadata.c
index f60131f9..1560f381 100644
--- a/src/bin/extract_metadata.c
+++ b/src/bin/extract_metadata.c
@@ -9,6 +9,7 @@
#include "util/readstat_dta_days.h"
#include "util/quote_and_escape.h"
#include "util/file_format.h"
+#include "util/main.h"
#include "extract_metadata.h"
#include "write/json/write_missing_values.h"
#include "write/json/write_value_labels.h"
@@ -303,7 +304,7 @@ cleanup: readstat_parser_free(parser);
return ret;
}
-int main(int argc, char *argv[]) {
+int portable_main(int argc, char *argv[]) {
if (argc != 3) {
printf("Usage: %s \n", argv[0]);
return 1;
diff --git a/src/bin/readstat.c b/src/bin/readstat.c
index f55da6a4..62a02809 100644
--- a/src/bin/readstat.c
+++ b/src/bin/readstat.c
@@ -44,6 +44,7 @@ int gettimeofday(struct timeval* t, void* timezone)
#endif
#include "util/file_format.h"
+#include "util/main.h"
#if defined _MSC_VER
#define unlink _unlink
@@ -480,7 +481,7 @@ static int dump_file(const char *input_filename) {
return 0;
}
-int main(int argc, char** argv) {
+int portable_main(int argc, char** argv) {
char *input_filename = NULL;
char *catalog_filename = NULL;
char *output_filename = NULL;
diff --git a/src/bin/util/main.h b/src/bin/util/main.h
new file mode 100644
index 00000000..04143718
--- /dev/null
+++ b/src/bin/util/main.h
@@ -0,0 +1,70 @@
+#include
+#include
+
+
+// True main for all platforms
+int portable_main(int argc, char *argv[]);
+
+
+#if defined _WIN32
+#include
+ // Standard way of decoding wide-string command-line arguments on Windows.
+ // Call portable_main with UTF-8 strings.
+ int main(int unused_argc, char *unused_argv[]) {
+ int argc;
+ int ret = 1;
+ wchar_t** utf16_argv = NULL;
+ char** utf8_argv = NULL;
+
+ // Manual standard argument decoding needed since wmain is not supported by MinGW by default.
+ utf16_argv = CommandLineToArgvW(GetCommandLineW(), &argc);
+
+ if(utf16_argv == NULL) {
+ fprintf(stderr, "Fatal error: command line argument extraction failure\n");
+ goto cleanup;
+ }
+
+ utf8_argv = calloc(argc, sizeof(char*));
+
+ for (int i=0; i
#include
+#include
#if !defined(_MSC_VER)
# include
#endif
+#if defined _WIN32
+# include
+#endif
#include "readstat.h"
#include "readstat_io_unistd.h"
@@ -24,8 +28,35 @@
#endif
+int open_with_unicode(const char *path, int options)
+{
+#if defined _WIN32
+ const int buffer_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
+
+ if(buffer_size <= 0)
+ return -1;
+
+ wchar_t* wpath = malloc((buffer_size + 1) * sizeof(wchar_t));
+ const int res = MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, buffer_size);
+ wpath[buffer_size] = 0;
+
+ if(res <= 0)
+ {
+ free(wpath);
+ return -1;
+ }
+
+ int fd = _wopen(wpath, options);
+
+ free(wpath);
+ return fd;
+#else
+ return open(path, options);
+#endif
+}
+
int unistd_open_handler(const char *path, void *io_ctx) {
- int fd = open(path, UNISTD_OPEN_OPTIONS);
+ int fd = open_with_unicode(path, UNISTD_OPEN_OPTIONS);
((unistd_io_ctx_t*) io_ctx)->fd = fd;
return fd;
}