Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
1 change: 1 addition & 0 deletions VS17/ReadStat_App/ReadStat_App.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
<ClInclude Include="..\..\src\bin\write\module.h" />
<ClInclude Include="..\..\src\bin\write\module_util.h" />
<ClInclude Include="..\..\src\bin\util\file_format.h" />
<ClInclude Include="..\..\src\bin\util\main.h" />
<ClInclude Include="..\..\src\bin\util\quote_and_escape.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
Expand Down
3 changes: 3 additions & 0 deletions VS17/ReadStat_App/ReadStat_App.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@
<ClInclude Include="..\..\src\bin\util\file_format.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\bin\util\main.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\bin\util\quote_and_escape.h">
<Filter>Header Files</Filter>
</ClInclude>
Expand Down
3 changes: 2 additions & 1 deletion src/bin/extract_metadata.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 <input-filename.(dta|sav)> <output-metadata.json>\n", argv[0]);
return 1;
Expand Down
3 changes: 2 additions & 1 deletion src/bin/readstat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
70 changes: 70 additions & 0 deletions src/bin/util/main.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include <stdlib.h>
#include <stdio.h>


// True main for all platforms
int portable_main(int argc, char *argv[]);


#if defined _WIN32
#include <windows.h>
// 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<argc; ++i) {
const int len = WideCharToMultiByte(CP_UTF8, 0, utf16_argv[i], -1, NULL, 0, NULL, NULL);

if (len <= 0) {
fprintf(stderr, "Fatal error: command line encoding failure (argument %d)\n", i+1);
goto cleanup;
}

utf8_argv[i] = malloc(len + 1);
const size_t ret = WideCharToMultiByte(CP_UTF8, 0, utf16_argv[i], -1, utf8_argv[i], len, NULL, NULL);

if (ret <= 0) {
fprintf(stderr, "Fatal error: command line encoding failure (argument %d)\n", i+1);
goto cleanup;
}

utf8_argv[i][len] = 0;
}

ret = portable_main(argc, utf8_argv);

cleanup:
if(utf8_argv != NULL)
for(int i=0; i<argc; ++i)
free(utf8_argv[i]);

free(utf8_argv);
LocalFree(utf16_argv);
return ret;
}
#else
// Proxy main.
// On Mac, argv encoding is the current local encoding.
// On Linux, argv encoding is difficult to know, but it
// should often be the current local encoding (generally UTF-8).
int main(int argc, char *argv[])
{
portable_main(argc, argv);
return 0;
}
#endif

33 changes: 32 additions & 1 deletion src/readstat_io_unistd.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@

#include <fcntl.h>
#include <stdlib.h>
#include <wchar.h>
#if !defined(_MSC_VER)
# include <unistd.h>
#endif
#if defined _WIN32
# include <windows.h>
#endif

#include "readstat.h"
#include "readstat_io_unistd.h"
Expand All @@ -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;
}
Expand Down