Skip to content

Commit 8d85440

Browse files
author
Bjoern Johansson
committed
Support unicode file paths on Windows
Add support for unicode file paths on Windows. By taking advantage of weak linking which is applied to many low level IO functions in glibc we can reimplement those functions on Windows to take the UTF-8 coded path and convert it to UTF-16 so we can call the wide string version of the same call. This requires that all paths are UTF-8 encoded which is what we currently do in most places. Some non-UTF-8 usage is fixed by this change. This change also introduces functions such as android_open and android_fopen that allows future code to be written without relying on replacing calls with weak linking. Note that one frequently used function that does not have weak linking enabled is "stat". Hence all uses of stat on the Windows platform have been replaced with android_stat. Change-Id: I32861f81f44e1ddd723429fbdd3d26b28bac293f
1 parent 9ee4be1 commit 8d85440

18 files changed

+497
-70
lines changed

Makefile.android-emu.mk

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ LOCAL_SRC_FILES := \
102102
android/utils/exec.cpp \
103103
android/utils/filelock.c \
104104
android/utils/file_data.c \
105+
android/utils/file_io.cpp \
105106
android/utils/format.cpp \
106107
android/utils/host_bitness.cpp \
107108
android/utils/http_utils.cpp \
@@ -115,7 +116,7 @@ LOCAL_SRC_FILES := \
115116
android/utils/mapfile.c \
116117
android/utils/misc.c \
117118
android/utils/panic.c \
118-
android/utils/path.c \
119+
android/utils/path.cpp \
119120
android/utils/path_system.cpp \
120121
android/utils/property_file.c \
121122
android/utils/reflist.c \
@@ -141,6 +142,7 @@ LOCAL_SRC_FILES += \
141142
android/base/system/Win32Utils.cpp \
142143
android/base/system/Win32UnicodeString.cpp \
143144
android/utils/win32_cmdline_quote.cpp \
145+
android/utils/win32_unicode.cpp \
144146

145147
else
146148
LOCAL_SRC_FILES += \

android/base/testing/TestTempDir.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "android/base/Log.h"
1919
#include "android/base/String.h"
2020
#include "android/base/StringFormat.h"
21+
#include "android/utils/file_io.h"
2122

2223
#ifdef _WIN32
2324
#include <windows.h>
@@ -129,7 +130,7 @@ class TestTempDir {
129130
}
130131
String entry_path = StringFormat("%s/%s", path, entry->d_name);
131132
struct stat stats;
132-
lstat(entry_path.c_str(), &stats);
133+
android_lstat(entry_path.c_str(), &stats);
133134
if (S_ISDIR(stats.st_mode)) {
134135
DeleteRecursive(entry_path);
135136
} else {
@@ -162,17 +163,13 @@ class TestTempDir {
162163
return result;
163164
}
164165

165-
int lstat(const char* path, struct stat* st) {
166-
return stat(path, st);
167-
}
168-
169166
char* mkdtemp(char* path) {
170167
char* sep = ::strrchr(path, '/');
171168
if (sep) {
172169
struct stat st;
173170
int ret;
174171
*sep = '\0'; // temporarily zero-terminate the dirname.
175-
ret = ::stat(path, &st);
172+
ret = android_stat(path, &st);
176173
*sep = '/'; // restore full path.
177174
if (ret < 0) {
178175
return NULL;
@@ -219,7 +216,7 @@ class TestTempDir {
219216
}
220217
// Check that it exists and is a directory.
221218
struct stat st;
222-
int ret = ::stat(result.c_str(), &st);
219+
int ret = android_stat(result.c_str(), &st);
223220
if (ret < 0 || !S_ISDIR(st.st_mode)) {
224221
LOG(FATAL) << "Can't find temporary path: ["
225222
<< result.c_str() << "]";

android/camera/camera-capture-linux.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "android/camera/camera-capture.h"
2424
#include "android/camera/camera-format-converters.h"
2525
#include "android/utils/eintr_wrapper.h"
26+
#include "android/utils/file_io.h"
2627

2728
#include <fcntl.h>
2829
#include <stdio.h>
@@ -498,7 +499,7 @@ _camera_device_open(LinuxCameraDevice* cd)
498499
{
499500
struct stat st;
500501

501-
if (stat(cd->device_name, &st)) {
502+
if (android_stat(cd->device_name, &st)) {
502503
return -1;
503504
}
504505

android/emulation/CpuAccelerator.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "android/base/StringFormat.h"
3333
#include "android/cpu_accelerator.h"
3434
#include "android/utils/file_data.h"
35+
#include "android/utils/file_io.h"
3536
#include "android/utils/path.h"
3637
#include "android/utils/x86_cpuid.h"
3738
#include "target-i386/hax-interface.h"
@@ -238,7 +239,7 @@ int32_t cpuAcceleratorGetHaxVersion(const char* kext_dir[],
238239

239240
for (size_t i = 0; i < kext_dir_count; i++) {
240241
struct stat s;
241-
int err = stat(kext_dir[i], &s);
242+
int err = android_stat(kext_dir[i], &s);
242243
if (err < 0 || !S_ISDIR(s.st_mode)) {
243244
// dir not found
244245
continue;

android/skin/qt/winsys-qt.cpp

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#endif
1616

1717
#include "android/base/system/System.h"
18+
#include "android/base/system/Win32UnicodeString.h"
1819
#include "android/qt/qt_path.h"
1920
#include "android/skin/rect.h"
2021
#include "android/skin/resource.h"
@@ -43,8 +44,16 @@
4344
#include <X11/Xlib.h>
4445
#endif
4546

47+
#ifdef _WIN32
48+
#include <windows.h>
49+
#include <shellapi.h>
50+
#endif
51+
4652
using android::base::System;
4753
using android::base::String;
54+
#ifdef _WIN32
55+
using android::base::Win32UnicodeString;
56+
#endif
4857

4958
#define DEBUG 1
5059

@@ -84,12 +93,6 @@ std::shared_ptr<void> skin_winsys_get_shared_ptr() {
8493
extern void skin_winsys_enter_main_loop(bool no_window, int argc, char** argv) {
8594
D("Starting QT main loop\n");
8695

87-
// Make Qt look at the libraries within this installation
88-
String qtPath = androidQtGetLibraryDir();
89-
QStringList pathList(qtPath.c_str());
90-
QCoreApplication::setLibraryPaths(pathList);
91-
D("Qt lib path: %s\n", qtPath.c_str());
92-
9396
if (!no_window) {
9497
// Give Qt the fonts from our resource file
9598
QFontDatabase fontDb;
@@ -331,6 +334,24 @@ extern void skin_winsys_spawn_thread(bool no_window,
331334
}
332335
}
333336

337+
void skin_winsys_setup_library_paths() {
338+
// Make Qt look at the libraries within this installation
339+
// Despite the fact that we added the plugins directory to the environment
340+
// we have to add it here as well to support extended unicode characters
341+
// in the library path. Without adding the plugin path here that won't work.
342+
// What's even more interesting is that adding the plugin path here is not
343+
// enough in itself. It also has to be set through the environment variable
344+
// or extended unicode characters won't work
345+
String qtLibPath = androidQtGetLibraryDir();
346+
String qtPluginsPath = androidQtGetPluginsDir();
347+
QStringList pathList;
348+
pathList.append(QString::fromUtf8(qtLibPath.c_str()));
349+
pathList.append(QString::fromUtf8(qtPluginsPath.c_str()));
350+
QApplication::setLibraryPaths(pathList);
351+
D("Qt lib path: %s\n", qtLibPath.c_str());
352+
D("Qt plugin path: %s\n", qtPluginsPath.c_str());
353+
}
354+
334355
extern void skin_winsys_start(bool no_window, bool raw_keys) {
335356
GlobalState* g = globalState();
336357
#ifdef Q_OS_LINUX
@@ -339,6 +360,8 @@ extern void skin_winsys_start(bool no_window, bool raw_keys) {
339360
// work (confirmed by grepping through Qt code).
340361
XInitThreads();
341362
#endif
363+
skin_winsys_setup_library_paths();
364+
342365
if (no_window) {
343366
g->app = new QCoreApplication(g->argc, g->argv);
344367
EmulatorQtNoWindow::create();
@@ -394,6 +417,33 @@ extern void skin_winsys_run_ui_update(SkinGenericFunction f, void* data) {
394417
extern "C" int qt_main(int, char**);
395418

396419
int qMain(int argc, char** argv) {
397-
return qt_main(argc, argv);
420+
// The arguments coming in here are encoded in whatever local code page
421+
// Windows is configured with but we need them to be UTF-8 encoded. So we
422+
// use GetCommandLineW and CommandLineToArgvW to get a UTF-16 encoded argv
423+
// which we then convert to UTF-8.
424+
//
425+
// According to the Qt documentation Qt itself doesn't really care about
426+
// these as it also uses GetCommandLineW on Windows so this shouldn't cause
427+
// problems for Qt. But the emulator uses argv[0] to determine the path of
428+
// the emulator executable so we need that to be encoded correctly.
429+
int numArgs = 0;
430+
wchar_t** wideArgv = CommandLineToArgvW(GetCommandLineW(), &numArgs);
431+
if (wideArgv == nullptr) {
432+
// If this fails we can at least give it a try with the local code page
433+
// As long as there are only ANSI characters in the arguments this works
434+
return qt_main(argc, argv);
435+
}
436+
437+
// Store converted strings and pointers to those strings, the pointers are
438+
// what will become the argv for qt_main
439+
std::vector<String> arguments(numArgs);
440+
std::vector<char*> argumentPointers(numArgs);
441+
442+
for (int i = 0; i < numArgs; ++i) {
443+
arguments[i] = Win32UnicodeString::convertToUtf8(wideArgv[i]);
444+
argumentPointers[i] = reinterpret_cast<char*>(arguments[i].data());
445+
}
446+
447+
return qt_main(numArgs, &argumentPointers[0]);
398448
}
399449
#endif // _WIN32

android/utils/exec.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,33 @@
1111

1212
#include "android/utils/exec.h"
1313

14+
#include "android/base/system/Win32UnicodeString.h"
15+
1416
#include <unistd.h>
1517

18+
#ifdef _WIN32
19+
#include <vector>
20+
#endif
21+
1622
#ifdef _WIN32
1723

24+
using android::base::Win32UnicodeString;
25+
1826
int safe_execv(const char* path, char* const* argv) {
19-
const int res = _spawnv(_P_WAIT, path, (const char* const*)argv);
27+
std::vector<Win32UnicodeString> arguments;
28+
for (size_t i = 0; argv[i] != nullptr; ++i) {
29+
arguments.push_back(Win32UnicodeString(argv[i]));
30+
}
31+
// Do this in two steps since the pointers might change because of push_back
32+
// in the loop above.
33+
std::vector<const wchar_t*> argumentPointers;
34+
for (const auto& arg : arguments) {
35+
argumentPointers.push_back(arg.c_str());
36+
}
37+
argumentPointers.push_back(nullptr);
38+
39+
Win32UnicodeString program(path);
40+
const int res = _wspawnv(_P_WAIT, program.c_str(), &argumentPointers[0]);
2041
if (res == -1) {
2142
return -1;
2243
}

0 commit comments

Comments
 (0)