Merge "adb: fix adb reverse --no-rebind help text"
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 04b82f6..9586f7c 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -157,7 +157,7 @@
     // Don't open log file if no tracing, since this will block
     // the crypto unmount of /data
     if (!get_trace_setting().empty()) {
-        if (isatty(STDOUT_FILENO) == 0) {
+        if (unix_isatty(STDOUT_FILENO) == 0) {
             start_device_log();
         }
     }
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index a48c992..ffde619 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -33,6 +33,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include <base/logging.h>
 #include <base/stringprintf.h>
@@ -101,11 +102,11 @@
         "                                 will disconnect from all connected TCP/IP devices.\n"
         "\n"
         "device commands:\n"
-        "  adb push <local> <remote>\n"
-        "                               - copy file/dir to device\n"
-        "  adb pull [-a] <remote> [<local>]\n"
-        "                               - copy file/dir from device\n"
-        "                                 ('-a' means copy timestamp and mode)\n"
+        "  adb push <local>... <remote>\n"
+        "                               - copy files/dirs to device\n"
+        "  adb pull [-a] <remote>... <local>\n"
+        "                               - copy files/dirs from device\n"
+        "                                 (-a preserves file timestamp and mode)\n"
         "  adb sync [ <directory> ]     - copy host->device only if changed\n"
         "                                 (-l means list but don't copy)\n"
         "  adb shell [-Ttx]             - run remote shell interactively\n"
@@ -1065,31 +1066,35 @@
     return path;
 }
 
-static void parse_push_pull_args(const char **arg, int narg,
-                                 char const **path1, char const **path2,
-                                 int *copy_attrs) {
-    *copy_attrs = 0;
+static void parse_push_pull_args(const char** arg, int narg,
+                                 std::vector<const char*>* srcs,
+                                 const char** dst, bool* copy_attrs) {
+    *copy_attrs = false;
 
+    srcs->clear();
+    bool ignore_flags = false;
     while (narg > 0) {
-        if (!strcmp(*arg, "-p")) {
-            // Silently ignore for backwards compatibility.
-        } else if (!strcmp(*arg, "-a")) {
-            *copy_attrs = 1;
+        if (ignore_flags || *arg[0] != '-') {
+            srcs->push_back(*arg);
         } else {
-            break;
+            if (!strcmp(*arg, "-p")) {
+                // Silently ignore for backwards compatibility.
+            } else if (!strcmp(*arg, "-a")) {
+                *copy_attrs = true;
+            } else if (!strcmp(*arg, "--")) {
+                ignore_flags = true;
+            } else {
+                fprintf(stderr, "adb: unrecognized option '%s'\n", *arg);
+                exit(1);
+            }
         }
         ++arg;
         --narg;
     }
 
-    if (narg > 0) {
-        *path1 = *arg;
-        ++arg;
-        --narg;
-    }
-
-    if (narg > 0) {
-        *path2 = *arg;
+    if (srcs->size() > 1) {
+        *dst = srcs->back();
+        srcs->pop_back();
     }
 }
 
@@ -1388,12 +1393,12 @@
                 // things like `adb shell < my_script.sh` work as expected.
                 // Otherwise leave |shell_type_arg| blank which uses PTY for
                 // interactive shells and raw for non-interactive.
-                if (!isatty(STDIN_FILENO)) {
+                if (!unix_isatty(STDIN_FILENO)) {
                     shell_type_arg = kShellServiceArgRaw;
                 }
             } else if (t_arg_count == 1) {
                 // A single -t arg isn't enough to override implicit -T.
-                if (!isatty(STDIN_FILENO)) {
+                if (!unix_isatty(STDIN_FILENO)) {
                     fprintf(stderr,
                             "Remote PTY will not be allocated because stdin is not a terminal.\n"
                             "Use multiple -t options to force remote PTY allocation.\n");
@@ -1561,20 +1566,22 @@
         return do_sync_ls(argv[1]) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "push")) {
-        int copy_attrs = 0;
-        const char* lpath = NULL, *rpath = NULL;
+        bool copy_attrs = false;
+        std::vector<const char*> srcs;
+        const char* dst = nullptr;
 
-        parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &copy_attrs);
-        if (!lpath || !rpath || copy_attrs != 0) return usage();
-        return do_sync_push(lpath, rpath) ? 0 : 1;
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
+        if (srcs.empty() || !dst) return usage();
+        return do_sync_push(srcs, dst) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "pull")) {
-        int copy_attrs = 0;
-        const char* rpath = NULL, *lpath = ".";
+        bool copy_attrs = false;
+        std::vector<const char*> srcs;
+        const char* dst = ".";
 
-        parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &copy_attrs);
-        if (!rpath) return usage();
-        return do_sync_pull(rpath, lpath, copy_attrs) ? 0 : 1;
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
+        if (srcs.empty()) return usage();
+        return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "install")) {
         if (argc < 2) return usage();
@@ -1764,8 +1771,9 @@
     }
 
     int result = -1;
-    const char* apk_file = argv[last_apk];
-    std::string apk_dest = android::base::StringPrintf(where, adb_basename(apk_file).c_str());
+    std::vector<const char*> apk_file = {argv[last_apk]};
+    std::string apk_dest = android::base::StringPrintf(
+        where, adb_basename(argv[last_apk]).c_str());
     if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
     argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
     result = pm_command(transport, serial, argc, argv);
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 8c9f7a6..7a60580 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -187,6 +187,16 @@
         line_printer_.Print(s, LinePrinter::ELIDE);
     }
 
+    void Printf(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+        std::string s;
+        va_list ap;
+        va_start(ap, fmt);
+        android::base::StringAppendV(&s, fmt, ap);
+        va_end(ap);
+
+        Print(s);
+    }
+
     void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
         std::string s = "adb: error: ";
 
@@ -308,7 +318,7 @@
         bytes_copied += ret;
 
         int percentage = static_cast<int>(bytes_copied * 100 / total_size);
-        sc.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage));
+        sc.Printf("%s: %d%%", rpath, percentage);
     }
 
     adb_close(lfd);
@@ -431,7 +441,7 @@
         bytes_copied += msg.data.size;
 
         int percentage = static_cast<int>(bytes_copied * 100 / size);
-        sc.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage));
+        sc.Printf("%s: %d%%", rpath, percentage);
     }
 
     adb_close(lfd);
@@ -609,40 +619,61 @@
         free(ci);
     }
 
-    sc.Print(android::base::StringPrintf("%s: %d file%s pushed. %d file%s skipped.%s\n",
-                                         rpath,
-                                         pushed, (pushed == 1) ? "" : "s",
-                                         skipped, (skipped == 1) ? "" : "s",
-                                         sc.TransferRate().c_str()));
+    sc.Printf("%s: %d file%s pushed. %d file%s skipped.%s\n", rpath, pushed,
+              (pushed == 1) ? "" : "s", skipped, (skipped == 1) ? "" : "s",
+              sc.TransferRate().c_str());
     return true;
 }
 
-bool do_sync_push(const char* lpath, const char* rpath) {
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
-    struct stat st;
-    if (stat(lpath, &st)) {
-        sc.Error("cannot stat '%s': %s", lpath, strerror(errno));
-        return false;
-    }
-
-    if (S_ISDIR(st.st_mode)) {
-        return copy_local_dir_remote(sc, lpath, rpath, false, false);
-    }
-
+    bool success = true;
     unsigned mode;
-    if (!sync_stat(sc, rpath, nullptr, &mode, nullptr)) return false;
-    std::string path_holder;
-    if (mode != 0 && S_ISDIR(mode)) {
-        // If we're copying a local file to a remote directory,
-        // we really want to copy to remote_dir + "/" + local_filename.
-        path_holder = android::base::StringPrintf("%s/%s", rpath, adb_basename(lpath).c_str());
-        rpath = path_holder.c_str();
+    if (!sync_stat(sc, dst, nullptr, &mode, nullptr)) return false;
+    bool dst_isdir = mode != 0 && S_ISDIR(mode);
+
+    if (!dst_isdir) {
+        if (srcs.size() > 1) {
+            sc.Error("target '%s' is not a directory", dst);
+            return false;
+        } else {
+            size_t dst_len = strlen(dst);
+            if (dst[dst_len - 1] == '/') {
+                sc.Error("failed to access '%s': Not a directory", dst);
+                return false;
+            }
+        }
     }
-    bool result = sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode);
+
+    for (const char* src_path : srcs) {
+        const char* dst_path = dst;
+        struct stat st;
+        if (stat(src_path, &st)) {
+            sc.Error("cannot stat '%s': %s", src_path, strerror(errno));
+            success = false;
+            continue;
+        }
+
+        if (S_ISDIR(st.st_mode)) {
+            success &= copy_local_dir_remote(sc, src_path, dst, false, false);
+            continue;
+        }
+
+        std::string path_holder;
+        if (mode != 0 && S_ISDIR(mode)) {
+            // If we're copying a local file to a remote directory,
+            // we really want to copy to remote_dir + "/" + local_filename.
+            path_holder = android::base::StringPrintf(
+                "%s/%s", dst_path, adb_basename(src_path).c_str());
+            dst_path = path_holder.c_str();
+        }
+        success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
+    }
+
     sc.Print("\n");
-    return result;
+    return success;
 }
 
 struct sync_ls_build_list_cb_args {
@@ -678,7 +709,7 @@
         ci->next = *filelist;
         *filelist = ci;
     } else {
-        args->sc->Print(android::base::StringPrintf("skipping special file '%s'\n", name));
+        args->sc->Printf("skipping special file '%s'\n", name);
     }
 }
 
@@ -725,7 +756,7 @@
 }
 
 static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const char* lpath,
-                                  int copy_attrs) {
+                                  bool copy_attrs) {
     // Make sure that both directory paths end in a slash.
     std::string rpath_clean(rpath);
     std::string lpath_clean(lpath);
@@ -744,7 +775,7 @@
     while (ci) {
         copyinfo* next = ci->next;
         if (ci->flag == 0) {
-            sc.Print(android::base::StringPrintf("pull: %s -> %s", ci->src, ci->dst));
+            sc.Printf("pull: %s -> %s", ci->src, ci->dst);
             if (!sync_recv(sc, ci->src, ci->dst)) {
                 return false;
             }
@@ -760,51 +791,86 @@
         ci = next;
     }
 
-    sc.Print(android::base::StringPrintf("%s: %d file%s pulled. %d file%s skipped.%s\n",
-                                         rpath,
-                                         pulled, (pulled == 1) ? "" : "s",
-                                         skipped, (skipped == 1) ? "" : "s",
-                                         sc.TransferRate().c_str()));
+    sc.Printf("%s: %d file%s pulled. %d file%s skipped.%s\n", rpath, pulled,
+              (pulled == 1) ? "" : "s", skipped, (skipped == 1) ? "" : "s",
+              sc.TransferRate().c_str());
     return true;
 }
 
-bool do_sync_pull(const char* rpath, const char* lpath, int copy_attrs) {
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
+                  bool copy_attrs) {
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
+    bool success = true;
     unsigned mode, time;
-    if (!sync_stat(sc, rpath, &time, &mode, nullptr)) return false;
-    if (mode == 0) {
-        sc.Error("remote object '%s' does not exist", rpath);
-        return false;
+    struct stat st;
+    if (stat(dst, &st)) {
+        // If we're only pulling one file, the destination path might point to
+        // a path that doesn't exist yet.
+        if (srcs.size() != 1 || errno != ENOENT) {
+            sc.Error("cannot stat '%s': %s", dst, strerror(errno));
+            return false;
+        }
     }
 
-    if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
-        std::string path_holder;
-        struct stat st;
-        if (stat(lpath, &st) == 0) {
-            if (S_ISDIR(st.st_mode)) {
-                // If we're copying a remote file to a local directory,
-                // we really want to copy to local_dir + "/" + basename(remote).
-                path_holder = android::base::StringPrintf("%s/%s", lpath, adb_basename(rpath).c_str());
-                lpath = path_holder.c_str();
-            }
-        }
-        if (!sync_recv(sc, rpath, lpath)) {
+    bool dst_isdir = S_ISDIR(st.st_mode);
+    if (!dst_isdir) {
+        if (srcs.size() > 1) {
+            sc.Error("target '%s' is not a directory", dst);
             return false;
         } else {
-            if (copy_attrs && set_time_and_mode(lpath, time, mode)) {
+            size_t dst_len = strlen(dst);
+            if (dst[dst_len - 1] == '/') {
+                sc.Error("failed to access '%s': Not a directory", dst);
                 return false;
             }
         }
-        sc.Print("\n");
-        return true;
-    } else if (S_ISDIR(mode)) {
-        return copy_remote_dir_local(sc, rpath, lpath, copy_attrs);
     }
 
-    sc.Error("remote object '%s' not a file or directory", rpath);
-    return false;
+    for (const char* src_path : srcs) {
+        const char* dst_path = dst;
+        if (!sync_stat(sc, src_path, &time, &mode, nullptr)) return false;
+        if (mode == 0) {
+            sc.Error("remote object '%s' does not exist", src_path);
+            success = false;
+            continue;
+        }
+
+        if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
+            std::string path_holder;
+            struct stat st;
+            if (stat(dst_path, &st) == 0) {
+                if (S_ISDIR(st.st_mode)) {
+                    // If we're copying a remote file to a local directory,
+                    // we really want to copy to local_dir + "/" +
+                    // basename(remote).
+                    path_holder = android::base::StringPrintf(
+                        "%s/%s", dst_path, adb_basename(src_path).c_str());
+                    dst_path = path_holder.c_str();
+                }
+            }
+            if (!sync_recv(sc, src_path, dst_path)) {
+                success = false;
+                continue;
+            } else {
+                if (copy_attrs && set_time_and_mode(dst_path, time, mode)) {
+                    success = false;
+                    continue;
+                }
+            }
+        } else if (S_ISDIR(mode)) {
+            success &= copy_remote_dir_local(sc, src_path, dst_path, copy_attrs);
+            continue;
+        } else {
+            sc.Error("remote object '%s' not a file or directory", src_path);
+            success = false;
+            continue;
+        }
+    }
+
+    sc.Print("\n");
+    return success;
 }
 
 bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 7c4d554..38382c1 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -18,6 +18,7 @@
 #define _FILE_SYNC_SERVICE_H_
 
 #include <string>
+#include <vector>
 
 #define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
 
@@ -64,9 +65,11 @@
 
 void file_sync_service(int fd, void* cookie);
 bool do_sync_ls(const char* path);
-bool do_sync_push(const char* lpath, const char* rpath);
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst);
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
+                  bool copy_attrs);
+
 bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
-bool do_sync_pull(const char* rpath, const char* lpath, int copy_attrs);
 
 #define SYNC_DATA_MAX (64*1024)
 
diff --git a/adb/line_printer.cpp b/adb/line_printer.cpp
index 81b3f0a..aa332f7 100644
--- a/adb/line_printer.cpp
+++ b/adb/line_printer.cpp
@@ -46,7 +46,7 @@
 LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) {
 #ifndef _WIN32
   const char* term = getenv("TERM");
-  smart_terminal_ = isatty(1) && term && string(term) != "dumb";
+  smart_terminal_ = unix_isatty(1) && term && string(term) != "dumb";
 #else
   // Disable output buffer.  It'd be nice to use line buffering but
   // MSDN says: "For some systems, [_IOLBF] provides line
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 51d09a6..1735627 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -181,6 +181,17 @@
 extern int unix_open(const char* path, int options, ...);
 #define  open    ___xxx_unix_open
 
+// Checks if |fd| corresponds to a console.
+// Standard Windows isatty() returns 1 for both console FDs and character
+// devices like NUL. unix_isatty() performs some extra checking to only match
+// console FDs.
+// |fd| must be a real file descriptor, meaning STDxx_FILENO or unix_open() FDs
+// will work but adb_open() FDs will not. Additionally the OS handle associated
+// with |fd| must have GENERIC_READ access (which console FDs have by default).
+// Returns 1 if |fd| is a console FD, 0 otherwise. The value of errno after
+// calling this function is unreliable and should not be used.
+int unix_isatty(int fd);
+#define  isatty  ___xxx_isatty
 
 /* normally provided by <cutils/misc.h> */
 extern void*  load_file(const char*  pathname, unsigned*  psize);
@@ -551,6 +562,11 @@
 #undef   creat
 #define  creat  ___xxx_creat
 
+static __inline__ int unix_isatty(int fd) {
+    return isatty(fd);
+}
+#define  isatty  ___xxx_isatty
+
 // Helper for network_* functions.
 inline int _fd_set_error_str(int fd, std::string* error) {
   if (fd == -1) {
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 14d1375..d2e6cdb 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -2499,10 +2499,52 @@
 //
 // Code organization:
 //
+// * _get_console_handle() and unix_isatty() provide console information.
 // * stdin_raw_init() and stdin_raw_restore() reconfigure the console.
 // * unix_read() detects console windows (as opposed to pipes, files, etc.).
 // * _console_read() is the main code of the emulation.
 
+// Returns a console HANDLE if |fd| is a console, otherwise returns nullptr.
+// If a valid HANDLE is returned and |mode| is not null, |mode| is also filled
+// with the console mode. Requires GENERIC_READ access to the underlying HANDLE.
+static HANDLE _get_console_handle(int fd, DWORD* mode=nullptr) {
+    // First check isatty(); this is very fast and eliminates most non-console
+    // FDs, but returns 1 for both consoles and character devices like NUL.
+#pragma push_macro("isatty")
+#undef isatty
+    if (!isatty(fd)) {
+        return nullptr;
+    }
+#pragma pop_macro("isatty")
+
+    // To differentiate between character devices and consoles we need to get
+    // the underlying HANDLE and use GetConsoleMode(), which is what requires
+    // GENERIC_READ permissions.
+    const intptr_t intptr_handle = _get_osfhandle(fd);
+    if (intptr_handle == -1) {
+        return nullptr;
+    }
+    const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
+    DWORD temp_mode = 0;
+    if (!GetConsoleMode(handle, mode ? mode : &temp_mode)) {
+        return nullptr;
+    }
+
+    return handle;
+}
+
+// Returns a console handle if |stream| is a console, otherwise returns nullptr.
+static HANDLE _get_console_handle(FILE* const stream) {
+    const int fd = fileno(stream);
+    if (fd < 0) {
+        return nullptr;
+    }
+    return _get_console_handle(fd);
+}
+
+int unix_isatty(int fd) {
+    return _get_console_handle(fd) ? 1 : 0;
+}
 
 // Read an input record from the console; one that should be processed.
 static bool _get_interesting_input_record_uncached(const HANDLE console,
@@ -3302,20 +3344,7 @@
 
 void stdin_raw_init(const int fd) {
     if (STDIN_FILENO == fd) {
-        const HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
-        if ((in == INVALID_HANDLE_VALUE) || (in == NULL)) {
-            return;
-        }
-
-        if (GetFileType(in) != FILE_TYPE_CHAR) {
-            // stdin might be a file or pipe.
-            return;
-        }
-
-        if (!GetConsoleMode(in, &_old_console_mode)) {
-            // If GetConsoleMode() fails, stdin is probably is not a console.
-            return;
-        }
+        const HANDLE in = _get_console_handle(fd, &_old_console_mode);
 
         // Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of
         // calling the process Ctrl-C routine (configured by
@@ -3366,11 +3395,8 @@
     } else {
         // On older versions of Windows (definitely 7, definitely not 10),
         // ReadConsole() with a size >= 31367 fails, so if |fd| is a console
-        // we need to limit the read size. This may also catch devices like NUL,
-        // but that is OK as we just want to avoid capping pipes and files which
-        // don't need size limiting. This isatty() test is very simple and quick
-        // and doesn't call the OS.
-        if (isatty(fd) && len > 4096) {
+        // we need to limit the read size.
+        if (len > 4096 && unix_isatty(fd)) {
             len = 4096;
         }
         // Just call into C Runtime which can read from pipes/files and which
@@ -3725,40 +3751,6 @@
     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,
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index 66d1ba8..55b5eb4 100755
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -18,6 +18,8 @@
 
 #include "sysdeps.h"
 
+#include "base/test_utils.h"
+
 TEST(sysdeps_win32, adb_getenv) {
     // Insert all test env vars before first call to adb_getenv() which will
     // read the env var block only once.
@@ -93,3 +95,45 @@
     // adb_strerror() returns.
     TestAdbStrError(ECONNRESET, "Connection reset by peer");
 }
+
+TEST(sysdeps_win32, unix_isatty) {
+    // stdin and stdout should be consoles. Use CONIN$ and CONOUT$ special files
+    // so that we can test this even if stdin/stdout have been redirected. Read
+    // permissions are required for unix_isatty().
+    int conin_fd = unix_open("CONIN$", O_RDONLY);
+    int conout_fd = unix_open("CONOUT$", O_RDWR);
+    for (const int fd : {conin_fd, conout_fd}) {
+        EXPECT_TRUE(fd >= 0);
+        EXPECT_EQ(1, unix_isatty(fd));
+        EXPECT_EQ(0, unix_close(fd));
+    }
+
+    // nul returns 1 from isatty(), make sure unix_isatty() corrects that.
+    for (auto flags : {O_RDONLY, O_RDWR}) {
+        int nul_fd = unix_open("nul", flags);
+        EXPECT_TRUE(nul_fd >= 0);
+        EXPECT_EQ(0, unix_isatty(nul_fd));
+        EXPECT_EQ(0, unix_close(nul_fd));
+    }
+
+    // Check a real file, both read-write and read-only.
+    TemporaryFile temp_file;
+    EXPECT_TRUE(temp_file.fd >= 0);
+    EXPECT_EQ(0, unix_isatty(temp_file.fd));
+
+    int temp_file_ro_fd = unix_open(temp_file.path, O_RDONLY);
+    EXPECT_TRUE(temp_file_ro_fd >= 0);
+    EXPECT_EQ(0, unix_isatty(temp_file_ro_fd));
+    EXPECT_EQ(0, unix_close(temp_file_ro_fd));
+
+    // Check a real OS pipe.
+    int pipe_fds[2];
+    EXPECT_EQ(0, _pipe(pipe_fds, 64, _O_BINARY));
+    EXPECT_EQ(0, unix_isatty(pipe_fds[0]));
+    EXPECT_EQ(0, unix_isatty(pipe_fds[1]));
+    EXPECT_EQ(0, _close(pipe_fds[0]));
+    EXPECT_EQ(0, _close(pipe_fds[1]));
+
+    // Make sure an invalid FD is handled correctly.
+    EXPECT_EQ(0, unix_isatty(-1));
+}
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 63e8971..44796d7 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -88,24 +88,6 @@
     return true;
 }
 
-
-// Return true if this partition is supported by the fastboot format command.
-// It is also used to determine if we should first erase a partition before
-// flashing it with an ext4 filesystem.  See needs_erase()
-//
-// Not all devices report the filesystem type, so don't report any errors,
-// just return false.
-bool fb_format_supported(usb_handle *usb, const char *partition, const char *type_override) {
-    if (type_override) {
-        return fs_get_generator(type_override) != nullptr;
-    }
-    std::string partition_type;
-    if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
-        return false;
-    }
-    return fs_get_generator(partition_type.c_str()) != nullptr;
-}
-
 static int cb_default(Action* a, int status, const char* resp) {
     if (status) {
         fprintf(stderr,"FAILED (%s)\n", resp);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 6b845a0..226f3ef 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -44,6 +44,7 @@
 #include <unistd.h>
 
 #include <base/parseint.h>
+#include <base/strings.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
 
@@ -575,6 +576,9 @@
         return 0;
     }
 
+    // Some bootloaders (angler, for example) send spurious whitespace too.
+    max_download_size = android::base::Trim(max_download_size);
+
     uint64_t limit;
     if (!android::base::ParseUint(max_download_size.c_str(), &limit)) {
         fprintf(stderr, "couldn't parse max-download-size '%s'\n", max_download_size.c_str());
@@ -614,8 +618,12 @@
 // Until we get lazy inode table init working in make_ext4fs, we need to
 // erase partitions of type ext4 before flashing a filesystem so no stale
 // inodes are left lying around.  Otherwise, e2fsck gets very upset.
-static bool needs_erase(usb_handle* usb, const char* part) {
-    return !fb_format_supported(usb, part, nullptr);
+static bool needs_erase(usb_handle* usb, const char* partition) {
+    std::string partition_type;
+    if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
+        return false;
+    }
+    return partition_type == "ext4";
 }
 
 static int load_buf_fd(usb_handle* usb, int fd, struct fastboot_buffer* buf) {
@@ -889,7 +897,7 @@
         partition_size = size_override;
     }
 
-    gen = fs_get_generator(partition_type.c_str());
+    gen = fs_get_generator(partition_type);
     if (!gen) {
         if (skip_if_not_supported) {
             fprintf(stderr, "Erase successful, but not automatically formatting.\n");
@@ -1059,8 +1067,11 @@
         } else if(!strcmp(*argv, "erase")) {
             require(2);
 
-            if (!fb_format_supported(usb, argv[1], nullptr)) {
-                fprintf(stderr, "******** Did you mean to fastboot format this partition?\n");
+            std::string partition_type;
+            if (fb_getvar(usb, std::string("partition-type:") + argv[1], &partition_type) &&
+                fs_get_generator(partition_type) != nullptr) {
+                fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
+                        partition_type.c_str());
             }
 
             fb_queue_erase(argv[1]);
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index a66c211..9e33531 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -50,7 +50,6 @@
 
 /* engine.c - high level command queue engine */
 bool fb_getvar(usb_handle* usb, const std::string& key, std::string* value);
-bool fb_format_supported(usb_handle* usb, const char* partition, const char* type_override);
 void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
 void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, uint32_t sz);
 void fb_queue_erase(const char *ptn);
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index c58a505..90d8474 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -39,9 +39,9 @@
 #endif
 };
 
-const struct fs_generator* fs_get_generator(const char* fs_type) {
+const struct fs_generator* fs_get_generator(const std::string& fs_type) {
     for (size_t i = 0; i < sizeof(generators) / sizeof(*generators); i++) {
-        if (strcmp(generators[i].fs_type, fs_type) == 0) {
+        if (fs_type == generators[i].fs_type) {
             return generators + i;
         }
     }
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 8444081..289488b 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -5,8 +5,7 @@
 
 struct fs_generator;
 
-const struct fs_generator* fs_get_generator(const char *fs_type);
+const struct fs_generator* fs_get_generator(const std::string& fs_type);
 int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize);
 
 #endif
-