Skip to content

Commit 83a894f

Browse files
authored
Fix handling of unicode arguments on windows (#5)
This commit tweaks the `as.exe` tool to handle passing of file paths containing non ascii characters. This is done in the following steps: - on windows, we don't use the `argc` and `argv` from main, since they only support ascii (unlike on linux where they contain utf8 directly). We use instead the windows api `CommandLineToArgvW` to get the arguments in utf16 and then convert it to utf8. - when parsing the arguments and creating `std::filesystem::path`, we cast the `optarg` char* to the C++20 type char8_t* so the path constructor knows the string is in utf8 encoding. - when creating the process, we convert the args from u8 to utf16 and use the wide char version of `CreateProcess`
1 parent b38b5ba commit 83a894f

File tree

6 files changed

+40
-6
lines changed

6 files changed

+40
-6
lines changed

src/gas/gas.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ Gas::ParseArgsResult Gas::parse_arguments (int argc, char **argv, std::unique_pt
247247
// Ignore the arch hack parameter
248248
const char *ret = strstr (optarg, Constants::arch_hack_param);
249249
if (ret == nullptr || ret != optarg) {
250-
input_files.emplace_back (optarg);
250+
// char8_t* cast treats path string as utf8
251+
input_files.emplace_back (reinterpret_cast<char8_t*>(optarg));
251252
}
252253
}
253254
break;
@@ -267,7 +268,7 @@ Gas::ParseArgsResult Gas::parse_arguments (int argc, char **argv, std::unique_pt
267268
break;
268269

269270
case OPTION_O:
270-
_gas_output_file = optarg;
271+
_gas_output_file = reinterpret_cast<char8_t*>(optarg);
271272
break;
272273

273274
case OPTION_MFPU:

src/gas/gas.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ namespace xamarin::android::gas
165165
~Gas ()
166166
{}
167167

168+
void get_command_line (int &argc, char **&argv);
169+
168170
int run (int argc, char **argv);
169171

170172
const std::string& program_name () const noexcept

src/gas/gas.posix.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
using namespace xamarin::android::gas;
88

9+
void Gas::get_command_line ([[maybe_unused]] int &argc, [[maybe_unused]] char **&argv)
10+
{
11+
}
12+
913
void Gas::determine_program_dir ([[maybe_unused]] int argc, char **argv)
1014
{
1115
fs::path program_path { argv[0] };

src/gas/gas.windows.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@
1313

1414
using namespace xamarin::android::gas;
1515

16+
void Gas::get_command_line (int &argc, char **&argv)
17+
{
18+
LPWSTR *argvw = CommandLineToArgvW (GetCommandLineW (), &argc);
19+
argv = new char*[argc + 1];
20+
21+
for (int i = 0; i < argc; i++) {
22+
int size = WideCharToMultiByte (CP_UTF8, 0, argvw [i], -1, NULL, 0, NULL, NULL);
23+
argv [i] = new char [size];
24+
WideCharToMultiByte (CP_UTF8, 0, argvw [i], -1, argv [i], size, NULL, NULL);
25+
}
26+
27+
argv [argc] = NULL;
28+
}
29+
1630
void Gas::determine_program_dir ([[maybe_unused]] int argc, [[maybe_unused]] char **argv)
1731
{
1832
TCHAR buffer[MAX_PATH + 1]{};

src/gas/main.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ xamarin::android::gas::Gas app;
55

66
int main (int argc, char **argv)
77
{
8+
// On windows this obtains the utf8 version of args
9+
app.get_command_line (argc, argv);
810
// TODO: handle exceptions here (use backward for stacktrace perhaps?)
911
return app.run (argc, argv);
1012
}

src/gas/process.windows.cc

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,22 @@ int Process::run (bool print_command_line)
5858
args.append (escape_argument (a));
5959
}
6060

61+
int size = MultiByteToWideChar (CP_UTF8, 0, args.c_str (), -1, NULL , 0);
62+
wchar_t* wargs = new wchar_t [size];
63+
MultiByteToWideChar (CP_UTF8, 0, args.c_str (), -1, wargs, size);
64+
65+
size = MultiByteToWideChar (CP_UTF8, 0, binary.c_str (), -1, NULL , 0);
66+
wchar_t* wbinary = new wchar_t [size];
67+
MultiByteToWideChar (CP_UTF8, 0, binary.c_str (), -1, wbinary, size);
68+
6169
PROCESS_INFORMATION pi {};
62-
STARTUPINFO si {};
70+
STARTUPINFOW si {};
6371
si.cb = sizeof(si);
6472

6573
DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT;
66-
BOOL success = CreateProcess (
67-
binary.c_str (),
68-
const_cast<LPSTR>(args.c_str ()),
74+
BOOL success = CreateProcessW (
75+
wbinary,
76+
wargs,
6977
nullptr, // process security attributes
7078
nullptr, // primary thread security attributes
7179
TRUE, // inherit handles
@@ -76,6 +84,9 @@ int Process::run (bool print_command_line)
7684
&pi
7785
);
7886

87+
delete[] wargs;
88+
delete[] wbinary;
89+
7990
if (!success) {
8091
return Constants::wrapper_exec_failed_error_code;
8192
}

0 commit comments

Comments
 (0)