am 5fb1a34a: Merge "Added transfer progress reporting for push and pull commands."

* commit '5fb1a34af4128bfe7332b02e31b146a82bc2872d':
  Added transfer progress reporting for push and pull commands.
diff --git a/adb/commandline.c b/adb/commandline.c
index c9bb437..83b568d 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -107,8 +107,12 @@
         "                                 will disconnect from all connected TCP/IP devices.\n"
         "\n"
         "device commands:\n"
-        "  adb push <local> <remote>    - copy file/dir to device\n"
-        "  adb pull <remote> [<local>]  - copy file/dir from device\n"
+        "  adb push [-p] <local> <remote>\n"
+        "                               - copy file/dir to device\n"
+        "                                 ('-p' to display the transfer progress)\n"
+        "  adb pull [-p] <remote> [<local>]\n"
+        "                               - copy file/dir from device\n"
+        "                                 ('-p' to display the transfer progress)\n"
         "  adb sync [ <directory> ]     - copy host->device only if changed\n"
         "                                 (-l means list but don't copy)\n"
         "                                 (see 'adb help all')\n"
@@ -921,6 +925,28 @@
     return path_buf;
 }
 
+
+static void parse_push_pull_args(char** arg, int narg, char const** path1, char const** path2,
+                                 int* show_progress) {
+    *show_progress = 0;
+
+    if ((narg > 0) && !strcmp(*arg, "-p")) {
+        *show_progress = 1;
+        ++arg;
+        --narg;
+    }
+
+    if (narg > 0) {
+        *path1 = *arg;
+        ++arg;
+        --narg;
+    }
+
+    if (narg > 0) {
+        *path2 = *arg;
+    }
+}
+
 int adb_commandline(int argc, char **argv)
 {
     char buf[4096];
@@ -1368,18 +1394,29 @@
     }
 
     if(!strcmp(argv[0], "push")) {
-        if(argc != 3) return usage();
-        return do_sync_push(argv[1], argv[2], 0 /* no verify APK */);
+        int show_progress = 0;
+        const char* lpath = NULL, *rpath = NULL;
+
+        parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress);
+
+        if ((lpath != NULL) && (rpath != NULL)) {
+            return do_sync_push(lpath, rpath, 0 /* no verify APK */, show_progress);
+        }
+
+        return usage();
     }
 
     if(!strcmp(argv[0], "pull")) {
-        if (argc == 2) {
-            return do_sync_pull(argv[1], ".");
-        } else if (argc == 3) {
-            return do_sync_pull(argv[1], argv[2]);
-        } else {
-            return usage();
+        int show_progress = 0;
+        const char* rpath = NULL, *lpath = ".";
+
+        parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress);
+
+        if (rpath != NULL) {
+            return do_sync_pull(rpath, lpath, show_progress);
         }
+
+        return usage();
     }
 
     if(!strcmp(argv[0], "install")) {
@@ -1717,7 +1754,7 @@
         }
     }
 
-    err = do_sync_push(apk_file, apk_dest, verify_apk);
+    err = do_sync_push(apk_file, apk_dest, verify_apk, 0 /* no show progress */);
     if (err) {
         goto cleanup_apk;
     } else {
@@ -1725,7 +1762,8 @@
     }
 
     if (verification_file != NULL) {
-        err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */);
+        err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */,
+                           0 /* no show progress */);
         if (err) {
             goto cleanup_apk;
         } else {
diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c
index 9fec081..8fad50e 100644
--- a/adb/file_sync_client.c
+++ b/adb/file_sync_client.c
@@ -62,6 +62,22 @@
             total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
 }
 
+static const char* transfer_progress_format = "\rTransferring: %llu/%llu (%d%%)";
+
+static void print_transfer_progress(unsigned long long bytes_current,
+                                    unsigned long long bytes_total) {
+    if (bytes_total == 0) return;
+
+    fprintf(stderr, transfer_progress_format, bytes_current, bytes_total,
+            (int) (bytes_current * 100 / bytes_total));
+
+    if (bytes_current == bytes_total) {
+        fputc('\n', stderr);
+    }
+
+    fflush(stderr);
+}
+
 void sync_quit(int fd)
 {
     syncmsg msg;
@@ -207,9 +223,10 @@
     return 0;
 }
 
-static int write_data_file(int fd, const char *path, syncsendbuf *sbuf)
+static int write_data_file(int fd, const char *path, syncsendbuf *sbuf, int show_progress)
 {
     int lfd, err = 0;
+    unsigned long long size = 0;
 
     lfd = adb_open(path, O_RDONLY);
     if(lfd < 0) {
@@ -217,6 +234,17 @@
         return -1;
     }
 
+    if (show_progress) {
+        // Determine local file size.
+        struct stat st;
+        if (lstat(path, &st)) {
+            fprintf(stderr,"cannot stat '%s': %s\n", path, strerror(errno));
+            return -1;
+        }
+
+        size = st.st_size;
+    }
+
     sbuf->id = ID_DATA;
     for(;;) {
         int ret;
@@ -238,13 +266,18 @@
             break;
         }
         total_bytes += ret;
+
+        if (show_progress) {
+            print_transfer_progress(total_bytes, size);
+        }
     }
 
     adb_close(lfd);
     return err;
 }
 
-static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf)
+static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf,
+                             int show_progress)
 {
     int err = 0;
     int total = 0;
@@ -264,6 +297,10 @@
         }
         total += count;
         total_bytes += count;
+
+        if (show_progress) {
+            print_transfer_progress(total, size);
+        }
     }
 
     return err;
@@ -295,7 +332,7 @@
 #endif
 
 static int sync_send(int fd, const char *lpath, const char *rpath,
-                     unsigned mtime, mode_t mode, int verifyApk)
+                     unsigned mtime, mode_t mode, int verifyApk, int show_progress)
 {
     syncmsg msg;
     int len, r;
@@ -377,10 +414,10 @@
     }
 
     if (file_buffer) {
-        write_data_buffer(fd, file_buffer, size, sbuf);
+        write_data_buffer(fd, file_buffer, size, sbuf, show_progress);
         free(file_buffer);
     } else if (S_ISREG(mode))
-        write_data_file(fd, lpath, sbuf);
+        write_data_file(fd, lpath, sbuf, show_progress);
 #ifdef HAVE_SYMLINKS
     else if (S_ISLNK(mode))
         write_data_link(fd, lpath, sbuf);
@@ -438,17 +475,38 @@
     return 0;
 }
 
-int sync_recv(int fd, const char *rpath, const char *lpath)
+int sync_recv(int fd, const char *rpath, const char *lpath, int show_progress)
 {
     syncmsg msg;
     int len;
     int lfd = -1;
     char *buffer = send_buffer.data;
     unsigned id;
+    unsigned long long size = 0;
 
     len = strlen(rpath);
     if(len > 1024) return -1;
 
+    if (show_progress) {
+        // Determine remote file size.
+        syncmsg stat_msg;
+        stat_msg.req.id = ID_STAT;
+        stat_msg.req.namelen = htoll(len);
+
+        if (writex(fd, &stat_msg.req, sizeof(stat_msg.req)) ||
+            writex(fd, rpath, len)) {
+            return -1;
+        }
+
+        if (readx(fd, &stat_msg.stat, sizeof(stat_msg.stat))) {
+            return -1;
+        }
+
+        if (stat_msg.stat.id != ID_STAT) return -1;
+
+        size = ltohl(stat_msg.stat.size);
+    }
+
     msg.req.id = ID_RECV;
     msg.req.namelen = htoll(len);
     if(writex(fd, &msg.req, sizeof(msg.req)) ||
@@ -502,6 +560,10 @@
         }
 
         total_bytes += len;
+
+        if (show_progress) {
+            print_transfer_progress(total_bytes, size);
+        }
     }
 
     adb_close(lfd);
@@ -721,7 +783,8 @@
         if(ci->flag == 0) {
             fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst);
             if(!listonly &&
-               sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */)){
+               sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */,
+                         0 /* no show progress */)) {
                 return 1;
             }
             pushed++;
@@ -739,7 +802,7 @@
 }
 
 
-int do_sync_push(const char *lpath, const char *rpath, int verifyApk)
+int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_progress)
 {
     struct stat st;
     unsigned mode;
@@ -786,7 +849,7 @@
             rpath = tmp;
         }
         BEGIN();
-        if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk)) {
+        if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk, show_progress)) {
             return 1;
         } else {
             END();
@@ -923,7 +986,7 @@
         next = ci->next;
         if (ci->flag == 0) {
             fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
-            if (sync_recv(fd, ci->src, ci->dst)) {
+            if (sync_recv(fd, ci->src, ci->dst, 0 /* no show progress */)) {
                 return 1;
             }
             pulled++;
@@ -940,7 +1003,7 @@
     return 0;
 }
 
-int do_sync_pull(const char *rpath, const char *lpath)
+int do_sync_pull(const char *rpath, const char *lpath, int show_progress)
 {
     unsigned mode;
     struct stat st;
@@ -981,7 +1044,7 @@
             }
         }
         BEGIN();
-        if(sync_recv(fd, rpath, lpath)) {
+        if (sync_recv(fd, rpath, lpath, show_progress)) {
             return 1;
         } else {
             END();
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index e402e06..3e7e096 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -72,15 +72,15 @@
     struct {
         unsigned id;
         unsigned msglen;
-    } status;    
+    } status;
 } syncmsg;
 
 
 void file_sync_service(int fd, void *cookie);
 int do_sync_ls(const char *path);
-int do_sync_push(const char *lpath, const char *rpath, int verifyApk);
+int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_progress);
 int do_sync_sync(const char *lpath, const char *rpath, int listonly);
-int do_sync_pull(const char *rpath, const char *lpath);
+int do_sync_pull(const char *rpath, const char *lpath, int show_progress);
 
 #define SYNC_DATA_MAX (64*1024)