am b8b71186: am 367fb503: Merge "adb: win32: Unicode path names, env vars, some console support"

* commit 'b8b7118614e8d6bd9a683ecd624915519f8933a9':
  adb: win32: Unicode path names, env vars, some console support
This commit is contained in:
Elliott Hughes 2015-08-01 00:06:48 +00:00 committed by Android Git Automerger
commit 66429b34d0
7 changed files with 827 additions and 72 deletions

View file

@ -179,6 +179,8 @@ ifeq ($(HOST_OS),darwin)
endif
ifeq ($(HOST_OS),windows)
# Use wmain instead of main
LOCAL_LDFLAGS += -municode
LOCAL_LDLIBS += -lws2_32 -lgdi32
EXTRA_STATIC_LIBS := AdbWinApi
endif

View file

@ -35,6 +35,7 @@
#include <unordered_map>
#include <base/logging.h>
#include <base/macros.h>
#include <base/stringprintf.h>
#include <base/strings.h>
@ -557,9 +558,9 @@ int launch_server(int server_port)
HANDLE pipe_read, pipe_write;
HANDLE stdout_handle, stderr_handle;
SECURITY_ATTRIBUTES sa;
STARTUPINFO startup;
STARTUPINFOW startup;
PROCESS_INFORMATION pinfo;
char program_path[ MAX_PATH ];
WCHAR program_path[ MAX_PATH ];
int ret;
sa.nLength = sizeof(sa);
@ -635,10 +636,18 @@ int launch_server(int server_port)
ZeroMemory( &pinfo, sizeof(pinfo) );
/* get path of current program */
GetModuleFileName( NULL, program_path, sizeof(program_path) );
char args[64];
snprintf(args, sizeof(args), "adb -P %d fork-server server", server_port);
ret = CreateProcess(
DWORD module_result = GetModuleFileNameW(NULL, program_path,
arraysize(program_path));
if ((module_result == arraysize(program_path)) || (module_result == 0)) {
// 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 */
args,
/* the fork-server argument will set the

View file

@ -301,11 +301,15 @@ static int get_user_keyfilepath(char *filename, size_t len)
char android_dir[PATH_MAX];
struct stat buf;
#ifdef _WIN32
char path[PATH_MAX];
std::string home_str;
home = getenv("ANDROID_SDK_HOME");
if (!home) {
SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, path);
home = path;
WCHAR path[MAX_PATH];
if (FAILED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path))) {
return -1;
}
home_str = narrow(path);
home = home_str.c_str();
}
format = "%s\\%s";
#else

View file

@ -82,21 +82,22 @@ static BOOL WINAPI ctrlc_handler(DWORD type) {
static std::string GetLogFilePath() {
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
DWORD nchars = GetTempPath(sizeof(temp_path), temp_path);
CHECK_LE(nchars, sizeof(temp_path));
if (nchars == 0) {
// TODO(danalbert): Log the error message from FormatError().
// Windows unfortunately has two errnos, errno and GetLastError(), so
// I'm not sure what to do about PLOG here. Probably better to just
// ignore it and add a simplified version of FormatError() for use in
// log messages.
DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
if ((nchars >= arraysize(temp_path)) || (nchars == 0)) {
// If string truncation or some other error.
// TODO(danalbert): Log the error message from
// FormatMessage(GetLastError()). Pure Windows APIs only touch
// GetLastError(), C Runtime APIs touch errno, so maybe there should be
// WPLOG or PLOGW (which would read GetLastError() instead of errno),
// 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";
}
return std::string(temp_path) + log_name;
return narrow(temp_path) + log_name;
}
#else
static const char kNullFileName[] = "/dev/null";
@ -189,9 +190,35 @@ int adb_main(int is_daemon, int server_port) {
return 0;
}
#ifdef _WIN32
static bool _argv_is_utf8 = false;
#endif
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_trace_init(argv);
D("Handling commandline()\n");
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

View file

@ -14,21 +14,33 @@
* limitations under the License.
*/
#include "sysdeps.h"
#include <assert.h>
#include <limits.h>
#include <windows.h>
#include <base/macros.h>
#include "adb.h"
void get_my_path(char *exe, size_t maxLen)
{
char *r;
// 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
// work in that format.
void get_my_path(char *exe, size_t maxLen) {
WCHAR wexe[MAX_PATH];
/* XXX: should be GetModuleFileNameA */
if (GetModuleFileName(NULL, exe, maxLen) > 0) {
r = strrchr(exe, '\\');
if (r != NULL)
*r = '\0';
DWORD module_result = GetModuleFileNameW(NULL, wexe, arraysize(wexe));
if ((module_result == arraysize(wexe)) || (module_result == 0)) {
// String truncation or other error.
wexe[0] = '\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 {
exe[0] = '\0';
}

View file

@ -43,20 +43,35 @@
_rc; })
#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
#include <ctype.h>
#include <direct.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <process.h>
#include <sys/stat.h>
#include <utime.h>
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <string>
#include <string> // Prototypes for narrow() and widen() use std::(w)string.
#include "fdevent.h"
@ -109,25 +124,11 @@ static __inline__ void close_on_exec(int fd)
#define S_ISLNK(m) 0 /* no symlinks on Win32 */
static __inline__ 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;
}
extern int adb_unlink(const char* path);
#undef unlink
#define unlink ___xxx_unlink
static __inline__ int adb_mkdir(const std::string& path, int mode) {
return _mkdir(path.c_str());
}
extern int adb_mkdir(const std::string& path, int mode);
#undef 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().
static __inline__ 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);
}
}
extern int unix_open(const char* path, int options, ...);
#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.
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 */
#include "fdevent.h"

View file

@ -27,6 +27,7 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <cutils/sockets.h>
@ -124,13 +125,13 @@ void *load_file(const char *fn, unsigned *_sz)
char *data;
DWORD file_size;
file = CreateFile( fn,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL );
file = CreateFileW( widen(fn).c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL );
if (file == INVALID_HANDLE_VALUE)
return NULL;
@ -406,8 +407,8 @@ int adb_open(const char* path, int options)
return -1;
}
f->fh_handle = CreateFile( path, desiredAccess, shareMode, NULL, OPEN_EXISTING,
0, NULL );
f->fh_handle = CreateFileW( widen(path).c_str(), desiredAccess, shareMode,
NULL, OPEN_EXISTING, 0, NULL );
if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
const DWORD err = GetLastError();
@ -447,9 +448,10 @@ int adb_creat(const char* path, int mode)
return -1;
}
f->fh_handle = CreateFile( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
NULL );
f->fh_handle = CreateFileW( widen(path).c_str(), GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
NULL );
if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
const DWORD err = GetLastError();
@ -3175,3 +3177,615 @@ int unix_read(int fd, void* buf, size_t len) {
#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;
}