adb: win32: remove widen()/narrow() in favor of UTF8ToWide()/WideToUTF8()
Now that we have a more standardized API (also available in Chromium),
switch to it. Another benefit is real error handling instead of just
killing the process on invalid Unicode.
Make UTF8ToWide()/WideToUTF8() set errno to EILSEQ on bad input. This is
the same error code that wcsrtombs(3) uses.
Update the unittest to check for EILSEQ.
Change-Id: Ie92acf74d37adaea116cf610c1bf8cd433741e16
Signed-off-by: Spencer Low <CompareAndSwap@gmail.com>
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 60556d4..81dcb41 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -103,7 +103,13 @@
}
// Convert UTF-16 to UTF-8.
- std::string msg(narrow(msgbuf));
+ std::string msg;
+ if (!android::base::WideToUTF8(msgbuf, &msg)) {
+ return android::base::StringPrintf(
+ "Error (%d) converting from UTF-16 to UTF-8 while retrieving error. (%lu)", errno,
+ error_code);
+ }
+
// Messages returned by the system end with line breaks.
msg = android::base::Trim(msg);
// There are many Windows error messages compared to POSIX, so include the
@@ -144,7 +150,11 @@
char *data;
DWORD file_size;
- file = CreateFileW( widen(fn).c_str(),
+ std::wstring fn_wide;
+ if (!android::base::UTF8ToWide(fn, &fn_wide))
+ return NULL;
+
+ file = CreateFileW( fn_wide.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
@@ -434,7 +444,11 @@
return -1;
}
- f->fh_handle = CreateFileW( widen(path).c_str(), desiredAccess, shareMode,
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+ f->fh_handle = CreateFileW( path_wide.c_str(), desiredAccess, shareMode,
NULL, OPEN_EXISTING, 0, NULL );
if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
@@ -475,7 +489,11 @@
return -1;
}
- f->fh_handle = CreateFileW( widen(path).c_str(), GENERIC_WRITE,
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+ f->fh_handle = CreateFileW( path_wide.c_str(), GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
NULL );
@@ -981,7 +999,7 @@
#if (NTDDI_VERSION >= NTDDI_WINXPSP2) || (_WIN32_WINNT >= _WIN32_WINNT_WS03)
// TODO: When the Android SDK tools increases the Windows system
- // requirements >= WinXP SP2, switch to GetAddrInfoW(widen(host).c_str()).
+ // requirements >= WinXP SP2, switch to android::base::UTF8ToWide() + GetAddrInfoW().
#else
// Otherwise, keep using getaddrinfo(), or do runtime API detection
// with GetProcAddress("GetAddrInfoW").
@@ -3405,11 +3423,11 @@
// 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
+// The code below chooses option 3, the UTF-8 everywhere strategy. It uses
+// android::base::WideToUTF8() 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
+// args that are passed to main() at the beginning of program startup. We also use
+// android::base::UTF8ToWide() 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
@@ -3439,101 +3457,17 @@
// 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);
-}
-
-// Convert size number of UTF-8 char's to UTF-16. Fatal exit on error.
-std::wstring widen(const char* utf8, const size_t size) {
- std::wstring utf16;
- if (!android::base::UTF8ToWide(utf8, size, &utf16)) {
- // If we call fatal() here and fatal() calls widen(), then there may be
- // infinite recursion. To avoid this, call _widen_fatal() instead.
- _widen_fatal("cannot convert from UTF-8 to UTF-16");
- }
-
- return utf16;
-}
-
-// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Fatal exit
-// on error.
-std::wstring widen(const char* utf8) {
- std::wstring utf16;
- if (!android::base::UTF8ToWide(utf8, &utf16)) {
- // If we call fatal() here and fatal() calls widen(), then there may be
- // infinite recursion. To avoid this, call _widen_fatal() instead.
- _widen_fatal("cannot convert from UTF-8 to UTF-16");
- }
-
- return utf16;
-}
-
-// Convert a UTF-8 std::string (including any embedded NULL characters) to
-// UTF-16. Fatal exit on error.
-std::wstring widen(const std::string& utf8) {
- std::wstring utf16;
- if (!android::base::UTF8ToWide(utf8, &utf16)) {
- // If we call fatal() here and fatal() calls widen(), then there may be
- // infinite recursion. To avoid this, call _widen_fatal() instead.
- _widen_fatal("cannot convert from UTF-8 to UTF-16");
- }
-
- return utf16;
-}
-
-// Convert a UTF-16 std::wstring (including any embedded NULL characters) to
-// UTF-8. Fatal exit on error.
-std::string narrow(const std::wstring& utf16) {
- std::string utf8;
- if (!android::base::WideToUTF8(utf16, &utf8)) {
- fatal("cannot convert from UTF-16 to UTF-8");
- }
-
- return utf8;
-}
-
-// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Fatal exit
-// on error.
-std::string narrow(const wchar_t* utf16) {
- std::string utf8;
- if (!android::base::WideToUTF8(utf16, &utf8)) {
- fatal("cannot convert from UTF-16 to UTF-8");
- }
-
- 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());
+ std::string arg_narrow;
+ if (!android::base::WideToUTF8(argv[i], &arg_narrow)) {
+ fatal_errno("cannot convert argument from UTF-16 to UTF-8");
+ }
+ narrow_args[i] = strdup(arg_narrow.c_str());
}
narrow_args[argc] = nullptr; // terminate
}
@@ -3549,20 +3483,24 @@
}
int unix_open(const char* path, int options, ...) {
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
if ((options & O_CREAT) == 0) {
- return _wopen(widen(path).c_str(), options);
+ return _wopen(path_wide.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);
+ return _wopen(path_wide.c_str(), options, mode);
}
}
// Version of stat() that takes a UTF-8 path.
-int adb_stat(const char* f, struct adb_stat* s) {
+int adb_stat(const char* path, 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)
@@ -3575,17 +3513,27 @@
// <sys/stat.h> has a function prototype for wstat() that should be available.
#endif
- return wstat(widen(f).c_str(), s);
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+
+ return wstat(path_wide.c_str(), s);
#pragma pop_macro("wstat")
}
// Version of opendir() that takes a UTF-8 path.
-DIR* adb_opendir(const char* name) {
+DIR* adb_opendir(const char* path) {
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return nullptr;
+ }
+
// 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()));
+ return reinterpret_cast<DIR*>(_wopendir(path_wide.c_str()));
}
// Version of readdir() that returns UTF-8 paths.
@@ -3595,8 +3543,12 @@
if (went == nullptr) {
return nullptr;
}
+
// Convert from UTF-16 to UTF-8.
- const std::string name_utf8(narrow(went->d_name));
+ std::string name_utf8;
+ if (!android::base::WideToUTF8(went->d_name, &name_utf8)) {
+ return nullptr;
+ }
// 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.
@@ -3628,7 +3580,10 @@
// Version of unlink() that takes a UTF-8 path.
int adb_unlink(const char* path) {
- const std::wstring wpath(widen(path));
+ std::wstring wpath;
+ if (!android::base::UTF8ToWide(path, &wpath)) {
+ return -1;
+ }
int rc = _wunlink(wpath.c_str());
@@ -3644,20 +3599,35 @@
// 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());
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+
+ return _wmkdir(path_wide.c_str());
}
// Version of utime() that takes a UTF-8 path.
int adb_utime(const char* path, struct utimbuf* u) {
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+
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));
+ return _wutime(path_wide.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);
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return -1;
+ }
+
+ return _wchmod(path_wide.c_str(), mode);
}
// Internal helper function to write UTF-8 bytes to a console. Returns -1
@@ -3819,8 +3789,18 @@
// 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());
+FILE* adb_fopen(const char* path, const char* mode) {
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ return nullptr;
+ }
+
+ std::wstring mode_wide;
+ if (!android::base::UTF8ToWide(mode, &mode_wide)) {
+ return nullptr;
+ }
+
+ return _wfopen(path_wide.c_str(), mode_wide.c_str());
}
// Return a lowercase version of the argument. Uses C Runtime tolower() on
@@ -3880,15 +3860,27 @@
continue;
}
+ // If we encounter an error converting UTF-16, don't error-out on account of a single env
+ // var because the program might never even read this particular variable.
+ std::string name_utf8;
+ if (!android::base::WideToUTF8(*env, equal - *env, &name_utf8)) {
+ continue;
+ }
+
// Store lowercase name so that we can do case-insensitive searches.
- const std::string name_utf8(ToLower(narrow(
- std::wstring(*env, equal - *env))));
- char* const value_utf8 = strdup(narrow(equal + 1).c_str());
+ name_utf8 = ToLower(name_utf8);
+
+ std::string value_utf8;
+ if (!android::base::WideToUTF8(equal + 1, &value_utf8)) {
+ continue;
+ }
+
+ char* const value_dup = strdup(value_utf8.c_str());
// Don't overwrite a previus env var with the same name. In reality,
// the system probably won't let two env vars with the same name exist
// in _wenviron.
- g_environ_utf8.insert({name_utf8, value_utf8});
+ g_environ_utf8.insert({name_utf8, value_dup});
}
}
@@ -3914,10 +3906,15 @@
return nullptr;
}
- const std::string buf_utf8(narrow(wbuf));
+ std::string buf_utf8;
+ const bool narrow_result = android::base::WideToUTF8(wbuf, &buf_utf8);
free(wbuf);
wbuf = nullptr;
+ if (!narrow_result) {
+ return nullptr;
+ }
+
// If size was specified, make sure all the chars will fit.
if (size != 0) {
if (size < static_cast<int>(buf_utf8.length() + 1)) {