am 367fb503: Merge "adb: win32: Unicode path names, env vars, some console support"
* commit '367fb50333a449f14636f5dc7cd4bc1c81323f59': adb: win32: Unicode path names, env vars, some console support
This commit is contained in:
commit
b8b7118614
7 changed files with 827 additions and 72 deletions
|
|
@ -179,6 +179,8 @@ ifeq ($(HOST_OS),darwin)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(HOST_OS),windows)
|
ifeq ($(HOST_OS),windows)
|
||||||
|
# Use wmain instead of main
|
||||||
|
LOCAL_LDFLAGS += -municode
|
||||||
LOCAL_LDLIBS += -lws2_32 -lgdi32
|
LOCAL_LDLIBS += -lws2_32 -lgdi32
|
||||||
EXTRA_STATIC_LIBS := AdbWinApi
|
EXTRA_STATIC_LIBS := AdbWinApi
|
||||||
endif
|
endif
|
||||||
|
|
|
||||||
21
adb/adb.cpp
21
adb/adb.cpp
|
|
@ -35,6 +35,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <base/logging.h>
|
#include <base/logging.h>
|
||||||
|
#include <base/macros.h>
|
||||||
#include <base/stringprintf.h>
|
#include <base/stringprintf.h>
|
||||||
#include <base/strings.h>
|
#include <base/strings.h>
|
||||||
|
|
||||||
|
|
@ -557,9 +558,9 @@ int launch_server(int server_port)
|
||||||
HANDLE pipe_read, pipe_write;
|
HANDLE pipe_read, pipe_write;
|
||||||
HANDLE stdout_handle, stderr_handle;
|
HANDLE stdout_handle, stderr_handle;
|
||||||
SECURITY_ATTRIBUTES sa;
|
SECURITY_ATTRIBUTES sa;
|
||||||
STARTUPINFO startup;
|
STARTUPINFOW startup;
|
||||||
PROCESS_INFORMATION pinfo;
|
PROCESS_INFORMATION pinfo;
|
||||||
char program_path[ MAX_PATH ];
|
WCHAR program_path[ MAX_PATH ];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
sa.nLength = sizeof(sa);
|
sa.nLength = sizeof(sa);
|
||||||
|
|
@ -635,10 +636,18 @@ int launch_server(int server_port)
|
||||||
ZeroMemory( &pinfo, sizeof(pinfo) );
|
ZeroMemory( &pinfo, sizeof(pinfo) );
|
||||||
|
|
||||||
/* get path of current program */
|
/* get path of current program */
|
||||||
GetModuleFileName( NULL, program_path, sizeof(program_path) );
|
DWORD module_result = GetModuleFileNameW(NULL, program_path,
|
||||||
char args[64];
|
arraysize(program_path));
|
||||||
snprintf(args, sizeof(args), "adb -P %d fork-server server", server_port);
|
if ((module_result == arraysize(program_path)) || (module_result == 0)) {
|
||||||
ret = CreateProcess(
|
// String truncation or some other error.
|
||||||
|
fprintf(stderr, "GetModuleFileNameW() failure, error %ld\n",
|
||||||
|
GetLastError());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
WCHAR args[64];
|
||||||
|
snwprintf(args, arraysize(args),
|
||||||
|
L"adb -P %d fork-server server", server_port);
|
||||||
|
ret = CreateProcessW(
|
||||||
program_path, /* program path */
|
program_path, /* program path */
|
||||||
args,
|
args,
|
||||||
/* the fork-server argument will set the
|
/* the fork-server argument will set the
|
||||||
|
|
|
||||||
|
|
@ -301,11 +301,15 @@ static int get_user_keyfilepath(char *filename, size_t len)
|
||||||
char android_dir[PATH_MAX];
|
char android_dir[PATH_MAX];
|
||||||
struct stat buf;
|
struct stat buf;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
char path[PATH_MAX];
|
std::string home_str;
|
||||||
home = getenv("ANDROID_SDK_HOME");
|
home = getenv("ANDROID_SDK_HOME");
|
||||||
if (!home) {
|
if (!home) {
|
||||||
SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, path);
|
WCHAR path[MAX_PATH];
|
||||||
home = path;
|
if (FAILED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
home_str = narrow(path);
|
||||||
|
home = home_str.c_str();
|
||||||
}
|
}
|
||||||
format = "%s\\%s";
|
format = "%s\\%s";
|
||||||
#else
|
#else
|
||||||
|
|
|
||||||
|
|
@ -82,21 +82,22 @@ static BOOL WINAPI ctrlc_handler(DWORD type) {
|
||||||
|
|
||||||
static std::string GetLogFilePath() {
|
static std::string GetLogFilePath() {
|
||||||
const char log_name[] = "adb.log";
|
const char log_name[] = "adb.log";
|
||||||
char temp_path[MAX_PATH - sizeof(log_name) + 1];
|
WCHAR temp_path[MAX_PATH];
|
||||||
|
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
|
||||||
DWORD nchars = GetTempPath(sizeof(temp_path), temp_path);
|
DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
|
||||||
CHECK_LE(nchars, sizeof(temp_path));
|
if ((nchars >= arraysize(temp_path)) || (nchars == 0)) {
|
||||||
if (nchars == 0) {
|
// If string truncation or some other error.
|
||||||
// TODO(danalbert): Log the error message from FormatError().
|
// TODO(danalbert): Log the error message from
|
||||||
// Windows unfortunately has two errnos, errno and GetLastError(), so
|
// FormatMessage(GetLastError()). Pure Windows APIs only touch
|
||||||
// I'm not sure what to do about PLOG here. Probably better to just
|
// GetLastError(), C Runtime APIs touch errno, so maybe there should be
|
||||||
// ignore it and add a simplified version of FormatError() for use in
|
// WPLOG or PLOGW (which would read GetLastError() instead of errno),
|
||||||
// log messages.
|
// in addition to PLOG, or maybe better to just ignore it and add a
|
||||||
|
// simplified version of FormatMessage() for use in log messages.
|
||||||
LOG(ERROR) << "Error creating log file";
|
LOG(ERROR) << "Error creating log file";
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::string(temp_path) + log_name;
|
return narrow(temp_path) + log_name;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static const char kNullFileName[] = "/dev/null";
|
static const char kNullFileName[] = "/dev/null";
|
||||||
|
|
@ -189,9 +190,35 @@ int adb_main(int is_daemon, int server_port) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static bool _argv_is_utf8 = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (!_argv_is_utf8) {
|
||||||
|
fatal("_argv_is_utf8 is not set, suggesting that wmain was not "
|
||||||
|
"called. Did you forget to link with -municode?");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
adb_sysdeps_init();
|
adb_sysdeps_init();
|
||||||
adb_trace_init(argv);
|
adb_trace_init(argv);
|
||||||
D("Handling commandline()\n");
|
D("Handling commandline()\n");
|
||||||
return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
|
return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
int wmain(int argc, wchar_t **argv) {
|
||||||
|
// Set diagnostic flag to try to detect if the build system was not
|
||||||
|
// configured to call wmain.
|
||||||
|
_argv_is_utf8 = true;
|
||||||
|
|
||||||
|
// Convert args from UTF-16 to UTF-8 and pass that to main().
|
||||||
|
NarrowArgs narrow_args(argc, argv);
|
||||||
|
return main(argc, narrow_args.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -14,21 +14,33 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "sysdeps.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <base/macros.h>
|
||||||
|
|
||||||
#include "adb.h"
|
#include "adb.h"
|
||||||
|
|
||||||
void get_my_path(char *exe, size_t maxLen)
|
// This is not currently called on Windows. Code that only runs on Windows
|
||||||
{
|
// should probably deal with UTF-16 WCHAR/wchar_t since Windows APIs natively
|
||||||
char *r;
|
// work in that format.
|
||||||
|
void get_my_path(char *exe, size_t maxLen) {
|
||||||
|
WCHAR wexe[MAX_PATH];
|
||||||
|
|
||||||
/* XXX: should be GetModuleFileNameA */
|
DWORD module_result = GetModuleFileNameW(NULL, wexe, arraysize(wexe));
|
||||||
if (GetModuleFileName(NULL, exe, maxLen) > 0) {
|
if ((module_result == arraysize(wexe)) || (module_result == 0)) {
|
||||||
r = strrchr(exe, '\\');
|
// String truncation or other error.
|
||||||
if (r != NULL)
|
wexe[0] = '\0';
|
||||||
*r = '\0';
|
}
|
||||||
|
|
||||||
|
// Convert from UTF-16 to UTF-8.
|
||||||
|
const std::string exe_str(narrow(wexe));
|
||||||
|
|
||||||
|
if (exe_str.length() + 1 <= maxLen) {
|
||||||
|
strcpy(exe, exe_str.c_str());
|
||||||
} else {
|
} else {
|
||||||
exe[0] = '\0';
|
exe[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
153
adb/sysdeps.h
153
adb/sysdeps.h
|
|
@ -43,20 +43,35 @@
|
||||||
_rc; })
|
_rc; })
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Some printf-like functions are implemented in terms of
|
||||||
|
// android::base::StringAppendV, so they should use the same attribute for
|
||||||
|
// compile-time format string checking. On Windows, if the mingw version of
|
||||||
|
// vsnprintf is used in StringAppendV, use `gnu_printf' which allows z in %zd
|
||||||
|
// and PRIu64 (and related) to be recognized by the compile-time checking.
|
||||||
|
#define ADB_FORMAT_ARCHETYPE __printf__
|
||||||
|
#ifdef __USE_MINGW_ANSI_STDIO
|
||||||
|
#if __USE_MINGW_ANSI_STDIO
|
||||||
|
#undef ADB_FORMAT_ARCHETYPE
|
||||||
|
#define ADB_FORMAT_ARCHETYPE gnu_printf
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <direct.h>
|
#include <direct.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <utime.h>
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string> // Prototypes for narrow() and widen() use std::(w)string.
|
||||||
|
|
||||||
#include "fdevent.h"
|
#include "fdevent.h"
|
||||||
|
|
||||||
|
|
@ -109,25 +124,11 @@ static __inline__ void close_on_exec(int fd)
|
||||||
|
|
||||||
#define S_ISLNK(m) 0 /* no symlinks on Win32 */
|
#define S_ISLNK(m) 0 /* no symlinks on Win32 */
|
||||||
|
|
||||||
static __inline__ int adb_unlink(const char* path)
|
extern int adb_unlink(const char* path);
|
||||||
{
|
|
||||||
int rc = unlink(path);
|
|
||||||
|
|
||||||
if (rc == -1 && errno == EACCES) {
|
|
||||||
/* unlink returns EACCES when the file is read-only, so we first */
|
|
||||||
/* try to make it writable, then unlink again... */
|
|
||||||
rc = chmod(path, _S_IREAD|_S_IWRITE );
|
|
||||||
if (rc == 0)
|
|
||||||
rc = unlink(path);
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
#undef unlink
|
#undef unlink
|
||||||
#define unlink ___xxx_unlink
|
#define unlink ___xxx_unlink
|
||||||
|
|
||||||
static __inline__ int adb_mkdir(const std::string& path, int mode) {
|
extern int adb_mkdir(const std::string& path, int mode);
|
||||||
return _mkdir(path.c_str());
|
|
||||||
}
|
|
||||||
#undef mkdir
|
#undef mkdir
|
||||||
#define mkdir ___xxx_mkdir
|
#define mkdir ___xxx_mkdir
|
||||||
|
|
||||||
|
|
@ -169,22 +170,7 @@ static __inline__ int adb_open_mode(const char* path, int options, int mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// See the comments for the !defined(_WIN32) version of unix_open().
|
// See the comments for the !defined(_WIN32) version of unix_open().
|
||||||
static __inline__ int unix_open(const char* path, int options,...)
|
extern int unix_open(const char* path, int options, ...);
|
||||||
{
|
|
||||||
if ((options & O_CREAT) == 0)
|
|
||||||
{
|
|
||||||
return open(path, options);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int mode;
|
|
||||||
va_list args;
|
|
||||||
va_start( args, options );
|
|
||||||
mode = va_arg( args, int );
|
|
||||||
va_end( args );
|
|
||||||
return open(path, options, mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#define open ___xxx_unix_open
|
#define open ___xxx_unix_open
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -251,6 +237,107 @@ static __inline__ int adb_is_absolute_host_path(const char* path) {
|
||||||
// Like strerror(), but for Win32 error codes.
|
// Like strerror(), but for Win32 error codes.
|
||||||
std::string SystemErrorCodeToString(DWORD error_code);
|
std::string SystemErrorCodeToString(DWORD error_code);
|
||||||
|
|
||||||
|
// We later define a macro mapping 'stat' to 'adb_stat'. This causes:
|
||||||
|
// struct stat s;
|
||||||
|
// stat(filename, &s);
|
||||||
|
// To turn into the following:
|
||||||
|
// struct adb_stat s;
|
||||||
|
// adb_stat(filename, &s);
|
||||||
|
// To get this to work, we need to make 'struct adb_stat' the same as
|
||||||
|
// 'struct stat'. Note that this definition of 'struct adb_stat' uses the
|
||||||
|
// *current* macro definition of stat, so it may actually be inheriting from
|
||||||
|
// struct _stat32i64 (or some other remapping).
|
||||||
|
struct adb_stat : public stat {};
|
||||||
|
|
||||||
|
static_assert(sizeof(struct adb_stat) == sizeof(struct stat),
|
||||||
|
"structures should be the same");
|
||||||
|
|
||||||
|
extern int adb_stat(const char* f, struct adb_stat* s);
|
||||||
|
|
||||||
|
// stat is already a macro, undefine it so we can redefine it.
|
||||||
|
#undef stat
|
||||||
|
#define stat adb_stat
|
||||||
|
|
||||||
|
// UTF-8 versions of POSIX APIs.
|
||||||
|
extern DIR* adb_opendir(const char* dirname);
|
||||||
|
extern struct dirent* adb_readdir(DIR* dir);
|
||||||
|
extern int adb_closedir(DIR* dir);
|
||||||
|
|
||||||
|
extern int adb_utime(const char *, struct utimbuf *);
|
||||||
|
extern int adb_chmod(const char *, int);
|
||||||
|
|
||||||
|
extern int adb_vfprintf(FILE *stream, const char *format, va_list ap)
|
||||||
|
__attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 0)));
|
||||||
|
extern int adb_fprintf(FILE *stream, const char *format, ...)
|
||||||
|
__attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3)));
|
||||||
|
extern int adb_printf(const char *format, ...)
|
||||||
|
__attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2)));
|
||||||
|
|
||||||
|
extern int adb_fputs(const char* buf, FILE* stream);
|
||||||
|
extern int adb_fputc(int ch, FILE* stream);
|
||||||
|
extern size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb,
|
||||||
|
FILE* stream);
|
||||||
|
|
||||||
|
extern FILE* adb_fopen(const char* f, const char* m);
|
||||||
|
|
||||||
|
extern char* adb_getenv(const char* name);
|
||||||
|
|
||||||
|
extern char* adb_getcwd(char* buf, int size);
|
||||||
|
|
||||||
|
// Remap calls to POSIX APIs to our UTF-8 versions.
|
||||||
|
#define opendir adb_opendir
|
||||||
|
#define readdir adb_readdir
|
||||||
|
#define closedir adb_closedir
|
||||||
|
#define rewinddir rewinddir_utf8_not_yet_implemented
|
||||||
|
#define telldir telldir_utf8_not_yet_implemented
|
||||||
|
#define seekdir seekdir_utf8_not_yet_implemented
|
||||||
|
|
||||||
|
#define utime adb_utime
|
||||||
|
#define chmod adb_chmod
|
||||||
|
|
||||||
|
#define vfprintf adb_vfprintf
|
||||||
|
#define fprintf adb_fprintf
|
||||||
|
#define printf adb_printf
|
||||||
|
#define fputs adb_fputs
|
||||||
|
#define fputc adb_fputc
|
||||||
|
#define fwrite adb_fwrite
|
||||||
|
|
||||||
|
#define fopen adb_fopen
|
||||||
|
|
||||||
|
#define getenv adb_getenv
|
||||||
|
#define putenv putenv_utf8_not_yet_implemented
|
||||||
|
#define setenv setenv_utf8_not_yet_implemented
|
||||||
|
#define unsetenv unsetenv_utf8_not_yet_implemented
|
||||||
|
|
||||||
|
#define getcwd adb_getcwd
|
||||||
|
|
||||||
|
// Convert from UTF-8 to UTF-16, typically used to convert char strings into
|
||||||
|
// wchar_t strings that can be passed to wchar_t-based OS and C Runtime APIs
|
||||||
|
// on Windows.
|
||||||
|
extern std::wstring widen(const std::string& utf8);
|
||||||
|
extern std::wstring widen(const char* utf8);
|
||||||
|
|
||||||
|
// Convert from UTF-16 to UTF-8, typically used to convert strings from OS and
|
||||||
|
// C Runtime APIs that return wchar_t, to a format for our char-based data
|
||||||
|
// structures.
|
||||||
|
extern std::string narrow(const std::wstring& utf16);
|
||||||
|
extern std::string narrow(const wchar_t* utf16);
|
||||||
|
|
||||||
|
// Helper class to convert UTF-16 argv from wmain() to UTF-8 args that can be
|
||||||
|
// passed to main().
|
||||||
|
class NarrowArgs {
|
||||||
|
public:
|
||||||
|
NarrowArgs(int argc, wchar_t** argv);
|
||||||
|
~NarrowArgs();
|
||||||
|
|
||||||
|
inline char** data() {
|
||||||
|
return narrow_args;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
char** narrow_args;
|
||||||
|
};
|
||||||
|
|
||||||
#else /* !_WIN32 a.k.a. Unix */
|
#else /* !_WIN32 a.k.a. Unix */
|
||||||
|
|
||||||
#include "fdevent.h"
|
#include "fdevent.h"
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <cutils/sockets.h>
|
#include <cutils/sockets.h>
|
||||||
|
|
||||||
|
|
@ -124,13 +125,13 @@ void *load_file(const char *fn, unsigned *_sz)
|
||||||
char *data;
|
char *data;
|
||||||
DWORD file_size;
|
DWORD file_size;
|
||||||
|
|
||||||
file = CreateFile( fn,
|
file = CreateFileW( widen(fn).c_str(),
|
||||||
GENERIC_READ,
|
GENERIC_READ,
|
||||||
FILE_SHARE_READ,
|
FILE_SHARE_READ,
|
||||||
NULL,
|
NULL,
|
||||||
OPEN_EXISTING,
|
OPEN_EXISTING,
|
||||||
0,
|
0,
|
||||||
NULL );
|
NULL );
|
||||||
|
|
||||||
if (file == INVALID_HANDLE_VALUE)
|
if (file == INVALID_HANDLE_VALUE)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -406,8 +407,8 @@ int adb_open(const char* path, int options)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
f->fh_handle = CreateFile( path, desiredAccess, shareMode, NULL, OPEN_EXISTING,
|
f->fh_handle = CreateFileW( widen(path).c_str(), desiredAccess, shareMode,
|
||||||
0, NULL );
|
NULL, OPEN_EXISTING, 0, NULL );
|
||||||
|
|
||||||
if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
|
if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
|
||||||
const DWORD err = GetLastError();
|
const DWORD err = GetLastError();
|
||||||
|
|
@ -447,9 +448,10 @@ int adb_creat(const char* path, int mode)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
f->fh_handle = CreateFile( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
f->fh_handle = CreateFileW( widen(path).c_str(), GENERIC_WRITE,
|
||||||
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||||
NULL );
|
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
|
||||||
|
NULL );
|
||||||
|
|
||||||
if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
|
if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
|
||||||
const DWORD err = GetLastError();
|
const DWORD err = GetLastError();
|
||||||
|
|
@ -3175,3 +3177,615 @@ int unix_read(int fd, void* buf, size_t len) {
|
||||||
#pragma pop_macro("read")
|
#pragma pop_macro("read")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
/**************************************************************************/
|
||||||
|
/***** *****/
|
||||||
|
/***** Unicode support *****/
|
||||||
|
/***** *****/
|
||||||
|
/**************************************************************************/
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
// This implements support for using files with Unicode filenames and for
|
||||||
|
// outputting Unicode text to a Win32 console window. This is inspired from
|
||||||
|
// http://utf8everywhere.org/.
|
||||||
|
//
|
||||||
|
// Background
|
||||||
|
// ----------
|
||||||
|
//
|
||||||
|
// On POSIX systems, to deal with files with Unicode filenames, just pass UTF-8
|
||||||
|
// filenames to APIs such as open(). This works because filenames are largely
|
||||||
|
// opaque 'cookies' (perhaps excluding path separators).
|
||||||
|
//
|
||||||
|
// On Windows, the native file APIs such as CreateFileW() take 2-byte wchar_t
|
||||||
|
// UTF-16 strings. There is an API, CreateFileA() that takes 1-byte char
|
||||||
|
// strings, but the strings are in the ANSI codepage and not UTF-8. (The
|
||||||
|
// CreateFile() API is really just a macro that adds the W/A based on whether
|
||||||
|
// the UNICODE preprocessor symbol is defined).
|
||||||
|
//
|
||||||
|
// Options
|
||||||
|
// -------
|
||||||
|
//
|
||||||
|
// Thus, to write a portable program, there are a few options:
|
||||||
|
//
|
||||||
|
// 1. Write the program with wchar_t filenames (wchar_t path[256];).
|
||||||
|
// For Windows, just call CreateFileW(). For POSIX, write a wrapper openW()
|
||||||
|
// that takes a wchar_t string, converts it to UTF-8 and then calls the real
|
||||||
|
// open() API.
|
||||||
|
//
|
||||||
|
// 2. Write the program with a TCHAR typedef that is 2 bytes on Windows and
|
||||||
|
// 1 byte on POSIX. Make T-* wrappers for various OS APIs and call those,
|
||||||
|
// potentially touching a lot of code.
|
||||||
|
//
|
||||||
|
// 3. Write the program with a 1-byte char filenames (char path[256];) that are
|
||||||
|
// UTF-8. For POSIX, just call open(). For Windows, write a wrapper that
|
||||||
|
// takes a UTF-8 string, converts it to UTF-16 and then calls the real OS
|
||||||
|
// or C Runtime API.
|
||||||
|
//
|
||||||
|
// The Choice
|
||||||
|
// ----------
|
||||||
|
//
|
||||||
|
// The code below chooses option 3, the UTF-8 everywhere strategy. It
|
||||||
|
// introduces narrow() which converts UTF-16 to UTF-8. This is used by the
|
||||||
|
// NarrowArgs helper class that is used to convert wmain() args into UTF-8
|
||||||
|
// args that are passed to main() at the beginning of program startup. We also
|
||||||
|
// introduce widen() which converts from UTF-8 to UTF-16. This is used to
|
||||||
|
// implement wrappers below that call UTF-16 OS and C Runtime APIs.
|
||||||
|
//
|
||||||
|
// Unicode console output
|
||||||
|
// ----------------------
|
||||||
|
//
|
||||||
|
// The way to output Unicode to a Win32 console window is to call
|
||||||
|
// WriteConsoleW() with UTF-16 text. (The user must also choose a proper font
|
||||||
|
// such as Lucida Console or Consolas, and in the case of Chinese, must go to
|
||||||
|
// the Control Panel and change the "system locale" to Chinese, which allows
|
||||||
|
// a Chinese font to be used in console windows.)
|
||||||
|
//
|
||||||
|
// The problem is getting the C Runtime to make fprintf and related APIs call
|
||||||
|
// WriteConsoleW() under the covers. The C Runtime API, _setmode() sounds
|
||||||
|
// promising, but the various modes have issues:
|
||||||
|
//
|
||||||
|
// 1. _setmode(_O_TEXT) (the default) does not use WriteConsoleW() so UTF-8 and
|
||||||
|
// UTF-16 do not display properly.
|
||||||
|
// 2. _setmode(_O_BINARY) does not use WriteConsoleW() and the text comes out
|
||||||
|
// totally wrong.
|
||||||
|
// 3. _setmode(_O_U8TEXT) seems to cause the C Runtime _invalid_parameter
|
||||||
|
// handler to be called (upon a later I/O call), aborting the process.
|
||||||
|
// 4. _setmode(_O_U16TEXT) and _setmode(_O_WTEXT) cause non-wide printf/fprintf
|
||||||
|
// to output nothing.
|
||||||
|
//
|
||||||
|
// So the only solution is to write our own adb_fprintf() that converts UTF-8
|
||||||
|
// to UTF-16 and then calls WriteConsoleW().
|
||||||
|
|
||||||
|
|
||||||
|
// Function prototype because attributes cannot be placed on func definitions.
|
||||||
|
static void _widen_fatal(const char *fmt, ...)
|
||||||
|
__attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2)));
|
||||||
|
|
||||||
|
// A version of fatal() that does not call adb_(v)fprintf(), so it can be
|
||||||
|
// called from those functions.
|
||||||
|
static void _widen_fatal(const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
// If (v)fprintf are macros that point to adb_(v)fprintf, when random adb
|
||||||
|
// code calls (v)fprintf, it may end up calling adb_(v)fprintf, which then
|
||||||
|
// calls _widen_fatal(). So then how does _widen_fatal() output a error?
|
||||||
|
// By directly calling real C Runtime APIs that don't properly output
|
||||||
|
// Unicode, but will be able to get a comprehendible message out. To do
|
||||||
|
// this, make sure we don't call (v)fprintf macros by undefining them.
|
||||||
|
#pragma push_macro("fprintf")
|
||||||
|
#pragma push_macro("vfprintf")
|
||||||
|
#undef fprintf
|
||||||
|
#undef vfprintf
|
||||||
|
fprintf(stderr, "error: ");
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
#pragma pop_macro("vfprintf")
|
||||||
|
#pragma pop_macro("fprintf")
|
||||||
|
va_end(ap);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Consider implementing widen() and narrow() out of std::wstring_convert
|
||||||
|
// once libcxx is supported on Windows. Or, consider libutils/Unicode.cpp.
|
||||||
|
|
||||||
|
// Convert from UTF-8 to UTF-16. A size of -1 specifies a NULL terminated
|
||||||
|
// string. Any other size specifies the number of chars to convert, excluding
|
||||||
|
// any NULL terminator (if you're passing an explicit size, you probably don't
|
||||||
|
// have a NULL terminated string in the first place).
|
||||||
|
std::wstring widen(const char* utf8, const int size) {
|
||||||
|
const int chars_to_convert = MultiByteToWideChar(CP_UTF8, 0, utf8, size,
|
||||||
|
NULL, 0);
|
||||||
|
if (chars_to_convert <= 0) {
|
||||||
|
// UTF-8 to UTF-16 should be lossless, so we don't expect this to fail.
|
||||||
|
_widen_fatal("MultiByteToWideChar failed counting: %d, "
|
||||||
|
"GetLastError: %lu", chars_to_convert, GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring utf16;
|
||||||
|
size_t chars_to_allocate = chars_to_convert;
|
||||||
|
if (size == -1) {
|
||||||
|
// chars_to_convert includes a NULL terminator, so subtract space
|
||||||
|
// for that because resize() includes that itself.
|
||||||
|
--chars_to_allocate;
|
||||||
|
}
|
||||||
|
utf16.resize(chars_to_allocate);
|
||||||
|
|
||||||
|
// This uses &string[0] to get write-access to the entire string buffer
|
||||||
|
// which may be assuming that the chars are all contiguous, but it seems
|
||||||
|
// to work and saves us the hassle of using a temporary
|
||||||
|
// std::vector<wchar_t>.
|
||||||
|
const int result = MultiByteToWideChar(CP_UTF8, 0, utf8, size, &utf16[0],
|
||||||
|
chars_to_convert);
|
||||||
|
if (result != chars_to_convert) {
|
||||||
|
// UTF-8 to UTF-16 should be lossless, so we don't expect this to fail.
|
||||||
|
_widen_fatal("MultiByteToWideChar failed conversion: %d, "
|
||||||
|
"GetLastError: %lu", result, GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a size was passed in (size != -1), then the string is NULL terminated
|
||||||
|
// by a NULL char that was written by std::string::resize(). If size == -1,
|
||||||
|
// then MultiByteToWideChar() read a NULL terminator from the original
|
||||||
|
// string and converted it to a NULL UTF-16 char in the output.
|
||||||
|
|
||||||
|
return utf16;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a NULL terminated string from UTF-8 to UTF-16.
|
||||||
|
std::wstring widen(const char* utf8) {
|
||||||
|
// Pass -1 to let widen() determine the string length.
|
||||||
|
return widen(utf8, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from UTF-8 to UTF-16.
|
||||||
|
std::wstring widen(const std::string& utf8) {
|
||||||
|
return widen(utf8.c_str(), utf8.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from UTF-16 to UTF-8.
|
||||||
|
std::string narrow(const std::wstring& utf16) {
|
||||||
|
return narrow(utf16.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from UTF-16 to UTF-8.
|
||||||
|
std::string narrow(const wchar_t* utf16) {
|
||||||
|
const int chars_required = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL,
|
||||||
|
0, NULL, NULL);
|
||||||
|
if (chars_required <= 0) {
|
||||||
|
// UTF-16 to UTF-8 should be lossless, so we don't expect this to fail.
|
||||||
|
fatal("WideCharToMultiByte failed counting: %d, GetLastError: %d",
|
||||||
|
chars_required, GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string utf8;
|
||||||
|
// Subtract space for the NULL terminator because resize() includes
|
||||||
|
// that itself. Note that this could potentially throw a std::bad_alloc
|
||||||
|
// exception.
|
||||||
|
utf8.resize(chars_required - 1);
|
||||||
|
|
||||||
|
// This uses &string[0] to get write-access to the entire string buffer
|
||||||
|
// which may be assuming that the chars are all contiguous, but it seems
|
||||||
|
// to work and saves us the hassle of using a temporary
|
||||||
|
// std::vector<char>.
|
||||||
|
const int result = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, &utf8[0],
|
||||||
|
chars_required, NULL, NULL);
|
||||||
|
if (result != chars_required) {
|
||||||
|
// UTF-16 to UTF-8 should be lossless, so we don't expect this to fail.
|
||||||
|
fatal("WideCharToMultiByte failed conversion: %d, GetLastError: %d",
|
||||||
|
result, GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
return utf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor for helper class to convert wmain() UTF-16 args to UTF-8 to
|
||||||
|
// be passed to main().
|
||||||
|
NarrowArgs::NarrowArgs(const int argc, wchar_t** const argv) {
|
||||||
|
narrow_args = new char*[argc + 1];
|
||||||
|
|
||||||
|
for (int i = 0; i < argc; ++i) {
|
||||||
|
narrow_args[i] = strdup(narrow(argv[i]).c_str());
|
||||||
|
}
|
||||||
|
narrow_args[argc] = nullptr; // terminate
|
||||||
|
}
|
||||||
|
|
||||||
|
NarrowArgs::~NarrowArgs() {
|
||||||
|
if (narrow_args != nullptr) {
|
||||||
|
for (char** argp = narrow_args; *argp != nullptr; ++argp) {
|
||||||
|
free(*argp);
|
||||||
|
}
|
||||||
|
delete[] narrow_args;
|
||||||
|
narrow_args = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int unix_open(const char* path, int options, ...) {
|
||||||
|
if ((options & O_CREAT) == 0) {
|
||||||
|
return _wopen(widen(path).c_str(), options);
|
||||||
|
} else {
|
||||||
|
int mode;
|
||||||
|
va_list args;
|
||||||
|
va_start(args, options);
|
||||||
|
mode = va_arg(args, int);
|
||||||
|
va_end(args);
|
||||||
|
return _wopen(widen(path).c_str(), options, mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of stat() that takes a UTF-8 path.
|
||||||
|
int adb_stat(const char* f, struct adb_stat* s) {
|
||||||
|
#pragma push_macro("wstat")
|
||||||
|
// This definition of wstat seems to be missing from <sys/stat.h>.
|
||||||
|
#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
|
||||||
|
#ifdef _USE_32BIT_TIME_T
|
||||||
|
#define wstat _wstat32i64
|
||||||
|
#else
|
||||||
|
#define wstat _wstat64
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
// <sys/stat.h> has a function prototype for wstat() that should be available.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return wstat(widen(f).c_str(), s);
|
||||||
|
|
||||||
|
#pragma pop_macro("wstat")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of opendir() that takes a UTF-8 path.
|
||||||
|
DIR* adb_opendir(const char* name) {
|
||||||
|
// Just cast _WDIR* to DIR*. This doesn't work if the caller reads any of
|
||||||
|
// the fields, but right now all the callers treat the structure as
|
||||||
|
// opaque.
|
||||||
|
return reinterpret_cast<DIR*>(_wopendir(widen(name).c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of readdir() that returns UTF-8 paths.
|
||||||
|
struct dirent* adb_readdir(DIR* dir) {
|
||||||
|
_WDIR* const wdir = reinterpret_cast<_WDIR*>(dir);
|
||||||
|
struct _wdirent* const went = _wreaddir(wdir);
|
||||||
|
if (went == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// Convert from UTF-16 to UTF-8.
|
||||||
|
const std::string name_utf8(narrow(went->d_name));
|
||||||
|
|
||||||
|
// Cast the _wdirent* to dirent* and overwrite the d_name field (which has
|
||||||
|
// space for UTF-16 wchar_t's) with UTF-8 char's.
|
||||||
|
struct dirent* ent = reinterpret_cast<struct dirent*>(went);
|
||||||
|
|
||||||
|
if (name_utf8.length() + 1 > sizeof(went->d_name)) {
|
||||||
|
// Name too big to fit in existing buffer.
|
||||||
|
errno = ENOMEM;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that sizeof(_wdirent::d_name) is bigger than sizeof(dirent::d_name)
|
||||||
|
// because _wdirent contains wchar_t instead of char. So even if name_utf8
|
||||||
|
// can fit in _wdirent::d_name, the resulting dirent::d_name field may be
|
||||||
|
// bigger than the caller expects because they expect a dirent structure
|
||||||
|
// which has a smaller d_name field. Ignore this since the caller should be
|
||||||
|
// resilient.
|
||||||
|
|
||||||
|
// Rewrite the UTF-16 d_name field to UTF-8.
|
||||||
|
strcpy(ent->d_name, name_utf8.c_str());
|
||||||
|
|
||||||
|
return ent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of closedir() to go with our version of adb_opendir().
|
||||||
|
int adb_closedir(DIR* dir) {
|
||||||
|
return _wclosedir(reinterpret_cast<_WDIR*>(dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of unlink() that takes a UTF-8 path.
|
||||||
|
int adb_unlink(const char* path) {
|
||||||
|
const std::wstring wpath(widen(path));
|
||||||
|
|
||||||
|
int rc = _wunlink(wpath.c_str());
|
||||||
|
|
||||||
|
if (rc == -1 && errno == EACCES) {
|
||||||
|
/* unlink returns EACCES when the file is read-only, so we first */
|
||||||
|
/* try to make it writable, then unlink again... */
|
||||||
|
rc = _wchmod(wpath.c_str(), _S_IREAD | _S_IWRITE);
|
||||||
|
if (rc == 0)
|
||||||
|
rc = _wunlink(wpath.c_str());
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of mkdir() that takes a UTF-8 path.
|
||||||
|
int adb_mkdir(const std::string& path, int mode) {
|
||||||
|
return _wmkdir(widen(path.c_str()).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of utime() that takes a UTF-8 path.
|
||||||
|
int adb_utime(const char* path, struct utimbuf* u) {
|
||||||
|
static_assert(sizeof(struct utimbuf) == sizeof(struct _utimbuf),
|
||||||
|
"utimbuf and _utimbuf should be the same size because they both "
|
||||||
|
"contain the same types, namely time_t");
|
||||||
|
return _wutime(widen(path).c_str(), reinterpret_cast<struct _utimbuf*>(u));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of chmod() that takes a UTF-8 path.
|
||||||
|
int adb_chmod(const char* path, int mode) {
|
||||||
|
return _wchmod(widen(path).c_str(), mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal function to get a Win32 console HANDLE from a C Runtime FILE*.
|
||||||
|
static HANDLE _get_console_handle(FILE* const stream) {
|
||||||
|
// Get a C Runtime file descriptor number from the FILE* structure.
|
||||||
|
const int fd = fileno(stream);
|
||||||
|
if (fd < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it is not a "character device", it is probably a file and not a
|
||||||
|
// console. Do this check early because it is probably cheap. Still do more
|
||||||
|
// checks after this since there are devices that pass this test, but are
|
||||||
|
// not a console, such as NUL, the Windows /dev/null equivalent (I think).
|
||||||
|
if (!isatty(fd)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a C Runtime file descriptor number, get the underlying OS
|
||||||
|
// file handle.
|
||||||
|
const intptr_t osfh = _get_osfhandle(fd);
|
||||||
|
if (osfh == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HANDLE h = reinterpret_cast<const HANDLE>(osfh);
|
||||||
|
|
||||||
|
DWORD old_mode = 0;
|
||||||
|
if (!GetConsoleMode(h, &old_mode)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If GetConsoleMode() was successful, assume this is a console.
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal helper function to write UTF-8 bytes to a console. Returns -1
|
||||||
|
// on error.
|
||||||
|
static int _console_write_utf8(const char* buf, size_t size, FILE* stream,
|
||||||
|
HANDLE console) {
|
||||||
|
// Convert from UTF-8 to UTF-16.
|
||||||
|
// This could throw std::bad_alloc.
|
||||||
|
const std::wstring output(widen(buf, size));
|
||||||
|
|
||||||
|
// Note that this does not do \n => \r\n translation because that
|
||||||
|
// doesn't seem necessary for the Windows console. For the Windows
|
||||||
|
// console \r moves to the beginning of the line and \n moves to a new
|
||||||
|
// line.
|
||||||
|
|
||||||
|
// Flush any stream buffering so that our output is afterwards which
|
||||||
|
// makes sense because our call is afterwards.
|
||||||
|
(void)fflush(stream);
|
||||||
|
|
||||||
|
// Write UTF-16 to the console.
|
||||||
|
DWORD written = 0;
|
||||||
|
if (!WriteConsoleW(console, output.c_str(), output.length(), &written,
|
||||||
|
NULL)) {
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the number of UTF-16 chars written, which might be different
|
||||||
|
// than the number of UTF-8 chars passed in. It doesn't seem practical to
|
||||||
|
// get this count correct.
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function prototype because attributes cannot be placed on func definitions.
|
||||||
|
static int _console_vfprintf(const HANDLE console, FILE* stream,
|
||||||
|
const char *format, va_list ap)
|
||||||
|
__attribute__((__format__(ADB_FORMAT_ARCHETYPE, 3, 0)));
|
||||||
|
|
||||||
|
// Internal function to format a UTF-8 string and write it to a Win32 console.
|
||||||
|
// Returns -1 on error.
|
||||||
|
static int _console_vfprintf(const HANDLE console, FILE* stream,
|
||||||
|
const char *format, va_list ap) {
|
||||||
|
std::string output_utf8;
|
||||||
|
|
||||||
|
// Format the string.
|
||||||
|
// This could throw std::bad_alloc.
|
||||||
|
android::base::StringAppendV(&output_utf8, format, ap);
|
||||||
|
|
||||||
|
return _console_write_utf8(output_utf8.c_str(), output_utf8.length(),
|
||||||
|
stream, console);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of vfprintf() that takes UTF-8 and can write Unicode to a
|
||||||
|
// Windows console.
|
||||||
|
int adb_vfprintf(FILE *stream, const char *format, va_list ap) {
|
||||||
|
const HANDLE console = _get_console_handle(stream);
|
||||||
|
|
||||||
|
// If there is an associated Win32 console, write to it specially,
|
||||||
|
// otherwise defer to the regular C Runtime, passing it UTF-8.
|
||||||
|
if (console != NULL) {
|
||||||
|
return _console_vfprintf(console, stream, format, ap);
|
||||||
|
} else {
|
||||||
|
// If vfprintf is a macro, undefine it, so we can call the real
|
||||||
|
// C Runtime API.
|
||||||
|
#pragma push_macro("vfprintf")
|
||||||
|
#undef vfprintf
|
||||||
|
return vfprintf(stream, format, ap);
|
||||||
|
#pragma pop_macro("vfprintf")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of fprintf() that takes UTF-8 and can write Unicode to a
|
||||||
|
// Windows console.
|
||||||
|
int adb_fprintf(FILE *stream, const char *format, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
const int result = adb_vfprintf(stream, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of printf() that takes UTF-8 and can write Unicode to a
|
||||||
|
// Windows console.
|
||||||
|
int adb_printf(const char *format, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
const int result = adb_vfprintf(stdout, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of fputs() that takes UTF-8 and can write Unicode to a
|
||||||
|
// Windows console.
|
||||||
|
int adb_fputs(const char* buf, FILE* stream) {
|
||||||
|
// adb_fprintf returns -1 on error, which is conveniently the same as EOF
|
||||||
|
// which fputs (and hence adb_fputs) should return on error.
|
||||||
|
return adb_fprintf(stream, "%s", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of fputc() that takes UTF-8 and can write Unicode to a
|
||||||
|
// Windows console.
|
||||||
|
int adb_fputc(int ch, FILE* stream) {
|
||||||
|
const int result = adb_fprintf(stream, "%c", ch);
|
||||||
|
if (result <= 0) {
|
||||||
|
// If there was an error, or if nothing was printed (which should be an
|
||||||
|
// error), return an error, which fprintf signifies with EOF.
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
// For success, fputc returns the char, cast to unsigned char, then to int.
|
||||||
|
return static_cast<unsigned char>(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal function to write UTF-8 to a Win32 console. Returns the number of
|
||||||
|
// items (of length size) written. On error, returns a short item count or 0.
|
||||||
|
static size_t _console_fwrite(const void* ptr, size_t size, size_t nmemb,
|
||||||
|
FILE* stream, HANDLE console) {
|
||||||
|
// TODO: Note that a Unicode character could be several UTF-8 bytes. But
|
||||||
|
// if we're passed only some of the bytes of a character (for example, from
|
||||||
|
// the network socket for adb shell), we won't be able to convert the char
|
||||||
|
// to a complete UTF-16 char (or surrogate pair), so the output won't look
|
||||||
|
// right.
|
||||||
|
//
|
||||||
|
// To fix this, see libutils/Unicode.cpp for hints on decoding UTF-8.
|
||||||
|
//
|
||||||
|
// For now we ignore this problem because the alternative is that we'd have
|
||||||
|
// to parse UTF-8 and buffer things up (doable). At least this is better
|
||||||
|
// than what we had before -- always incorrect multi-byte UTF-8 output.
|
||||||
|
int result = _console_write_utf8(reinterpret_cast<const char*>(ptr),
|
||||||
|
size * nmemb, stream, console);
|
||||||
|
if (result == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return result / size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of fwrite() that takes UTF-8 and can write Unicode to a
|
||||||
|
// Windows console.
|
||||||
|
size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream) {
|
||||||
|
const HANDLE console = _get_console_handle(stream);
|
||||||
|
|
||||||
|
// If there is an associated Win32 console, write to it specially,
|
||||||
|
// otherwise defer to the regular C Runtime, passing it UTF-8.
|
||||||
|
if (console != NULL) {
|
||||||
|
return _console_fwrite(ptr, size, nmemb, stream, console);
|
||||||
|
} else {
|
||||||
|
// If fwrite is a macro, undefine it, so we can call the real
|
||||||
|
// C Runtime API.
|
||||||
|
#pragma push_macro("fwrite")
|
||||||
|
#undef fwrite
|
||||||
|
return fwrite(ptr, size, nmemb, stream);
|
||||||
|
#pragma pop_macro("fwrite")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of fopen() that takes a UTF-8 filename and can access a file with
|
||||||
|
// a Unicode filename.
|
||||||
|
FILE* adb_fopen(const char* f, const char* m) {
|
||||||
|
return _wfopen(widen(f).c_str(), widen(m).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shadow UTF-8 environment variable name/value pairs that are created from
|
||||||
|
// _wenviron the first time that adb_getenv() is called. Note that this is not
|
||||||
|
// currently updated if putenv, setenv, unsetenv are called.
|
||||||
|
static std::unordered_map<std::string, char*> g_environ_utf8;
|
||||||
|
|
||||||
|
// Make sure that shadow UTF-8 environment variables are setup.
|
||||||
|
static void _ensure_env_setup() {
|
||||||
|
// If some name/value pairs exist, then we've already done the setup below.
|
||||||
|
if (g_environ_utf8.size() != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read name/value pairs from UTF-16 _wenviron and write new name/value
|
||||||
|
// pairs to UTF-8 g_environ_utf8. Note that it probably does not make sense
|
||||||
|
// to use the D() macro here because that tracing only works if the
|
||||||
|
// ADB_TRACE environment variable is setup, but that env var can't be read
|
||||||
|
// until this code completes.
|
||||||
|
for (wchar_t** env = _wenviron; *env != nullptr; ++env) {
|
||||||
|
wchar_t* const equal = wcschr(*env, L'=');
|
||||||
|
if (equal == nullptr) {
|
||||||
|
// Malformed environment variable with no equal sign. Shouldn't
|
||||||
|
// really happen, but we should be resilient to this.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string name_utf8(narrow(std::wstring(*env, equal - *env)));
|
||||||
|
char* const value_utf8 = strdup(narrow(equal + 1).c_str());
|
||||||
|
|
||||||
|
// Overwrite any duplicate name, but there shouldn't be a dup in the
|
||||||
|
// first place.
|
||||||
|
g_environ_utf8[name_utf8] = value_utf8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of getenv() that takes a UTF-8 environment variable name and
|
||||||
|
// retrieves a UTF-8 value.
|
||||||
|
char* adb_getenv(const char* name) {
|
||||||
|
_ensure_env_setup();
|
||||||
|
|
||||||
|
std::unordered_map<std::string, char*>::const_iterator it =
|
||||||
|
g_environ_utf8.find(std::string(name));
|
||||||
|
if (it == g_environ_utf8.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of getcwd() that returns the current working directory in UTF-8.
|
||||||
|
char* adb_getcwd(char* buf, int size) {
|
||||||
|
wchar_t* wbuf = _wgetcwd(nullptr, 0);
|
||||||
|
if (wbuf == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string buf_utf8(narrow(wbuf));
|
||||||
|
free(wbuf);
|
||||||
|
wbuf = nullptr;
|
||||||
|
|
||||||
|
// If size was specified, make sure all the chars will fit.
|
||||||
|
if (size != 0) {
|
||||||
|
if (size < static_cast<int>(buf_utf8.length() + 1)) {
|
||||||
|
errno = ERANGE;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If buf was not specified, allocate storage.
|
||||||
|
if (buf == nullptr) {
|
||||||
|
if (size == 0) {
|
||||||
|
size = buf_utf8.length() + 1;
|
||||||
|
}
|
||||||
|
buf = reinterpret_cast<char*>(malloc(size));
|
||||||
|
if (buf == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destination buffer was allocated with enough space, or we've already
|
||||||
|
// checked an existing buffer size for enough space.
|
||||||
|
strcpy(buf, buf_utf8.c_str());
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue