Commit 8532ed11848092d816aecc2d34d1bb432a525df9

Patrick Steinhardt 2019-10-18T12:14:19

refdb_fs: convert reflog parsing to use parser The refdb_fs code to parse the reflog currently uses a hand-rolled parser. Convert it to use our `git_parse_ctx` structure instead.

diff --git a/src/parse.c b/src/parse.c
index b04fda3..0a10758 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -101,6 +101,16 @@ int git_parse_advance_digit(int64_t *out, git_parse_ctx *ctx, int base)
 	return 0;
 }
 
+int git_parse_advance_oid(git_oid *out, git_parse_ctx *ctx)
+{
+	if (ctx->line_len < GIT_OID_HEXSZ)
+		return -1;
+	if ((git_oid_fromstrn(out, ctx->line, GIT_OID_HEXSZ)) < 0)
+		return -1;
+	git_parse_advance_chars(ctx, GIT_OID_HEXSZ);
+	return 0;
+}
+
 int git_parse_peek(char *out, git_parse_ctx *ctx, int flags)
 {
 	size_t remain = ctx->line_len;
diff --git a/src/parse.h b/src/parse.h
index 188ac28..0ecb7c1 100644
--- a/src/parse.h
+++ b/src/parse.h
@@ -50,6 +50,7 @@ int git_parse_advance_expected(
 int git_parse_advance_ws(git_parse_ctx *ctx);
 int git_parse_advance_nl(git_parse_ctx *ctx);
 int git_parse_advance_digit(int64_t *out, git_parse_ctx *ctx, int base);
+int git_parse_advance_oid(git_oid *out, git_parse_ctx *ctx);
 
 enum GIT_PARSE_PEEK_FLAGS {
 	GIT_PARSE_PEEK_SKIP_WHITESPACE = (1 << 0)
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 449dedf..1bf300c 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -13,6 +13,7 @@
 #include "futils.h"
 #include "filebuf.h"
 #include "pack.h"
+#include "parse.h"
 #include "reflog.h"
 #include "refdb.h"
 #include "iterator.h"
@@ -1651,67 +1652,53 @@ static int reflog_alloc(git_reflog **reflog, const char *name)
 
 static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
 {
-	const char *ptr;
-	git_reflog_entry *entry;
+	git_parse_ctx parser = GIT_PARSE_CTX_INIT;
+	git_reflog_entry *entry = NULL;
 
-#define seek_forward(_increase) do { \
-	if (_increase >= buf_size) { \
-		git_error_set(GIT_ERROR_INVALID, "ran out of data while parsing reflog"); \
-		goto fail; \
-	} \
-	buf += _increase; \
-	buf_size -= _increase; \
-	} while (0)
-
-	while (buf_size > GIT_REFLOG_SIZE_MIN) {
-		entry = git__calloc(1, sizeof(git_reflog_entry));
-		GIT_ERROR_CHECK_ALLOC(entry);
+	if ((git_parse_ctx_init(&parser, buf, buf_size)) < 0)
+		return -1;
 
-		entry->committer = git__calloc(1, sizeof(git_signature));
-		GIT_ERROR_CHECK_ALLOC(entry->committer);
+	for (; parser.remain_len; git_parse_advance_line(&parser)) {
+		const char *sig;
+		char c;
 
-		if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
-			goto fail;
-		seek_forward(GIT_OID_HEXSZ + 1);
+		entry = git__calloc(1, sizeof(*entry));
+		GIT_ERROR_CHECK_ALLOC(entry);
 
-		if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
-			goto fail;
-		seek_forward(GIT_OID_HEXSZ + 1);
+		entry->committer = git__calloc(1, sizeof(*entry->committer));
+		GIT_ERROR_CHECK_ALLOC(entry->committer);
 
-		ptr = buf;
+		if (git_parse_advance_oid(&entry->oid_old, &parser) < 0 ||
+		    git_parse_advance_expected(&parser, " ", 1) < 0 ||
+		    git_parse_advance_oid(&entry->oid_cur, &parser) < 0)
+			goto error;
 
-		/* Seek forward to the end of the signature. */
-		while (*buf && *buf != '\t' && *buf != '\n')
-			seek_forward(1);
+		sig = parser.line;
+		while (git_parse_peek(&c, &parser, 0) == 0 && c != '\t' && c != '\n')
+			git_parse_advance_chars(&parser, 1);
 
-		if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
-			goto fail;
+		if (git_signature__parse(entry->committer, &sig, parser.line, NULL, 0) < 0)
+			goto error;
 
-		if (*buf == '\t') {
-			/* We got a message. Read everything till we reach LF. */
-			seek_forward(1);
-			ptr = buf;
+		if (c == '\t') {
+			size_t len;
+			git_parse_advance_chars(&parser, 1);
 
-			while (*buf && *buf != '\n')
-				seek_forward(1);
+			len = parser.line_len;
+			if (parser.line[len - 1] == '\n')
+				len--;
 
-			entry->msg = git__strndup(ptr, buf - ptr);
+			entry->msg = git__strndup(parser.line, len);
 			GIT_ERROR_CHECK_ALLOC(entry->msg);
-		} else
-			entry->msg = NULL;
-
-		while (*buf && *buf == '\n' && buf_size > 1)
-			seek_forward(1);
+		}
 
 		if (git_vector_insert(&log->entries, entry) < 0)
-			goto fail;
+			goto error;
 	}
 
 	return 0;
 
-#undef seek_forward
-
-fail:
+error:
 	git_reflog_entry__free(entry);
 
 	return -1;