Merge "Show transfer progress in adb sync/pull/push."
This commit is contained in:
commit
26352bf1fb
6 changed files with 387 additions and 142 deletions
|
|
@ -213,12 +213,13 @@ LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
|
||||||
LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
|
LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
|
adb_client.cpp \
|
||||||
client/main.cpp \
|
client/main.cpp \
|
||||||
console.cpp \
|
console.cpp \
|
||||||
commandline.cpp \
|
commandline.cpp \
|
||||||
adb_client.cpp \
|
|
||||||
services.cpp \
|
|
||||||
file_sync_client.cpp \
|
file_sync_client.cpp \
|
||||||
|
line_printer.cpp \
|
||||||
|
services.cpp \
|
||||||
shell_service_protocol.cpp \
|
shell_service_protocol.cpp \
|
||||||
|
|
||||||
LOCAL_CFLAGS += \
|
LOCAL_CFLAGS += \
|
||||||
|
|
|
||||||
|
|
@ -101,12 +101,10 @@ static void help() {
|
||||||
" will disconnect from all connected TCP/IP devices.\n"
|
" will disconnect from all connected TCP/IP devices.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"device commands:\n"
|
"device commands:\n"
|
||||||
" adb push [-p] <local> <remote>\n"
|
" adb push <local> <remote>\n"
|
||||||
" - copy file/dir to device\n"
|
" - copy file/dir to device\n"
|
||||||
" ('-p' to display the transfer progress)\n"
|
" adb pull [-a] <remote> [<local>]\n"
|
||||||
" adb pull [-p] [-a] <remote> [<local>]\n"
|
|
||||||
" - copy file/dir from device\n"
|
" - copy file/dir from device\n"
|
||||||
" ('-p' to display the transfer progress)\n"
|
|
||||||
" ('-a' means copy timestamp and mode)\n"
|
" ('-a' means copy timestamp and mode)\n"
|
||||||
" adb sync [ <directory> ] - copy host->device only if changed\n"
|
" adb sync [ <directory> ] - copy host->device only if changed\n"
|
||||||
" (-l means list but don't copy)\n"
|
" (-l means list but don't copy)\n"
|
||||||
|
|
@ -1065,15 +1063,14 @@ static std::string find_product_out_path(const std::string& hint) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_push_pull_args(const char **arg, int narg, char const **path1,
|
static void parse_push_pull_args(const char **arg, int narg,
|
||||||
char const **path2, bool* show_progress,
|
char const **path1, char const **path2,
|
||||||
int *copy_attrs) {
|
int *copy_attrs) {
|
||||||
*show_progress = false;
|
|
||||||
*copy_attrs = 0;
|
*copy_attrs = 0;
|
||||||
|
|
||||||
while (narg > 0) {
|
while (narg > 0) {
|
||||||
if (!strcmp(*arg, "-p")) {
|
if (!strcmp(*arg, "-p")) {
|
||||||
*show_progress = true;
|
// Silently ignore for backwards compatibility.
|
||||||
} else if (!strcmp(*arg, "-a")) {
|
} else if (!strcmp(*arg, "-a")) {
|
||||||
*copy_attrs = 1;
|
*copy_attrs = 1;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1561,22 +1558,20 @@ int adb_commandline(int argc, const char **argv) {
|
||||||
return do_sync_ls(argv[1]) ? 0 : 1;
|
return do_sync_ls(argv[1]) ? 0 : 1;
|
||||||
}
|
}
|
||||||
else if (!strcmp(argv[0], "push")) {
|
else if (!strcmp(argv[0], "push")) {
|
||||||
bool show_progress = false;
|
|
||||||
int copy_attrs = 0;
|
int copy_attrs = 0;
|
||||||
const char* lpath = NULL, *rpath = NULL;
|
const char* lpath = NULL, *rpath = NULL;
|
||||||
|
|
||||||
parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress, ©_attrs);
|
parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, ©_attrs);
|
||||||
if (!lpath || !rpath || copy_attrs != 0) return usage();
|
if (!lpath || !rpath || copy_attrs != 0) return usage();
|
||||||
return do_sync_push(lpath, rpath, show_progress) ? 0 : 1;
|
return do_sync_push(lpath, rpath) ? 0 : 1;
|
||||||
}
|
}
|
||||||
else if (!strcmp(argv[0], "pull")) {
|
else if (!strcmp(argv[0], "pull")) {
|
||||||
bool show_progress = false;
|
|
||||||
int copy_attrs = 0;
|
int copy_attrs = 0;
|
||||||
const char* rpath = NULL, *lpath = ".";
|
const char* rpath = NULL, *lpath = ".";
|
||||||
|
|
||||||
parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress, ©_attrs);
|
parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, ©_attrs);
|
||||||
if (!rpath) return usage();
|
if (!rpath) return usage();
|
||||||
return do_sync_pull(rpath, lpath, show_progress, copy_attrs) ? 0 : 1;
|
return do_sync_pull(rpath, lpath, copy_attrs) ? 0 : 1;
|
||||||
}
|
}
|
||||||
else if (!strcmp(argv[0], "install")) {
|
else if (!strcmp(argv[0], "install")) {
|
||||||
if (argc < 2) return usage();
|
if (argc < 2) return usage();
|
||||||
|
|
@ -1768,7 +1763,7 @@ static int install_app(TransportType transport, const char* serial, int argc, co
|
||||||
int result = -1;
|
int result = -1;
|
||||||
const char* apk_file = argv[last_apk];
|
const char* apk_file = argv[last_apk];
|
||||||
std::string apk_dest = android::base::StringPrintf(where, adb_basename(apk_file).c_str());
|
std::string apk_dest = android::base::StringPrintf(where, adb_basename(apk_file).c_str());
|
||||||
if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
|
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 */
|
argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
|
||||||
result = pm_command(transport, serial, argc, argv);
|
result = pm_command(transport, serial, argc, argv);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
#include "adb_io.h"
|
#include "adb_io.h"
|
||||||
#include "adb_utils.h"
|
#include "adb_utils.h"
|
||||||
#include "file_sync_service.h"
|
#include "file_sync_service.h"
|
||||||
|
#include "line_printer.h"
|
||||||
|
|
||||||
#include <base/file.h>
|
#include <base/file.h>
|
||||||
#include <base/strings.h>
|
#include <base/strings.h>
|
||||||
|
|
@ -49,36 +50,15 @@ struct syncsendbuf {
|
||||||
char data[SYNC_DATA_MAX];
|
char data[SYNC_DATA_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
static long long NOW() {
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv, 0);
|
|
||||||
return ((long long) tv.tv_usec) + 1000000LL * ((long long) tv.tv_sec);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void print_transfer_progress(uint64_t bytes_current,
|
|
||||||
uint64_t bytes_total) {
|
|
||||||
if (bytes_total == 0) return;
|
|
||||||
|
|
||||||
fprintf(stderr, "\rTransferring: %" PRIu64 "/%" PRIu64 " (%d%%)",
|
|
||||||
bytes_current, bytes_total,
|
|
||||||
(int) (bytes_current * 100 / bytes_total));
|
|
||||||
|
|
||||||
if (bytes_current == bytes_total) {
|
|
||||||
fputc('\n', stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
fflush(stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
class SyncConnection {
|
class SyncConnection {
|
||||||
public:
|
public:
|
||||||
SyncConnection() : total_bytes(0), start_time_(NOW()) {
|
SyncConnection() : total_bytes(0), start_time_ms_(CurrentTimeMs()) {
|
||||||
max = SYNC_DATA_MAX; // TODO: decide at runtime.
|
max = SYNC_DATA_MAX; // TODO: decide at runtime.
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
fd = adb_connect("sync:", &error);
|
fd = adb_connect("sync:", &error);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
fprintf(stderr, "adb: error: %s\n", error.c_str());
|
Error("connect failed: %s", error.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +66,6 @@ class SyncConnection {
|
||||||
if (!IsValid()) return;
|
if (!IsValid()) return;
|
||||||
|
|
||||||
SendQuit();
|
SendQuit();
|
||||||
ShowTransferRate();
|
|
||||||
adb_close(fd);
|
adb_close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,7 +74,7 @@ class SyncConnection {
|
||||||
bool SendRequest(int id, const char* path_and_mode) {
|
bool SendRequest(int id, const char* path_and_mode) {
|
||||||
size_t path_length = strlen(path_and_mode);
|
size_t path_length = strlen(path_and_mode);
|
||||||
if (path_length > 1024) {
|
if (path_length > 1024) {
|
||||||
fprintf(stderr, "adb: SendRequest failed: path too long: %zu\n", path_length);
|
Error("SendRequest failed: path too long: %zu", path_length);
|
||||||
errno = ENAMETOOLONG;
|
errno = ENAMETOOLONG;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -115,11 +94,14 @@ class SyncConnection {
|
||||||
// Sending header, payload, and footer in a single write makes a huge
|
// Sending header, payload, and footer in a single write makes a huge
|
||||||
// difference to "adb sync" performance.
|
// difference to "adb sync" performance.
|
||||||
bool SendSmallFile(const char* path_and_mode,
|
bool SendSmallFile(const char* path_and_mode,
|
||||||
|
const char* rpath,
|
||||||
const char* data, size_t data_length,
|
const char* data, size_t data_length,
|
||||||
unsigned mtime) {
|
unsigned mtime) {
|
||||||
|
Print(rpath);
|
||||||
|
|
||||||
size_t path_length = strlen(path_and_mode);
|
size_t path_length = strlen(path_and_mode);
|
||||||
if (path_length > 1024) {
|
if (path_length > 1024) {
|
||||||
fprintf(stderr, "adb: SendSmallFile failed: path too long: %zu\n", path_length);
|
Error("SendSmallFile failed: path too long: %zu", path_length);
|
||||||
errno = ENAMETOOLONG;
|
errno = ENAMETOOLONG;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -157,16 +139,14 @@ class SyncConnection {
|
||||||
bool CopyDone(const char* from, const char* to) {
|
bool CopyDone(const char* from, const char* to) {
|
||||||
syncmsg msg;
|
syncmsg msg;
|
||||||
if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
|
if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
|
||||||
fprintf(stderr, "adb: failed to copy '%s' to '%s': no ID_DONE: %s\n",
|
Error("failed to copy '%s' to '%s': no ID_DONE: %s", from, to, strerror(errno));
|
||||||
from, to, strerror(errno));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (msg.status.id == ID_OKAY) {
|
if (msg.status.id == ID_OKAY) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (msg.status.id != ID_FAIL) {
|
if (msg.status.id != ID_FAIL) {
|
||||||
fprintf(stderr, "adb: failed to copy '%s' to '%s': unknown reason %d\n",
|
Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
|
||||||
from, to, msg.status.id);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return ReportCopyFailure(from, to, msg);
|
return ReportCopyFailure(from, to, msg);
|
||||||
|
|
@ -175,15 +155,41 @@ class SyncConnection {
|
||||||
bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) {
|
bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) {
|
||||||
std::vector<char> buf(msg.status.msglen + 1);
|
std::vector<char> buf(msg.status.msglen + 1);
|
||||||
if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) {
|
if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) {
|
||||||
fprintf(stderr, "adb: failed to copy '%s' to '%s'; failed to read reason (!): %s\n",
|
Error("failed to copy '%s' to '%s'; failed to read reason (!): %s",
|
||||||
from, to, strerror(errno));
|
from, to, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
buf[msg.status.msglen] = 0;
|
buf[msg.status.msglen] = 0;
|
||||||
fprintf(stderr, "adb: failed to copy '%s' to '%s': %s\n", from, to, &buf[0]);
|
Error("failed to copy '%s' to '%s': %s", from, to, &buf[0]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string TransferRate() {
|
||||||
|
uint64_t ms = CurrentTimeMs() - start_time_ms_;
|
||||||
|
if (total_bytes == 0 || ms == 0) return "";
|
||||||
|
|
||||||
|
double s = static_cast<double>(ms) / 1000LL;
|
||||||
|
double rate = (static_cast<double>(total_bytes) / s) / (1024*1024);
|
||||||
|
return android::base::StringPrintf(" %.1f MB/s (%" PRId64 " bytes in %.3fs)",
|
||||||
|
rate, total_bytes, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Print(const std::string& s) {
|
||||||
|
// TODO: we actually don't want ELIDE; we want "ELIDE if smart, FULL if dumb".
|
||||||
|
line_printer_.Print(s, LinePrinter::ELIDE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
|
||||||
|
std::string s = "adb: error: ";
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
android::base::StringAppendV(&s, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
line_printer_.Print(s, LinePrinter::FULL);
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t total_bytes;
|
uint64_t total_bytes;
|
||||||
|
|
||||||
// TODO: add a char[max] buffer here, to replace syncsendbuf...
|
// TODO: add a char[max] buffer here, to replace syncsendbuf...
|
||||||
|
|
@ -191,19 +197,18 @@ class SyncConnection {
|
||||||
size_t max;
|
size_t max;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t start_time_;
|
uint64_t start_time_ms_;
|
||||||
|
|
||||||
|
LinePrinter line_printer_;
|
||||||
|
|
||||||
void SendQuit() {
|
void SendQuit() {
|
||||||
SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
|
SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowTransferRate() {
|
static uint64_t CurrentTimeMs() {
|
||||||
uint64_t t = NOW() - start_time_;
|
struct timeval tv;
|
||||||
if (total_bytes == 0 || t == 0) return;
|
gettimeofday(&tv, 0); // (Not clock_gettime because of Mac/Windows.)
|
||||||
|
return static_cast<uint64_t>(tv.tv_sec) * 1000 + tv.tv_usec / 1000;
|
||||||
fprintf(stderr, "%lld KB/s (%" PRId64 " bytes in %lld.%03llds)\n",
|
|
||||||
((total_bytes * 1000000LL) / t) / 1024LL,
|
|
||||||
total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -249,29 +254,26 @@ static bool sync_stat(SyncConnection& sc, const char* path,
|
||||||
return sc.SendRequest(ID_STAT, path) && sync_finish_stat(sc, timestamp, mode, size);
|
return sc.SendRequest(ID_STAT, path) && sync_finish_stat(sc, timestamp, mode, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const char* path,
|
static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode,
|
||||||
unsigned mtime, bool show_progress) {
|
const char* lpath, const char* rpath,
|
||||||
|
unsigned mtime) {
|
||||||
if (!sc.SendRequest(ID_SEND, path_and_mode)) {
|
if (!sc.SendRequest(ID_SEND, path_and_mode)) {
|
||||||
fprintf(stderr, "adb: failed to send ID_SEND message '%s': %s\n",
|
sc.Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno));
|
||||||
path_and_mode, strerror(errno));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long long size = 0;
|
struct stat st;
|
||||||
if (show_progress) {
|
if (stat(lpath, &st) == -1) {
|
||||||
// Determine local file size.
|
sc.Error("cannot stat '%s': %s", lpath, strerror(errno));
|
||||||
struct stat st;
|
return false;
|
||||||
if (stat(path, &st) == -1) {
|
|
||||||
fprintf(stderr, "adb: cannot stat '%s': %s\n", path, strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size = st.st_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int lfd = adb_open(path, O_RDONLY);
|
uint64_t total_size = st.st_size;
|
||||||
|
uint64_t bytes_copied = 0;
|
||||||
|
|
||||||
|
int lfd = adb_open(lpath, O_RDONLY);
|
||||||
if (lfd < 0) {
|
if (lfd < 0) {
|
||||||
fprintf(stderr, "adb: cannot open '%s': %s\n", path, strerror(errno));
|
sc.Error("cannot open '%s': %s", lpath, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -281,7 +283,7 @@ static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const c
|
||||||
int ret = adb_read(lfd, sbuf.data, sc.max);
|
int ret = adb_read(lfd, sbuf.data, sc.max);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "adb: cannot read '%s': %s\n", path, strerror(errno));
|
sc.Error("cannot read '%s': %s", lpath, strerror(errno));
|
||||||
adb_close(lfd);
|
adb_close(lfd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -295,9 +297,10 @@ static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const c
|
||||||
}
|
}
|
||||||
sc.total_bytes += ret;
|
sc.total_bytes += ret;
|
||||||
|
|
||||||
if (show_progress) {
|
bytes_copied += ret;
|
||||||
print_transfer_progress(sc.total_bytes, size);
|
|
||||||
}
|
int percentage = static_cast<int>(bytes_copied * 100 / total_size);
|
||||||
|
sc.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage));
|
||||||
}
|
}
|
||||||
|
|
||||||
adb_close(lfd);
|
adb_close(lfd);
|
||||||
|
|
@ -306,8 +309,7 @@ static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const c
|
||||||
msg.data.id = ID_DONE;
|
msg.data.id = ID_DONE;
|
||||||
msg.data.size = mtime;
|
msg.data.size = mtime;
|
||||||
if (!WriteFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
|
if (!WriteFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
|
||||||
fprintf(stderr, "adb: failed to send ID_DONE message for '%s': %s\n",
|
sc.Error("failed to send ID_DONE message for '%s': %s", rpath, strerror(errno));
|
||||||
path, strerror(errno));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,7 +317,7 @@ static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const c
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
|
static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
|
||||||
unsigned mtime, mode_t mode, bool show_progress)
|
unsigned mtime, mode_t mode)
|
||||||
{
|
{
|
||||||
std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
|
std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
|
||||||
|
|
||||||
|
|
@ -324,44 +326,48 @@ static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
|
||||||
char buf[PATH_MAX];
|
char buf[PATH_MAX];
|
||||||
ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
|
ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
|
||||||
if (data_length == -1) {
|
if (data_length == -1) {
|
||||||
fprintf(stderr, "adb: readlink '%s' failed: %s\n", lpath, strerror(errno));
|
sc.Error("readlink '%s' failed: %s", lpath, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
buf[data_length++] = '\0';
|
buf[data_length++] = '\0';
|
||||||
|
|
||||||
if (!sc.SendSmallFile(path_and_mode.c_str(), buf, data_length, mtime)) return false;
|
if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, buf, data_length, mtime)) return false;
|
||||||
return sc.CopyDone(lpath, rpath);
|
return sc.CopyDone(lpath, rpath);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!S_ISREG(mode)) {
|
if (!S_ISREG(mode)) {
|
||||||
fprintf(stderr, "adb: local file '%s' has unsupported mode: 0o%o\n", lpath, mode);
|
sc.Error("local file '%s' has unsupported mode: 0o%o", lpath, mode);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(lpath, &st) == -1) {
|
if (stat(lpath, &st) == -1) {
|
||||||
fprintf(stderr, "adb: failed to stat local file '%s': %s\n", lpath, strerror(errno));
|
sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (st.st_size < SYNC_DATA_MAX) {
|
if (st.st_size < SYNC_DATA_MAX) {
|
||||||
std::string data;
|
std::string data;
|
||||||
if (!android::base::ReadFileToString(lpath, &data)) {
|
if (!android::base::ReadFileToString(lpath, &data)) {
|
||||||
fprintf(stderr, "adb: failed to read all of '%s': %s\n", lpath, strerror(errno));
|
sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, data.data(), data.size(), mtime)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!sc.SendSmallFile(path_and_mode.c_str(), data.data(), data.size(), mtime)) return false;
|
|
||||||
} else {
|
} else {
|
||||||
if (!SendLargeFile(sc, path_and_mode.c_str(), lpath, mtime, show_progress)) return false;
|
if (!SendLargeFile(sc, path_and_mode.c_str(), lpath, rpath, mtime)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return sc.CopyDone(lpath, rpath);
|
return sc.CopyDone(lpath, rpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, bool show_progress) {
|
static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath) {
|
||||||
|
sc.Print(rpath);
|
||||||
|
|
||||||
unsigned size = 0;
|
unsigned size = 0;
|
||||||
if (show_progress) {
|
if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
|
||||||
if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sc.SendRequest(ID_RECV, rpath)) return false;
|
if (!sc.SendRequest(ID_RECV, rpath)) return false;
|
||||||
|
|
||||||
|
|
@ -369,10 +375,11 @@ static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
|
||||||
mkdirs(lpath);
|
mkdirs(lpath);
|
||||||
int lfd = adb_creat(lpath, 0644);
|
int lfd = adb_creat(lpath, 0644);
|
||||||
if (lfd < 0) {
|
if (lfd < 0) {
|
||||||
fprintf(stderr, "adb: cannot create '%s': %s\n", lpath, strerror(errno));
|
sc.Error("cannot create '%s': %s", lpath, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t bytes_copied = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
syncmsg msg;
|
syncmsg msg;
|
||||||
if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
|
if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
|
||||||
|
|
@ -391,7 +398,7 @@ static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.data.size > sc.max) {
|
if (msg.data.size > sc.max) {
|
||||||
fprintf(stderr, "adb: msg.data.size too large: %u (max %zu)\n", msg.data.size, sc.max);
|
sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
|
||||||
adb_close(lfd);
|
adb_close(lfd);
|
||||||
adb_unlink(lpath);
|
adb_unlink(lpath);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -405,7 +412,7 @@ static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!WriteFdExactly(lfd, buffer, msg.data.size)) {
|
if (!WriteFdExactly(lfd, buffer, msg.data.size)) {
|
||||||
fprintf(stderr, "adb: cannot write '%s': %s\n", lpath, strerror(errno));
|
sc.Error("cannot write '%s': %s", lpath, strerror(errno));
|
||||||
adb_close(lfd);
|
adb_close(lfd);
|
||||||
adb_unlink(lpath);
|
adb_unlink(lpath);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -413,9 +420,10 @@ static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
|
||||||
|
|
||||||
sc.total_bytes += msg.data.size;
|
sc.total_bytes += msg.data.size;
|
||||||
|
|
||||||
if (show_progress) {
|
bytes_copied += msg.data.size;
|
||||||
print_transfer_progress(sc.total_bytes, size);
|
|
||||||
}
|
int percentage = static_cast<int>(bytes_copied * 100 / size);
|
||||||
|
sc.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage));
|
||||||
}
|
}
|
||||||
|
|
||||||
adb_close(lfd);
|
adb_close(lfd);
|
||||||
|
|
@ -453,7 +461,7 @@ static copyinfo* mkcopyinfo(const char* spath, const char* dpath, const char* na
|
||||||
int dsize = dlen + nlen + 2;
|
int dsize = dlen + nlen + 2;
|
||||||
|
|
||||||
copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize));
|
copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize));
|
||||||
if(ci == 0) {
|
if (ci == 0) {
|
||||||
fprintf(stderr, "out of memory\n");
|
fprintf(stderr, "out of memory\n");
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
@ -475,23 +483,24 @@ static bool IsDotOrDotDot(const char* name) {
|
||||||
return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
|
return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int local_build_list(copyinfo** filelist, const char* lpath, const char* rpath) {
|
static int local_build_list(SyncConnection& sc,
|
||||||
|
copyinfo** filelist, const char* lpath, const char* rpath) {
|
||||||
copyinfo *dirlist = 0;
|
copyinfo *dirlist = 0;
|
||||||
copyinfo *ci, *next;
|
copyinfo *ci, *next;
|
||||||
|
|
||||||
std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(lpath), closedir);
|
std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(lpath), closedir);
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
fprintf(stderr, "adb: cannot open '%s': %s\n", lpath, strerror(errno));
|
sc.Error("cannot open '%s': %s", lpath, strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dirent *de;
|
dirent* de;
|
||||||
while ((de = readdir(dir.get()))) {
|
while ((de = readdir(dir.get()))) {
|
||||||
if (IsDotOrDotDot(de->d_name)) continue;
|
if (IsDotOrDotDot(de->d_name)) continue;
|
||||||
|
|
||||||
char stat_path[PATH_MAX];
|
char stat_path[PATH_MAX];
|
||||||
if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path)) {
|
if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path)) {
|
||||||
fprintf(stderr, "adb: skipping long path '%s%s'\n", lpath, de->d_name);
|
sc.Error("skipping long path '%s%s'", lpath, de->d_name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
strcpy(stat_path, lpath);
|
strcpy(stat_path, lpath);
|
||||||
|
|
@ -506,7 +515,7 @@ static int local_build_list(copyinfo** filelist, const char* lpath, const char*
|
||||||
} else {
|
} else {
|
||||||
ci = mkcopyinfo(lpath, rpath, de->d_name, 0);
|
ci = mkcopyinfo(lpath, rpath, de->d_name, 0);
|
||||||
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
|
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
|
||||||
fprintf(stderr, "adb: skipping special file '%s'\n", ci->src);
|
sc.Error("skipping special file '%s'", ci->src);
|
||||||
free(ci);
|
free(ci);
|
||||||
} else {
|
} else {
|
||||||
ci->time = st.st_mtime;
|
ci->time = st.st_mtime;
|
||||||
|
|
@ -517,7 +526,7 @@ static int local_build_list(copyinfo** filelist, const char* lpath, const char*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "adb: cannot lstat '%s': %s\n",stat_path , strerror(errno));
|
sc.Error("cannot lstat '%s': %s",stat_path , strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -525,7 +534,7 @@ static int local_build_list(copyinfo** filelist, const char* lpath, const char*
|
||||||
dir.reset();
|
dir.reset();
|
||||||
for (ci = dirlist; ci != 0; ci = next) {
|
for (ci = dirlist; ci != 0; ci = next) {
|
||||||
next = ci->next;
|
next = ci->next;
|
||||||
local_build_list(filelist, ci->src, ci->dst);
|
local_build_list(sc, filelist, ci->src, ci->dst);
|
||||||
free(ci);
|
free(ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -555,7 +564,7 @@ static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const c
|
||||||
rpath = tmp;
|
rpath = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (local_build_list(&filelist, lpath, rpath)) {
|
if (local_build_list(sc, &filelist, lpath, rpath)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -578,9 +587,12 @@ static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const c
|
||||||
for (ci = filelist; ci != 0; ci = next) {
|
for (ci = filelist; ci != 0; ci = next) {
|
||||||
next = ci->next;
|
next = ci->next;
|
||||||
if (ci->flag == 0) {
|
if (ci->flag == 0) {
|
||||||
fprintf(stderr, "%spush: %s -> %s\n", list_only ? "would " : "", ci->src, ci->dst);
|
if (list_only) {
|
||||||
if (!list_only && !sync_send(sc, ci->src, ci->dst, ci->time, ci->mode, false)) {
|
fprintf(stderr, "would push: %s -> %s\n", ci->src, ci->dst);
|
||||||
return false;
|
} else {
|
||||||
|
if (!sync_send(sc, ci->src, ci->dst, ci->time, ci->mode)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pushed++;
|
pushed++;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -589,20 +601,21 @@ static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const c
|
||||||
free(ci);
|
free(ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "%d file%s pushed. %d file%s skipped.\n",
|
sc.Print(android::base::StringPrintf("%s: %d file%s pushed. %d file%s skipped.%s\n",
|
||||||
pushed, (pushed == 1) ? "" : "s",
|
rpath,
|
||||||
skipped, (skipped == 1) ? "" : "s");
|
pushed, (pushed == 1) ? "" : "s",
|
||||||
|
skipped, (skipped == 1) ? "" : "s",
|
||||||
|
sc.TransferRate().c_str()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool do_sync_push(const char* lpath, const char* rpath, bool show_progress) {
|
bool do_sync_push(const char* lpath, const char* rpath) {
|
||||||
SyncConnection sc;
|
SyncConnection sc;
|
||||||
if (!sc.IsValid()) return false;
|
if (!sc.IsValid()) return false;
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(lpath, &st)) {
|
if (stat(lpath, &st)) {
|
||||||
fprintf(stderr, "adb: cannot stat '%s': %s\n", lpath, strerror(errno));
|
sc.Error("cannot stat '%s': %s", lpath, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -619,21 +632,23 @@ bool do_sync_push(const char* lpath, const char* rpath, bool show_progress) {
|
||||||
path_holder = android::base::StringPrintf("%s/%s", rpath, adb_basename(lpath).c_str());
|
path_holder = android::base::StringPrintf("%s/%s", rpath, adb_basename(lpath).c_str());
|
||||||
rpath = path_holder.c_str();
|
rpath = path_holder.c_str();
|
||||||
}
|
}
|
||||||
return sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode, show_progress);
|
bool result = sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode);
|
||||||
|
sc.Print("\n");
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct sync_ls_build_list_cb_args {
|
struct sync_ls_build_list_cb_args {
|
||||||
copyinfo **filelist;
|
SyncConnection* sc;
|
||||||
copyinfo **dirlist;
|
copyinfo** filelist;
|
||||||
const char *rpath;
|
copyinfo** dirlist;
|
||||||
const char *lpath;
|
const char* rpath;
|
||||||
|
const char* lpath;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
|
static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
|
||||||
const char* name, void* cookie)
|
const char* name, void* cookie)
|
||||||
{
|
{
|
||||||
sync_ls_build_list_cb_args *args = (sync_ls_build_list_cb_args *)cookie;
|
sync_ls_build_list_cb_args* args = static_cast<sync_ls_build_list_cb_args*>(cookie);
|
||||||
copyinfo *ci;
|
copyinfo *ci;
|
||||||
|
|
||||||
if (S_ISDIR(mode)) {
|
if (S_ISDIR(mode)) {
|
||||||
|
|
@ -655,28 +670,29 @@ static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
|
||||||
ci->next = *filelist;
|
ci->next = *filelist;
|
||||||
*filelist = ci;
|
*filelist = ci;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "adb: skipping special file '%s'\n", name);
|
args->sc->Print(android::base::StringPrintf("skipping special file '%s'\n", name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool remote_build_list(SyncConnection& sc, copyinfo **filelist,
|
static bool remote_build_list(SyncConnection& sc, copyinfo **filelist,
|
||||||
const char *rpath, const char *lpath) {
|
const char *rpath, const char *lpath) {
|
||||||
copyinfo *dirlist = NULL;
|
copyinfo* dirlist = nullptr;
|
||||||
sync_ls_build_list_cb_args args;
|
|
||||||
|
|
||||||
|
sync_ls_build_list_cb_args args;
|
||||||
|
args.sc = ≻
|
||||||
args.filelist = filelist;
|
args.filelist = filelist;
|
||||||
args.dirlist = &dirlist;
|
args.dirlist = &dirlist;
|
||||||
args.rpath = rpath;
|
args.rpath = rpath;
|
||||||
args.lpath = lpath;
|
args.lpath = lpath;
|
||||||
|
|
||||||
// Put the files/dirs in rpath on the lists.
|
// Put the files/dirs in rpath on the lists.
|
||||||
if (!sync_ls(sc, rpath, sync_ls_build_list_cb, (void *)&args)) {
|
if (!sync_ls(sc, rpath, sync_ls_build_list_cb, &args)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recurse into each directory we found.
|
// Recurse into each directory we found.
|
||||||
while (dirlist != NULL) {
|
while (dirlist != NULL) {
|
||||||
copyinfo *next = dirlist->next;
|
copyinfo* next = dirlist->next;
|
||||||
if (!remote_build_list(sc, filelist, dirlist->src, dirlist->dst)) {
|
if (!remote_build_list(sc, filelist, dirlist->src, dirlist->dst)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -710,7 +726,7 @@ static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const c
|
||||||
if (lpath_clean.back() != '/') lpath_clean.push_back('/');
|
if (lpath_clean.back() != '/') lpath_clean.push_back('/');
|
||||||
|
|
||||||
// Recursively build the list of files to copy.
|
// Recursively build the list of files to copy.
|
||||||
fprintf(stderr, "pull: building file list...\n");
|
sc.Print("pull: building file list...");
|
||||||
copyinfo* filelist = nullptr;
|
copyinfo* filelist = nullptr;
|
||||||
if (!remote_build_list(sc, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false;
|
if (!remote_build_list(sc, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false;
|
||||||
|
|
||||||
|
|
@ -720,8 +736,8 @@ static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const c
|
||||||
while (ci) {
|
while (ci) {
|
||||||
copyinfo* next = ci->next;
|
copyinfo* next = ci->next;
|
||||||
if (ci->flag == 0) {
|
if (ci->flag == 0) {
|
||||||
fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
|
sc.Print(android::base::StringPrintf("pull: %s -> %s", ci->src, ci->dst));
|
||||||
if (!sync_recv(sc, ci->src, ci->dst, false)) {
|
if (!sync_recv(sc, ci->src, ci->dst)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -736,20 +752,22 @@ static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const c
|
||||||
ci = next;
|
ci = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n",
|
sc.Print(android::base::StringPrintf("%s: %d file%s pulled. %d file%s skipped.%s\n",
|
||||||
pulled, (pulled == 1) ? "" : "s",
|
rpath,
|
||||||
skipped, (skipped == 1) ? "" : "s");
|
pulled, (pulled == 1) ? "" : "s",
|
||||||
|
skipped, (skipped == 1) ? "" : "s",
|
||||||
|
sc.TransferRate().c_str()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool do_sync_pull(const char* rpath, const char* lpath, bool show_progress, int copy_attrs) {
|
bool do_sync_pull(const char* rpath, const char* lpath, int copy_attrs) {
|
||||||
SyncConnection sc;
|
SyncConnection sc;
|
||||||
if (!sc.IsValid()) return false;
|
if (!sc.IsValid()) return false;
|
||||||
|
|
||||||
unsigned mode, time;
|
unsigned mode, time;
|
||||||
if (!sync_stat(sc, rpath, &time, &mode, nullptr)) return false;
|
if (!sync_stat(sc, rpath, &time, &mode, nullptr)) return false;
|
||||||
if (mode == 0) {
|
if (mode == 0) {
|
||||||
fprintf(stderr, "adb: remote object '%s' does not exist\n", rpath);
|
sc.Error("remote object '%s' does not exist", rpath);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -764,25 +782,24 @@ bool do_sync_pull(const char* rpath, const char* lpath, bool show_progress, int
|
||||||
lpath = path_holder.c_str();
|
lpath = path_holder.c_str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!sync_recv(sc, rpath, lpath, show_progress)) {
|
if (!sync_recv(sc, rpath, lpath)) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (copy_attrs && set_time_and_mode(lpath, time, mode)) {
|
if (copy_attrs && set_time_and_mode(lpath, time, mode)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sc.Print("\n");
|
||||||
return true;
|
return true;
|
||||||
} else if (S_ISDIR(mode)) {
|
} else if (S_ISDIR(mode)) {
|
||||||
return copy_remote_dir_local(sc, rpath, lpath, copy_attrs);
|
return copy_remote_dir_local(sc, rpath, lpath, copy_attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "adb: remote object '%s' not a file or directory\n", rpath);
|
sc.Error("remote object '%s' not a file or directory", rpath);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
|
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
|
||||||
fprintf(stderr, "syncing %s...\n", rpath.c_str());
|
|
||||||
|
|
||||||
SyncConnection sc;
|
SyncConnection sc;
|
||||||
if (!sc.IsValid()) return false;
|
if (!sc.IsValid()) return false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,9 @@ union syncmsg {
|
||||||
|
|
||||||
void file_sync_service(int fd, void* cookie);
|
void file_sync_service(int fd, void* cookie);
|
||||||
bool do_sync_ls(const char* path);
|
bool do_sync_ls(const char* path);
|
||||||
bool do_sync_push(const char* lpath, const char* rpath, bool show_progress);
|
bool do_sync_push(const char* lpath, const char* rpath);
|
||||||
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
|
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, bool show_progress, int copy_attrs);
|
bool do_sync_pull(const char* rpath, const char* lpath, int copy_attrs);
|
||||||
|
|
||||||
#define SYNC_DATA_MAX (64*1024)
|
#define SYNC_DATA_MAX (64*1024)
|
||||||
|
|
||||||
|
|
|
||||||
161
adb/line_printer.cpp
Normal file
161
adb/line_printer.cpp
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "line_printer.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Make sure printf is really adb_printf which works for UTF-8 on Windows.
|
||||||
|
#include <sysdeps.h>
|
||||||
|
|
||||||
|
// Stuff from ninja's util.h that's needed below.
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
string ElideMiddle(const string& str, size_t width) {
|
||||||
|
const int kMargin = 3; // Space for "...".
|
||||||
|
string result = str;
|
||||||
|
if (result.size() + kMargin > width) {
|
||||||
|
size_t elide_size = (width - kMargin) / 2;
|
||||||
|
result = result.substr(0, elide_size)
|
||||||
|
+ "..."
|
||||||
|
+ result.substr(result.size() - elide_size, elide_size);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) {
|
||||||
|
#ifndef _WIN32
|
||||||
|
const char* term = getenv("TERM");
|
||||||
|
smart_terminal_ = 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
|
||||||
|
// buffering. However, for Win32, the behavior is the same as _IOFBF
|
||||||
|
// - Full Buffering."
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
|
console_ = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||||
|
smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinePrinter::Print(string to_print, LineType type) {
|
||||||
|
if (console_locked_) {
|
||||||
|
line_buffer_ = to_print;
|
||||||
|
line_type_ = type;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smart_terminal_) {
|
||||||
|
printf("\r"); // Print over previous line, if any.
|
||||||
|
// On Windows, calling a C library function writing to stdout also handles
|
||||||
|
// pausing the executable when the "Pause" key or Ctrl-S is pressed.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smart_terminal_ && type == ELIDE) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||||
|
GetConsoleScreenBufferInfo(console_, &csbi);
|
||||||
|
|
||||||
|
// TODO: const std::wstring to_print_wide = widen(to_print);
|
||||||
|
// TODO: wstring ElideMiddle.
|
||||||
|
to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
|
||||||
|
// We don't want to have the cursor spamming back and forth, so instead of
|
||||||
|
// printf use WriteConsoleOutput which updates the contents of the buffer,
|
||||||
|
// but doesn't move the cursor position.
|
||||||
|
COORD buf_size = { csbi.dwSize.X, 1 };
|
||||||
|
COORD zero_zero = { 0, 0 };
|
||||||
|
SMALL_RECT target = {
|
||||||
|
csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
|
||||||
|
static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
|
||||||
|
csbi.dwCursorPosition.Y
|
||||||
|
};
|
||||||
|
vector<CHAR_INFO> char_data(csbi.dwSize.X);
|
||||||
|
for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
|
||||||
|
// TODO: UnicodeChar instead of AsciiChar, to_print_wide[i].
|
||||||
|
char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';
|
||||||
|
char_data[i].Attributes = csbi.wAttributes;
|
||||||
|
}
|
||||||
|
// TODO: WriteConsoleOutputW.
|
||||||
|
WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);
|
||||||
|
#else
|
||||||
|
// Limit output to width of the terminal if provided so we don't cause
|
||||||
|
// line-wrapping.
|
||||||
|
winsize size;
|
||||||
|
if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
|
||||||
|
to_print = ElideMiddle(to_print, size.ws_col);
|
||||||
|
}
|
||||||
|
printf("%s", to_print.c_str());
|
||||||
|
printf("\x1B[K"); // Clear to end of line.
|
||||||
|
fflush(stdout);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
have_blank_line_ = false;
|
||||||
|
} else {
|
||||||
|
printf("%s\n", to_print.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinePrinter::PrintOrBuffer(const char* data, size_t size) {
|
||||||
|
if (console_locked_) {
|
||||||
|
output_buffer_.append(data, size);
|
||||||
|
} else {
|
||||||
|
// Avoid printf and C strings, since the actual output might contain null
|
||||||
|
// bytes like UTF-16 does (yuck).
|
||||||
|
fwrite(data, 1, size, stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinePrinter::PrintOnNewLine(const string& to_print) {
|
||||||
|
if (console_locked_ && !line_buffer_.empty()) {
|
||||||
|
output_buffer_.append(line_buffer_);
|
||||||
|
output_buffer_.append(1, '\n');
|
||||||
|
line_buffer_.clear();
|
||||||
|
}
|
||||||
|
if (!have_blank_line_) {
|
||||||
|
PrintOrBuffer("\n", 1);
|
||||||
|
}
|
||||||
|
if (!to_print.empty()) {
|
||||||
|
PrintOrBuffer(&to_print[0], to_print.size());
|
||||||
|
}
|
||||||
|
have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinePrinter::SetConsoleLocked(bool locked) {
|
||||||
|
if (locked == console_locked_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (locked)
|
||||||
|
PrintOnNewLine("");
|
||||||
|
|
||||||
|
console_locked_ = locked;
|
||||||
|
|
||||||
|
if (!locked) {
|
||||||
|
PrintOnNewLine(output_buffer_);
|
||||||
|
if (!line_buffer_.empty()) {
|
||||||
|
Print(line_buffer_, line_type_);
|
||||||
|
}
|
||||||
|
output_buffer_.clear();
|
||||||
|
line_buffer_.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
71
adb/line_printer.h
Normal file
71
adb/line_printer.h
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef NINJA_LINE_PRINTER_H_
|
||||||
|
#define NINJA_LINE_PRINTER_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/// Prints lines of text, possibly overprinting previously printed lines
|
||||||
|
/// if the terminal supports it.
|
||||||
|
struct LinePrinter {
|
||||||
|
LinePrinter();
|
||||||
|
|
||||||
|
bool is_smart_terminal() const { return smart_terminal_; }
|
||||||
|
void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
|
||||||
|
|
||||||
|
enum LineType {
|
||||||
|
FULL,
|
||||||
|
ELIDE
|
||||||
|
};
|
||||||
|
/// Overprints the current line. If type is ELIDE, elides to_print to fit on
|
||||||
|
/// one line.
|
||||||
|
void Print(std::string to_print, LineType type);
|
||||||
|
|
||||||
|
/// Prints a string on a new line, not overprinting previous output.
|
||||||
|
void PrintOnNewLine(const std::string& to_print);
|
||||||
|
|
||||||
|
/// Lock or unlock the console. Any output sent to the LinePrinter while the
|
||||||
|
/// console is locked will not be printed until it is unlocked.
|
||||||
|
void SetConsoleLocked(bool locked);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Whether we can do fancy terminal control codes.
|
||||||
|
bool smart_terminal_;
|
||||||
|
|
||||||
|
/// Whether the caret is at the beginning of a blank line.
|
||||||
|
bool have_blank_line_;
|
||||||
|
|
||||||
|
/// Whether console is locked.
|
||||||
|
bool console_locked_;
|
||||||
|
|
||||||
|
/// Buffered current line while console is locked.
|
||||||
|
std::string line_buffer_;
|
||||||
|
|
||||||
|
/// Buffered line type while console is locked.
|
||||||
|
LineType line_type_;
|
||||||
|
|
||||||
|
/// Buffered console output while console is locked.
|
||||||
|
std::string output_buffer_;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
void* console_;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Print the given data to the console, or buffer it if it is locked.
|
||||||
|
void PrintOrBuffer(const char *data, size_t size);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NINJA_LINE_PRINTER_H_
|
||||||
Loading…
Add table
Reference in a new issue