Commit Diff


commit - d71d75ad16058715e286e90bf48ab4f3d76e2127
commit + ab9a70b228c50aa8f3066780a9472f5fe8453d85
blob - b621824f64024b17e410ee7d940177183af49938
blob + 325073050fc88a4c277d9fab2d2a72433701503f
--- include/got_error.h
+++ include/got_error.h
@@ -21,6 +21,11 @@
 #define GOT_ERR_NOT_ABSPATH	0x0003
 #define GOT_ERR_BAD_PATH	0x0004
 #define GOT_ERR_NOT_REF		0x0005
+#define GOT_ERR_IO		0x0006
+#define GOT_ERR_EOF		0x0007
+#define GOT_ERR_DECOMPRESSION	0x0008
+#define GOT_ERR_NO_SPACE	0x0009
+#define GOT_ERR_BAD_OBJ_HDR	0x0010
 
 static const struct got_error {
 	int code;
@@ -32,6 +37,11 @@ static const struct got_error {
 	{ GOT_ERR_NOT_ABSPATH,	"absolute path expected" },
 	{ GOT_ERR_BAD_PATH,	"bad path" },
 	{ GOT_ERR_NOT_REF,	"no such reference found" },
+	{ GOT_ERR_IO,		"input/output error" },
+	{ GOT_ERR_EOF,		"unexpected end of file" },
+	{ GOT_ERR_DECOMPRESSION,"decompression failed" },
+	{ GOT_ERR_NO_SPACE,	"buffer too small" },
+	{ GOT_ERR_BAD_OBJ_HDR,	"bad object header" },
 };
 
 const struct got_error * got_error(int code);
blob - fdfff2e90cf197c3377cf3a2df9b005075469071
blob + e0280c719c9da46b5e6b4d3497f5dbf157b7fc49
--- include/got_object.h
+++ include/got_object.h
@@ -18,4 +18,19 @@ struct got_object_id {
 	u_int8_t sha1[SHA1_DIGEST_LENGTH];
 };
 
+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 size;
+	struct got_object_id id;
+};
+
+struct got_repository;
+
 const char * got_object_id_str(struct got_object_id *, char *, size_t);
+const struct got_error *got_object_open(struct got_object **,
+    struct got_repository *, struct got_object_id *);
+void got_object_close(struct got_object *);
blob - 4e9d9cfc4245c5c928a8abeb20463300b7469393
blob + ff3a5659e40c001e5bdb0b4223aaadf60aca59f0
--- lib/object.c
+++ lib/object.c
@@ -15,10 +15,29 @@
  */
 
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sha1.h>
+#include <zlib.h>
+#include <ctype.h>
+#include <limits.h>
 
+#include "got_error.h"
 #include "got_object.h"
+#include "got_repository.h"
 
+#ifndef MIN
+#define	MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
+#endif
+
+#ifndef nitems
+#define nitems(_a) (sizeof(_a) / sizeof((_a)[0]))
+#endif
+
+#define GOT_OBJ_TAG_COMMIT	"commit"
+#define GOT_OBJ_TAG_TREE	"tree"
+#define GOT_OBJ_TAG_BLOB	"blob"
+
 const char *
 got_object_id_str(struct got_object_id *id, char *buf, size_t size)
 {
@@ -39,3 +58,205 @@ got_object_id_str(struct got_object_id *id, char *buf,
 
 	return buf;
 }
+
+struct got_zstream_buf {
+	z_stream z;
+	char *inbuf;
+	size_t inlen;
+	char *outbuf;
+	size_t outlen;
+	int flags;
+#define GOT_ZSTREAM_F_HAVE_MORE 0x01
+};
+
+static void
+inflate_end(struct got_zstream_buf *zb)
+{
+	free(zb->inbuf);
+	free(zb->outbuf);
+	inflateEnd(&zb->z);
+}
+
+static const struct got_error *
+inflate_init(struct got_zstream_buf *zb, size_t bufsize)
+{
+	const struct got_error *err = NULL;
+
+	memset(zb, 0, sizeof(*zb));
+
+	zb->z.zalloc = Z_NULL;
+	zb->z.zfree = Z_NULL;
+	if (inflateInit(&zb->z) != Z_OK) {
+		err = got_error(GOT_ERR_IO);
+		goto done;
+	}
+
+	zb->inlen = zb->outlen = bufsize;
+
+	zb->inbuf = calloc(1, zb->inlen);
+	if (zb->inbuf == NULL) {
+		err = got_error(GOT_ERR_NO_MEM);
+		goto done;
+	}
+
+	zb->outbuf = calloc(1, zb->outlen);
+	if (zb->outbuf == NULL) {
+		err = got_error(GOT_ERR_NO_MEM);
+		goto done;
+	}
+
+done:
+	if (err)
+		inflate_end(zb);
+	return err;
+}
+
+static const struct got_error *
+inflate_read(struct got_zstream_buf *zb, FILE *f, size_t *outlenp)
+{
+	size_t last_total_out = zb->z.total_out;
+	z_stream *z = &zb->z;
+	int n, ret;
+
+	z->next_out = zb->outbuf;
+	z->avail_out = zb->outlen;
+
+	if (z->avail_in == 0 && (zb->flags & GOT_ZSTREAM_F_HAVE_MORE) == 0) {
+		int i;
+		n = fread(zb->inbuf, 1, zb->inlen, f);
+		if (n == 0) {
+			if (ferror(f))
+				return got_error(GOT_ERR_IO);
+			*outlenp = 0;
+			return NULL;
+		}
+		z->next_in = zb->inbuf;
+		z->avail_in = n;
+	}
+
+	ret = inflate(z, Z_SYNC_FLUSH);
+	if (ret == Z_OK) {
+		if (z->avail_out == 0)
+			zb->flags |= GOT_ZSTREAM_F_HAVE_MORE;
+		else
+			zb->flags &= ~GOT_ZSTREAM_F_HAVE_MORE;
+	} else if (ret != Z_STREAM_END)
+		return got_error(GOT_ERR_DECOMPRESSION);
+
+	*outlenp = z->total_out - last_total_out;
+	return NULL;
+}
+
+static const struct got_error *
+parse_obj_header(struct got_object **obj, char *buf, size_t len)
+{
+	const char *obj_tags[] = {
+		GOT_OBJ_TAG_COMMIT,
+		GOT_OBJ_TAG_TREE,
+		GOT_OBJ_TAG_BLOB
+	};
+	const int obj_types[] = {
+		GOT_OBJ_TYPE_COMMIT,
+		GOT_OBJ_TYPE_TREE,
+		GOT_OBJ_TYPE_BLOB,
+	};
+	int type = 0;
+	size_t size = 0;
+	int i;
+	char *p = strchr(buf, '\0');
+
+	if (p == NULL)
+		return got_error(GOT_ERR_BAD_OBJ_HDR);
+
+	for (i = 0; i < nitems(obj_tags); i++) {
+		const char *tag = obj_tags[i];
+		const char *errstr;
+
+		if (strncmp(buf, tag, strlen(tag)) != 0)
+			continue;
+
+		type = obj_types[i];
+		if (len <= strlen(tag))
+			return got_error(GOT_ERR_BAD_OBJ_HDR);
+		size = strtonum(buf + strlen(tag), 0, LONG_MAX, &errstr);
+		if (errstr != NULL)
+			return got_error(GOT_ERR_BAD_OBJ_HDR);
+		break;
+	}
+
+	if (type == 0)
+		return got_error(GOT_ERR_BAD_OBJ_HDR);
+
+	*obj = calloc(1, sizeof(**obj));
+	(*obj)->type = type;
+	(*obj)->size = size;
+	return NULL;
+}
+
+static const struct got_error *
+read_object_header(struct got_object **obj, struct got_repository *repo,
+    const char *path)
+{
+	const struct got_error *err;
+	FILE *f;
+	struct got_zstream_buf zb;
+	char *p;
+	size_t outlen;
+	int i, ret;
+
+	f = fopen(path, "rb");
+	if (f == NULL)
+		return got_error(GOT_ERR_BAD_PATH);
+
+	err = inflate_init(&zb, 64);
+	if (err) {
+		fclose(f);
+		return err;
+	}
+
+	err = inflate_read(&zb, f, &outlen);
+	if (err)
+		goto done;
+
+	err = parse_obj_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)
+{
+	const struct got_error *err = NULL;
+	char *path_objects = got_repo_get_path_objects(repo);
+	char hex[SHA1_DIGEST_STRING_LENGTH];
+	char *path = NULL;
+
+	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) {
+		err = got_error(GOT_ERR_NO_MEM);
+		goto done;
+	}
+
+	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;
+}
+
+void
+got_object_close(struct got_object *obj)
+{
+	free(obj);
+}
blob - 4a4a51ff51f09e461601809905d6eb6f3ec619a0
blob + 091f43b0a03fb2b5ddc634dc5e8fcd36ccd7dff6
--- regress/repository/Makefile
+++ regress/repository/Makefile
@@ -4,7 +4,7 @@ PROG = repository_test
 SRCS = path.c repository.c error.c refs.c object.c repository_test.c
 
 CPPFLAGS = -I${.CURDIR}/../../include
-LDADD = -lutil
+LDADD = -lutil -lz
 
 NOMAN = yes
 
blob - eb48aeca9c322748e88fc2cc83d9ad58b86c98d3
blob + aeb8b920a4ed73c542162adf523aa4110f9cfd8e
--- regress/repository/repository_test.c
+++ regress/repository/repository_test.c
@@ -29,12 +29,13 @@
 #define GOT_REPO_PATH "../../../"
 
 static int
-repo_resolve_head_ref(const char *repo_path)
+repo_read_object_header(const char *repo_path)
 {
 	const struct got_error *err;
 	struct got_repository *repo;
 	struct got_reference *head_ref;
 	struct got_object_id *id;
+	struct got_object *obj;
 	char buf[SHA1_DIGEST_STRING_LENGTH];
 	int ret;
 
@@ -48,6 +49,11 @@ repo_resolve_head_ref(const char *repo_path)
 	if (err != NULL || head_ref == NULL)
 		return 0;
 	printf("HEAD is at %s\n", got_object_id_str(id, buf, sizeof(buf)));
+	err = got_object_open(&obj, repo, id);
+	if (err != NULL || obj == NULL)
+		return 0;
+	printf("object type=%d size=%lu\n", obj->type, obj->size);
+	got_object_close(obj);
 	free(id);
 	got_ref_close(head_ref);
 	got_repo_close(repo);
@@ -69,7 +75,7 @@ main(int argc, const char *argv[])
 		return 1;
 	}
 
-	RUN_TEST(repo_resolve_head_ref(repo_path), "resolve_head_ref");
+	RUN_TEST(repo_read_object_header(repo_path), "read_object_header");
 
 	return failure ? 1 : 0;
 }