Commit f1c6967f90f720a023714672e6751755b755e2a5

Stefan Sperling 2020-03-19T22:43:44

fix chopped display of git-server's progress output

diff --git a/include/got_fetch.h b/include/got_fetch.h
index 8b9f1d3..43417f2 100644
--- a/include/got_fetch.h
+++ b/include/got_fetch.h
@@ -24,6 +24,8 @@
 
 #define GOT_FETCH_DEFAULT_REMOTE_NAME	"origin"
 
+#define GOT_FETCH_PKTMAX	65536
+
 /*
  * Attempt to parse a URI into the following parts:
  * A protocol scheme, hostname, port number (as a string), path on server,
diff --git a/lib/fetch.c b/lib/fetch.c
index 1f30cd2..0fa6534 100644
--- a/lib/fetch.c
+++ b/lib/fetch.c
@@ -409,6 +409,7 @@ got_fetch_pack(struct got_object_id **pack_hash, struct got_pathlist_head *refs,
 	char *ref_prefix = NULL;
 	size_t ref_prefixlen = 0;
 	char *path;
+	char *progress = NULL;
 
 	*pack_hash = NULL;
 	for (i = 0; i < nitems(tmpfds); i++)
@@ -546,6 +547,11 @@ got_fetch_pack(struct got_object_id **pack_hash, struct got_pathlist_head *refs,
 	}
 
 	packfile_size = 0;
+	progress = calloc(GOT_FETCH_PKTMAX, 1);
+	if (progress == NULL) {
+		err = got_error_from_errno("calloc");
+		goto done;
+	}
 	while (!done) {
 		struct got_object_id *id = NULL;
 		char *refname = NULL;
@@ -562,20 +568,45 @@ got_fetch_pack(struct got_object_id **pack_hash, struct got_pathlist_head *refs,
 				*pack_hash = id;
 			else
 				free(id);
-		}
-		else if (refname && id) {
+		} else if (refname && id) {
 			err = got_pathlist_append(refs, refname, id);
 			if (err)
 				goto done;
 		} else if (server_progress) {
-			char *s, *s0 = server_progress;
-			while ((s = strsep(&s0, "\r")) != NULL) {
-				if (*s == '\0')
-					continue;
+			char *p;
+			/*
+			 * XXX git-daemon tends to send batched output with
+			 * lines spanning separate packets. Buffer progress
+			 * output until we see a CR or LF to avoid giving
+			 * partial lines of progress output to the callback.
+			 */
+			if (strlcat(progress, server_progress,
+			    GOT_FETCH_PKTMAX) >= GOT_FETCH_PKTMAX) {
+				progress[0] = '\0'; /* discard */
+				continue;
+			}
+			while ((p = strchr(progress, '\r')) != NULL ||
+			    (p = strchr(progress, '\n')) != NULL) {
+				char *s;
+				size_t n;
+				char c = *p;
+				*p = '\0';
+				if (asprintf(&s, "%s%s", progress,
+				    c == '\n' ? "\n" : "") == -1) {
+					err = got_error_from_errno("asprintf");
+					goto done;
+				}
 				err = progress_cb(progress_arg, s,
 				    packfile_size_cur, 0, 0, 0, 0);
+				free(s);
 				if (err)
 					break;
+				n = strlen(progress);
+				if (n < GOT_FETCH_PKTMAX - 1) {
+					memmove(progress, &progress[n + 1],
+					    GOT_FETCH_PKTMAX - n - 1);
+				} else
+					progress[0] = '\0';
 			}
 			free(server_progress);
 			if (err)
@@ -714,6 +745,7 @@ done:
 	free(idxpath);
 	free(packpath);
 	free(ref_prefix);
+	free(progress);
 
 	TAILQ_FOREACH(pe, &have_refs, entry) {
 		free((char *)pe->path);
diff --git a/libexec/got-fetch-pack/got-fetch-pack.c b/libexec/got-fetch-pack/got-fetch-pack.c
index 5f2e668..ff77adb 100644
--- a/libexec/got-fetch-pack/got-fetch-pack.c
+++ b/libexec/got-fetch-pack/got-fetch-pack.c
@@ -39,6 +39,7 @@
 #include "got_object.h"
 #include "got_path.h"
 #include "got_version.h"
+#include "got_fetch.h"
 
 #include "got_lib_sha1.h"
 #include "got_lib_delta.h"
@@ -51,8 +52,6 @@
 #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
 #endif
 
-#define GOT_PKTMAX	65536
-
 struct got_object *indexed;
 static char *fetchbranch;
 static struct got_object_id zhash = {.sha1={0}};
@@ -451,7 +450,7 @@ fetch_pack(int fd, int packfd, struct got_object_id *packid,
     struct got_pathlist_head *have_refs, struct imsgbuf *ibuf)
 {
 	const struct got_error *err = NULL;
-	char buf[GOT_PKTMAX];
+	char buf[GOT_FETCH_PKTMAX];
 	char hashstr[SHA1_DIGEST_STRING_LENGTH];
 	struct got_object_id *have, *want;
 	int is_firstpkt = 1, nref = 0, refsz = 16;