Commit Diff


commit - 6332351956c375744e908c1f895721ba3a5157d2
commit + d1cda8264f62723250746b16cb897832a99c537b
blob - 325073050fc88a4c277d9fab2d2a72433701503f
blob + ae0f38629814f9c9648b74a1a15ef0a6500e7e9e
--- include/got_error.h
+++ include/got_error.h
@@ -26,6 +26,8 @@
 #define GOT_ERR_DECOMPRESSION	0x0008
 #define GOT_ERR_NO_SPACE	0x0009
 #define GOT_ERR_BAD_OBJ_HDR	0x0010
+#define GOT_ERR_OBJ_TYPE	0x0011
+#define GOT_ERR_BAD_OBJ_DATA	0x0012
 
 static const struct got_error {
 	int code;
@@ -42,6 +44,8 @@ static const struct got_error {
 	{ GOT_ERR_DECOMPRESSION,"decompression failed" },
 	{ GOT_ERR_NO_SPACE,	"buffer too small" },
 	{ GOT_ERR_BAD_OBJ_HDR,	"bad object header" },
+	{ GOT_ERR_OBJ_TYPE,	"wrong type of object" },
+	{ GOT_ERR_BAD_OBJ_DATA,	"bad object data" },
 };
 
 const struct got_error * got_error(int code);
blob - e0280c719c9da46b5e6b4d3497f5dbf157b7fc49
blob + dda5b9eff685434839544f6b14a1e25a990e7142
--- include/got_object.h
+++ include/got_object.h
@@ -18,14 +18,44 @@ struct got_object_id {
 	u_int8_t sha1[SHA1_DIGEST_LENGTH];
 };
 
+struct got_blob_object {
+	char *dummy;
+};
+
+struct got_tree_object {
+	char *dummy;
+};
+
+struct got_parent_id {
+	SIMPLEQ_ENTRY(got_parent_id) entry;
+	struct got_object_id id;
+};
+
+SIMPLEQ_HEAD(got_parent_id_list, got_parent_id);
+
+struct got_commit_object {
+	struct got_object_id tree_id;
+	unsigned int nparents;
+	SIMPLEQ_HEAD(, got_parent_id) parent_ids;
+	char *author;
+	char *committer;
+	char *logmsg;
+};
+
 struct got_object {
 	int type;
 #define GOT_OBJ_TYPE_COMMIT 	1
 #define GOT_OBJ_TYPE_TREE	2
 #define GOT_OBJ_TYPE_BLOB	3
 
+	size_t hdrlen;
 	size_t size;
 	struct got_object_id id;
+	union {
+		struct got_blob_object blob;
+		struct got_tree_object tree;
+		struct got_commit_object commit;
+	} obj;
 };
 
 struct got_repository;
@@ -34,3 +64,7 @@ const char * got_object_id_str(struct got_object_id *,
 const struct got_error *got_object_open(struct got_object **,
     struct got_repository *, struct got_object_id *);
 void got_object_close(struct got_object *);
+const struct got_error *got_object_commit_open(struct got_commit_object **,
+    struct got_repository *, struct got_object *);
+void got_object_commit_close(struct got_commit_object *);
+
blob - /dev/null
blob + 668013da54ca7a913d9378e2413398b9dd4a0185 (mode 644)
--- /dev/null
+++ include/got_sha1.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+int got_parse_sha1_digest(uint8_t *, const char *);
blob - 254d635660b60064ce399812b785835832117b72
blob + f24b83d937d8f42d22bbce3b29d61394aca28678
--- lib/object.c
+++ lib/object.c
@@ -14,6 +14,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <sys/queue.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -25,6 +27,7 @@
 #include "got_error.h"
 #include "got_object.h"
 #include "got_repository.h"
+#include "got_sha1.h"
 
 #ifndef MIN
 #define	MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
@@ -38,6 +41,11 @@
 #define GOT_OBJ_TAG_TREE	"tree"
 #define GOT_OBJ_TAG_BLOB	"blob"
 
+#define GOT_COMMIT_TAG_TREE		"tree "
+#define GOT_COMMIT_TAG_PARENT		"parent "
+#define GOT_COMMIT_TAG_AUTHOR		"author "
+#define GOT_COMMIT_TAG_COMMITTER	"committer "
+
 const char *
 got_object_id_str(struct got_object_id *id, char *buf, size_t size)
 {
@@ -148,7 +156,7 @@ inflate_read(struct got_zstream_buf *zb, FILE *f, size
 }
 
 static const struct got_error *
-parse_obj_header(struct got_object **obj, char *buf, size_t len)
+parse_object_header(struct got_object **obj, char *buf, size_t len)
 {
 	const char *obj_tags[] = {
 		GOT_OBJ_TAG_COMMIT,
@@ -161,13 +169,15 @@ parse_obj_header(struct got_object **obj, char *buf, s
 		GOT_OBJ_TYPE_BLOB,
 	};
 	int type = 0;
-	size_t size = 0;
+	size_t size = 0, hdrlen = 0;
 	int i;
 	char *p = strchr(buf, '\0');
 
 	if (p == NULL)
 		return got_error(GOT_ERR_BAD_OBJ_HDR);
 
+	hdrlen = strlen(buf) + 1 /* '\0' */;
+
 	for (i = 0; i < nitems(obj_tags); i++) {
 		const char *tag = obj_tags[i];
 		size_t tlen = strlen(tag);
@@ -190,6 +200,7 @@ parse_obj_header(struct got_object **obj, char *buf, s
 
 	*obj = calloc(1, sizeof(**obj));
 	(*obj)->type = type;
+	(*obj)->hdrlen = hdrlen;
 	(*obj)->size = size;
 	return NULL;
 }
@@ -201,7 +212,6 @@ read_object_header(struct got_object **obj, struct got
 	const struct got_error *err;
 	FILE *f;
 	struct got_zstream_buf zb;
-	char *p;
 	size_t outlen;
 	int i, ret;
 
@@ -219,40 +229,50 @@ read_object_header(struct got_object **obj, struct got
 	if (err)
 		goto done;
 
-	err = parse_obj_header(obj, zb.outbuf, outlen);
+	err = parse_object_header(obj, zb.outbuf, outlen);
 done:
 	inflate_end(&zb);
 	fclose(f);
 	return err;
 }
 
-const struct got_error *
-got_object_open(struct got_object **obj, struct got_repository *repo,
-    struct got_object_id *id)
+static const struct got_error *
+object_path(char **path, struct got_object_id *id,
+    struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
-	char *path_objects = got_repo_get_path_objects(repo);
 	char hex[SHA1_DIGEST_STRING_LENGTH];
-	char *path = NULL;
+	char *path_objects = got_repo_get_path_objects(repo);
 
 	if (path_objects == NULL)
 		return got_error(GOT_ERR_NO_MEM);
 
 	got_object_id_str(id, hex, sizeof(hex));
 
-	if (asprintf(&path, "%s/%.2x/%s",
-	    path_objects, id->sha1[0], hex + 2) == -1) {
+	if (asprintf(path, "%s/%.2x/%s", path_objects,
+	    id->sha1[0], hex + 2) == -1)
 		err = got_error(GOT_ERR_NO_MEM);
-		goto done;
-	}
 
+	free(path_objects);
+	return err;
+}
+
+const struct got_error *
+got_object_open(struct got_object **obj, struct got_repository *repo,
+    struct got_object_id *id)
+{
+	const struct got_error *err = NULL;
+	char *path = NULL;
+
+	err = object_path(&path, id, repo);
+	if (err)
+		return err;
+
 	err = read_object_header(obj, repo, path);
 	if (err == NULL)
 		memcpy((*obj)->id.sha1, id->sha1, SHA1_DIGEST_LENGTH);
-
 done:
 	free(path);
-	free(path_objects);
 	return err;
 }
 
@@ -260,4 +280,215 @@ void
 got_object_close(struct got_object *obj)
 {
 	free(obj);
+}
+
+static int
+commit_object_valid(struct got_commit_object *commit)
+{
+	int i;
+	int n;
+
+	if (commit == NULL)
+		return 0;
+
+	n = 0;
+	for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
+		if (commit->tree_id.sha1[i] == 0)
+			n++;
+	}
+	if (n == SHA1_DIGEST_LENGTH)
+		return 0;
+
+	return 1;
 }
+
+static const struct got_error *
+parse_commit_object(struct got_commit_object **commit, char *buf, size_t len)
+{
+	const struct got_error *err = NULL;
+	char *s = buf;
+	size_t tlen;
+	ssize_t remain = (ssize_t)len;
+ 
+	*commit = calloc(1, sizeof(**commit));
+	if (*commit == NULL)
+		return got_error(GOT_ERR_NO_MEM);
+
+	SIMPLEQ_INIT(&(*commit)->parent_ids);
+
+	tlen = strlen(GOT_COMMIT_TAG_TREE);
+	if (strncmp(s, GOT_COMMIT_TAG_TREE, tlen) == 0) {
+		remain -= tlen;
+		if (remain < SHA1_DIGEST_STRING_LENGTH) {
+			err = got_error(GOT_ERR_BAD_OBJ_DATA);
+			goto done;
+		}
+		s += tlen;
+		if (!got_parse_sha1_digest((*commit)->tree_id.sha1, s)) {
+			err = got_error(GOT_ERR_BAD_OBJ_DATA);
+			goto done;
+		}
+		remain -= SHA1_DIGEST_STRING_LENGTH;
+		s += SHA1_DIGEST_STRING_LENGTH;
+	} else {
+		err = got_error(GOT_ERR_BAD_OBJ_DATA);
+		goto done;
+	}
+
+	tlen = strlen(GOT_COMMIT_TAG_PARENT);
+	while (strncmp(s, GOT_COMMIT_TAG_PARENT, tlen) == 0) {
+		struct got_parent_id *pid;
+
+		remain -= tlen;
+		if (remain < SHA1_DIGEST_STRING_LENGTH) {
+			err = got_error(GOT_ERR_BAD_OBJ_DATA);
+			goto done;
+		}	
+
+		pid = calloc(1, sizeof(*pid));
+		if (pid == NULL) {
+			err = got_error(GOT_ERR_NO_MEM);
+			goto done;
+		}
+		s += tlen;
+		if (!got_parse_sha1_digest(pid->id.sha1, s)) {
+			err = got_error(GOT_ERR_BAD_OBJ_DATA);
+			goto done;
+		}
+		SIMPLEQ_INSERT_TAIL(&(*commit)->parent_ids, pid, entry);
+		(*commit)->nparents++;
+
+		s += SHA1_DIGEST_STRING_LENGTH;
+	}
+
+	tlen = strlen(GOT_COMMIT_TAG_AUTHOR);
+	if (strncmp(s, GOT_COMMIT_TAG_AUTHOR, tlen) == 0) {
+		char *p;
+
+		remain -= tlen;
+		if (remain <= 0) {
+			err = got_error(GOT_ERR_BAD_OBJ_DATA);
+			goto done;
+		}
+		s += tlen;
+		p = strchr(s, '\n');
+		if (p == NULL) {
+			err = got_error(GOT_ERR_BAD_OBJ_DATA);
+			goto done;
+		}
+		*p = '\0';
+		(*commit)->author = strdup(s);
+		if ((*commit)->author == NULL) {
+			err = got_error(GOT_ERR_NO_MEM);
+			goto done;
+		}
+		s += strlen((*commit)->author) + 1;
+	}
+
+	tlen = strlen(GOT_COMMIT_TAG_COMMITTER);
+	if (strncmp(s, GOT_COMMIT_TAG_COMMITTER, tlen) == 0) {
+		char *p;
+
+		remain -= tlen;
+		if (remain <= 0) {
+			err = got_error(GOT_ERR_BAD_OBJ_DATA);
+			goto done;
+		}
+		s += tlen;
+		p = strchr(s, '\n');
+		if (p == NULL) {
+			err = got_error(GOT_ERR_BAD_OBJ_DATA);
+			goto done;
+		}
+		*p = '\0';
+		(*commit)->committer = strdup(s);
+		if ((*commit)->committer == NULL) {
+			err = got_error(GOT_ERR_NO_MEM);
+			goto done;
+		}
+		s += strlen((*commit)->committer) + 1;
+	}
+
+	(*commit)->logmsg = strdup(s);
+done:
+	if (err)
+		got_object_commit_close(*commit);
+	return err;
+}
+
+static const struct got_error *
+read_commit_object(struct got_commit_object **commit,
+    struct got_repository *repo, struct got_object *obj, const char *path)
+{
+	const struct got_error *err = NULL;
+	FILE *f;
+	struct got_zstream_buf zb;
+	size_t len;
+	char *p;
+	int i, ret;
+
+	f = fopen(path, "rb");
+	if (f == NULL)
+		return got_error(GOT_ERR_BAD_PATH);
+
+	err = inflate_init(&zb, 8192);
+	if (err) {
+		fclose(f);
+		return err;
+	}
+
+	do {
+		err = inflate_read(&zb, f, &len);
+		if (err || len == 0)
+			break;
+	} while (len < obj->hdrlen + obj->size);
+
+	if (len < obj->hdrlen + obj->size) {
+		err = got_error(GOT_ERR_BAD_OBJ_DATA);
+		goto done;
+	}
+
+	/* Skip object header. */
+	len -= obj->hdrlen;
+	err = parse_commit_object(commit, zb.outbuf + obj->hdrlen, len);
+done:
+	inflate_end(&zb);
+	fclose(f);
+	return err;
+}
+
+const struct got_error *
+got_object_commit_open(struct got_commit_object **commit,
+    struct got_repository *repo, struct got_object *obj)
+{
+	const struct got_error *err = NULL;
+	char *path = NULL;
+
+	if (obj->type != GOT_OBJ_TYPE_COMMIT)
+		return got_error(GOT_ERR_OBJ_TYPE);
+
+	err = object_path(&path, &obj->id, repo);
+	if (err)
+		return err;
+
+	err = read_commit_object(commit, repo, obj, path);
+	free(path);
+	return err;
+}
+
+void
+got_object_commit_close(struct got_commit_object *commit)
+{
+	struct got_parent_id *pid;
+
+	while (!SIMPLEQ_EMPTY(&commit->parent_ids)) {
+		pid = SIMPLEQ_FIRST(&commit->parent_ids);
+		SIMPLEQ_REMOVE_HEAD(&commit->parent_ids, entry);
+		free(pid);
+	}
+
+	free(commit->author);
+	free(commit->committer);
+	free(commit->logmsg);
+	free(commit);
+}
blob - 19e8815ba5c3b4d7548b9705f9b9beba009b6cb5
blob + b299fd1c82f09e8d7eb1da05f0288b581998f571
--- lib/refs.c
+++ lib/refs.c
@@ -15,18 +15,19 @@
  */
 
 #include <sys/types.h>
+#include <sys/queue.h>
+
 #include <sha1.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <util.h>
-#include <limits.h>
-#include <errno.h>
 
 #include "got_error.h"
 #include "got_object.h"
 #include "got_repository.h"
 #include "got_refs.h"
+#include "got_sha1.h"
 
 #include "path.h"
 
@@ -56,45 +57,6 @@ parse_symref(struct got_reference **ref, const char *n
 	symref->name = symref_name;
 	symref->ref = symref_ref;
 	return NULL;
-}
-
-static int
-parse_xdigit(uint8_t *val, const char *hex)
-{
-	char *ep;
-	long lval;
-
-	errno = 0;
-	lval = strtol(hex, &ep, 16);
-	if (hex[0] == '\0' || *ep != '\0')
-		return 0;
-	if (errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN))
-		return 0;
-
-	*val = (uint8_t)lval;
-	return 1;
-}
-
-static int
-parse_sha1_digest(uint8_t *digest, const char *line)
-{
-	uint8_t b = 0;
-	char hex[3] = {'\0', '\0', '\0'};
-	int i, j;
-
-	for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
-		if (line[0] == '\0' || line[1] == '\0')
-			return 0;
-		for (j = 0; j < 2; j++) {
-			hex[j] = *line;
-			line++;
-		}
-		if (!parse_xdigit(&b, hex))
-			return 0;
-		digest[i] = b;
-	}
-
-	return 1;
 }
 
 static const struct got_error *
@@ -112,7 +74,7 @@ parse_ref_line(struct got_reference **ref, const char 
 	if (ref_name == NULL)
 		return got_error(GOT_ERR_NO_MEM);
 
-	if (!parse_sha1_digest(digest, line))
+	if (!got_parse_sha1_digest(digest, line))
 		return got_error(GOT_ERR_NOT_REF);
 
 	*ref = calloc(1, sizeof(**ref));
blob - /dev/null
blob + e6058f894daa451ecd640fb5d2a0e836c8237a19 (mode 644)
--- /dev/null
+++ lib/sha1.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sha1.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+
+static int
+parse_xdigit(uint8_t *val, const char *hex)
+{
+	char *ep;
+	long lval;
+
+	errno = 0;
+	lval = strtol(hex, &ep, 16);
+	if (hex[0] == '\0' || *ep != '\0')
+		return 0;
+	if (errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN))
+		return 0;
+
+	*val = (uint8_t)lval;
+	return 1;
+}
+
+int
+got_parse_sha1_digest(uint8_t *digest, const char *line)
+{
+	uint8_t b = 0;
+	char hex[3] = {'\0', '\0', '\0'};
+	int i, j;
+
+	for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
+		if (line[0] == '\0' || line[1] == '\0')
+			return 0;
+		for (j = 0; j < 2; j++) {
+			hex[j] = *line;
+			line++;
+		}
+		if (!parse_xdigit(&b, hex))
+			return 0;
+		digest[i] = b;
+	}
+
+	return 1;
+}
blob - 091f43b0a03fb2b5ddc634dc5e8fcd36ccd7dff6
blob + de7eb21b21c7823a753261aadf7cba35c9580fbf
--- regress/repository/Makefile
+++ regress/repository/Makefile
@@ -1,7 +1,7 @@
 .PATH:${.CURDIR}/../../lib
 
 PROG = repository_test
-SRCS = path.c repository.c error.c refs.c object.c repository_test.c
+SRCS = path.c repository.c error.c refs.c object.c sha1.c repository_test.c
 
 CPPFLAGS = -I${.CURDIR}/../../include
 LDADD = -lutil -lz
blob - aeb8b920a4ed73c542162adf523aa4110f9cfd8e
blob + 45451895935d5938eef7e4317784c4e8e90671bd
--- regress/repository/repository_test.c
+++ regress/repository/repository_test.c
@@ -14,6 +14,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <sys/queue.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <sha1.h>
@@ -53,6 +55,24 @@ repo_read_object_header(const char *repo_path)
 	if (err != NULL || obj == NULL)
 		return 0;
 	printf("object type=%d size=%lu\n", obj->type, obj->size);
+	if (obj->type == GOT_OBJ_TYPE_COMMIT) {
+		struct got_commit_object *commit;
+		struct got_parent_id *pid;
+
+		err = got_object_commit_open(&commit, repo, obj);
+		if (err != NULL || commit == NULL)
+			return 0;
+		printf("tree: %s\n",
+		    got_object_id_str(&commit->tree_id, buf, sizeof(buf)));
+		printf("parent%s: ", (commit->nparents == 1) ? "" : "s");
+		SIMPLEQ_FOREACH(pid, &commit->parent_ids, entry) {
+			printf("%s\n",
+			    got_object_id_str(&pid->id, buf, sizeof(buf)));
+		}
+		printf("author: %s\n", commit->author);
+		printf("committer: %s\n", commit->committer);
+		printf("log: %s\n", commit->logmsg);
+	}
 	got_object_close(obj);
 	free(id);
 	got_ref_close(head_ref);