Commit Diff


commit - /dev/null
commit + 4027f31acc6a39eaa8e8448ccfa73b47af4963cb
blob - /dev/null
blob + 7552f1fdd3a8be1fd325b7bb4c4bdce4f0cc187f (mode 644)
--- /dev/null
+++ include/got_error.h
@@ -0,0 +1,22 @@
+
+/* Error codes */
+#define GOT_ERR_UNKNOWN		0x0000
+#define GOT_ERR_NO_MEM		0x0001
+#define GOT_ERR_NOT_GIT_REPO	0x0002
+#define GOT_ERR_NOT_ABSPATH	0x0003
+#define GOT_ERR_BAD_PATH	0x0004
+#define GOT_ERR_NOT_REF		0x0005
+
+static const struct got_error {
+	int code;
+	const char *msg;
+} got_errors[] = {
+	{ GOT_ERR_UNKNOWN,	"unknown error" },
+	{ GOT_ERR_NO_MEM,	"out of memory" },
+	{ GOT_ERR_NOT_GIT_REPO, "no git repository found" },
+	{ GOT_ERR_NOT_ABSPATH,	"absolute path expected" },
+	{ GOT_ERR_BAD_PATH,	"bad path" },
+	{ GOT_ERR_NOT_REF,	"no such reference found" },
+};
+
+const struct got_error * got_error(int code);
blob - /dev/null
blob + 59cd078e3c9458f5ba59d6223390427f725f2a5e (mode 644)
--- /dev/null
+++ include/got_object.h
@@ -0,0 +1,3 @@
+struct got_object {
+	u_int8_t sha1[SHA1_DIGEST_LENGTH];
+};
blob - /dev/null
blob + 76a4e277e96f9698f3c6e3b8a772e1e78efc1a1b (mode 644)
--- /dev/null
+++ include/got_path.h
@@ -0,0 +1,16 @@
+/* Utilities for dealing with filesystem paths. */
+
+/* Determine whether a path is an absolute path. */
+int got_path_is_absolute(const char *);
+
+/*
+ * Return an absolute version of a relative path.
+ * The result is allocated with malloc(3).
+ */
+char *got_path_get_absolute(const char *);
+
+/* 
+ * Normalize a path for internal processing.
+ * The result is allocated with malloc(3).
+ */
+char *got_path_normalize(const char *);
blob - /dev/null
blob + 65aea1ffc7c8d110311b895edb682a68537150d6 (mode 644)
--- /dev/null
+++ include/got_refs.h
@@ -0,0 +1,33 @@
+/* A symbolic reference. */
+struct got_symref {
+	char *name;
+	char *ref;
+};
+
+/* A non-symbolic reference (there is no better designation). */
+struct got_ref {
+	char *name;
+	u_int8_t sha1[SHA1_DIGEST_LENGTH];
+};
+
+/* A reference which points to an arbitrary object. */
+struct got_reference {
+	unsigned int flags;
+#define GOT_REF_IS_SYMBOLIC	0x01
+
+	union {
+		struct got_ref ref;
+		struct got_symref symref;
+	} ref;
+};
+
+/* Well-known reference names. */
+#define GOT_REF_HEAD		"HEAD"
+#define GOT_REF_ORIG_HEAD	"ORIG_HEAD"
+#define GOT_REF_MERGE_HEAD	"MERGE_HEAD"
+
+const struct got_error *
+got_ref_open(struct got_reference **, const char *, const char *);
+
+void got_ref_close(struct got_reference *);
+
blob - /dev/null
blob + d0ccca9367d39e6c2ab979c91943b0364f10750e (mode 644)
--- /dev/null
+++ include/got_repository.h
@@ -0,0 +1,14 @@
+struct got_repository {
+	char *path;
+};
+
+/* Open and close git repositories. */
+const struct got_error *got_repo_open(struct got_repository**, const char *);
+void got_repo_close(struct got_repository*);
+
+/* Get the absolute path to the top-level directory of a repository. */
+const char *got_repo_get_path(struct got_repository *);
+
+/* Get a reference, by name, from a repository. */
+const struct got_error *got_repo_get_reference(struct got_reference **,
+    struct got_repository *, const char *);
blob - /dev/null
blob + 05fcf6abb4b3d0b4656ddf8eaf2fc7aeb0a9f9f3 (mode 644)
--- /dev/null
+++ lib/error.c
@@ -0,0 +1,16 @@
+#include "got_error.h"
+
+#define nitems(a) (sizeof(a) / sizeof((a)[0]))
+
+const struct got_error *
+got_error(int code)
+{
+	int i;
+
+	for (i = 0; i < nitems(got_errors); i++) {
+		if (code == got_errors[i].code)
+			return &got_errors[i];
+	}
+
+	return &got_errors[GOT_ERR_UNKNOWN];
+}
blob - /dev/null
blob + b9a6a54e07dee9e7d7b0b7887fa585d3d20ec0f3 (mode 644)
--- /dev/null
+++ lib/path.c
@@ -0,0 +1,59 @@
+/* #include <sys/syslimits.h> */
+
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+int
+got_path_is_absolute(const char *path)
+{
+	return path[0] == '/';
+}
+
+char *
+got_path_get_absolute(const char *relpath) 
+{
+	char cwd[PATH_MAX];
+	char *abspath;
+
+	if (getcwd(cwd, sizeof(cwd)) == NULL)
+		return NULL;
+
+	if (asprintf(&abspath, "%s/%s/", cwd, relpath) == -1)
+		return NULL;
+
+	return abspath;
+}
+
+char *
+got_path_normalize(const char *path)
+{
+	char *resolved;
+
+	resolved = realpath(path, NULL);
+	if (resolved == NULL)
+		return NULL;
+	
+	if (!got_path_is_absolute(resolved)) {
+		char *abspath = got_path_get_absolute(resolved);
+		free(resolved);
+		resolved = abspath;
+	}
+
+	return resolved;
+}
+
+int
+got_path_is_normalized(const char *path)
+{
+	char *normpath;
+	int ret;
+
+	normpath = got_path_normalize(path);
+	ret = (strcmp(normpath, path) == 0);
+	free(normpath);
+
+	return ret;
+}
blob - /dev/null
blob + 0bd5a78c58bad63fed9c620dde8bc386c274a9a4 (mode 644)
--- /dev/null
+++ lib/refs.c
@@ -0,0 +1,143 @@
+#include <sys/types.h>
+#include <sha1.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <util.h>
+
+#include "got_error.h"
+#include "got_path.h"
+#include "got_refs.h"
+
+static const struct got_error *
+parse_symref(struct got_reference **ref, const char *name, const char *line)
+{
+	struct got_symref *symref;
+	char *symref_name;
+	char *symref_ref;
+
+	if (line[0] == '\0')
+		return got_error(GOT_ERR_NOT_REF);
+
+	symref_name = strdup(name);
+	if (symref_name == NULL)
+		return got_error(GOT_ERR_NO_MEM);
+	symref_ref = strdup(line);
+	if (symref_ref == NULL) {
+		free(symref_name);
+		return got_error(GOT_ERR_NO_MEM);
+	}
+
+	*ref = calloc(1, sizeof(**ref));
+	(*ref)->flags |= GOT_REF_IS_SYMBOLIC;
+	symref = &((*ref)->ref.symref);
+	symref->name = symref_name;
+	symref->ref = symref_ref;
+	return NULL;
+}
+
+static int
+parse_sha1_digest(uint8_t *digest, const char *line)
+{
+	uint8_t b;
+	int i, n;
+
+	for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
+		n = sscanf(line, "%hhx", &b);
+		if (n == 1)
+			digest[i] = b;
+		else
+			return 0;
+	}
+
+	return 1;
+}
+
+static const struct got_error *
+parse_ref_line(struct got_reference **ref, const char *name, const char *line)
+{
+	uint8_t digest[SHA1_DIGEST_LENGTH];
+	char *ref_name;
+
+	if (strncmp(line, "ref: ", 5) == 0) {
+		line += 5;
+		return parse_symref(ref, name, line);
+	}
+
+	ref_name = strdup(name);
+	if (ref_name == NULL)
+		return got_error(GOT_ERR_NO_MEM);
+
+	if (!parse_sha1_digest(digest, line))
+		return got_error(GOT_ERR_NOT_REF);
+
+	*ref = calloc(1, sizeof(**ref));
+	(*ref)->ref.ref.name = ref_name;
+	memcpy(&(*ref)->ref.ref.sha1, digest, SHA1_DIGEST_LENGTH);
+	return NULL;
+}
+
+static const struct got_error *
+parse_ref_file(struct got_reference **ref, const char *name,
+    const char *abspath)
+{
+	const struct got_error *err = NULL;
+	FILE *f = fopen(abspath, "rb");
+	char *line;
+	size_t len;
+	const char delim[3] = {'\0', '\0', '\0'};
+
+	if (f == NULL)
+		return got_error(GOT_ERR_NOT_REF);
+
+	line = fparseln(f, &len, NULL, delim, 0);
+	if (line == NULL) {
+		err = got_error(GOT_ERR_NOT_REF);
+		goto done;
+	}
+
+	err = parse_ref_line(ref, name, line);
+done:
+	free(line);
+	fclose(f);
+	return err;
+}
+
+const struct got_error *
+got_ref_open(struct got_reference **ref, const char *path_refs,
+   const char *refname)
+{
+	const struct got_error *err = NULL;
+	char *path_ref = NULL;
+	char *normpath = NULL;
+	const char *parent_dir;
+	
+	/* XXX For now, this assumes that refs exist in the filesystem. */
+
+	if (asprintf(&path_ref, "%s/%s", path_refs, refname) == -1) {
+		err = got_error(GOT_ERR_NO_MEM);
+		goto done;
+	}
+
+	normpath = got_path_normalize(path_ref);
+	if (normpath == NULL) {
+		err = got_error(GOT_ERR_NOT_REF);
+		goto done;
+	}
+
+	err = parse_ref_file(ref, refname, normpath ? normpath : path_refs);
+done:
+	free(normpath);
+	free(path_ref);
+	return err;
+}
+
+void
+got_ref_close(struct got_reference *ref)
+{
+	if (ref->flags & GOT_REF_IS_SYMBOLIC)
+		free(ref->ref.symref.name);
+	else
+		free(ref->ref.ref.name);
+	free(ref);
+}
blob - /dev/null
blob + 9eac0b7afc0607619c7a922299d4f45394f7af00 (mode 644)
--- /dev/null
+++ lib/repository.c
@@ -0,0 +1,133 @@
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sha1.h>
+#include <string.h>
+
+#include "got_path.h"
+#include "got_error.h"
+#include "got_refs.h"
+#include "got_repository.h"
+
+#define GOT_GIT_DIR	".git"
+
+/* Mandatory files and directories inside the git directory. */
+#define GOT_OBJECTS_DIR	"objects"
+#define GOT_REFS_DIR	"refs"
+#define GOT_HEAD_FILE	"HEAD"
+
+static char *
+get_path_git_dir(struct got_repository *repo)
+{
+	char *path_git;
+	
+	if (asprintf(&path_git, "%s/%s", repo->path, GOT_GIT_DIR) == -1)
+		return NULL;
+
+	return path_git;
+}
+
+static char *
+get_path_git_child(struct got_repository *repo, const char *basename)
+{
+	char *path_child;
+	
+	if (asprintf(&path_child, "%s/%s/%s", repo->path, GOT_GIT_DIR,
+	    basename) == -1)
+		return NULL;
+
+	return path_child;
+}
+
+static char *
+get_path_objects(struct got_repository *repo)
+{
+	return get_path_git_child(repo, GOT_OBJECTS_DIR);
+}
+
+static char *
+get_path_refs(struct got_repository *repo)
+{
+	return get_path_git_child(repo, GOT_REFS_DIR);
+}
+
+static char *
+get_path_head(struct got_repository *repo)
+{
+	return get_path_git_child(repo, GOT_HEAD_FILE);
+}
+
+static int
+is_git_repo(struct got_repository *repo)
+{
+	char *path_git = get_path_git_dir(repo);
+	char *path_objects = get_path_objects(repo);
+	char *path_refs = get_path_refs(repo);
+	char *path_head = get_path_head(repo);
+	int ret;
+
+	ret = (path_git != NULL) && (path_objects != NULL) &&
+	    (path_refs != NULL) && (path_head != NULL);
+
+	free(path_git);
+	free(path_objects);
+	free(path_refs);
+	free(path_head);
+	return ret;
+
+}
+
+const struct got_error *
+got_repo_open(struct got_repository **ret, const char *abspath)
+{
+	struct got_repository *repo;
+
+	if (!got_path_is_absolute(abspath))
+		return got_error(GOT_ERR_NOT_ABSPATH);
+
+	repo = calloc(1, sizeof(*repo));
+	if (repo == NULL)
+		return got_error(GOT_ERR_NO_MEM);
+
+	repo->path = got_path_normalize(abspath);
+	if (repo->path == NULL)
+		return got_error(GOT_ERR_BAD_PATH);
+
+	if (!is_git_repo(repo))
+		return got_error(GOT_ERR_NOT_GIT_REPO);
+		
+	*ret = repo;
+	return NULL;
+}
+
+void
+got_repo_close(struct got_repository *repo)
+{
+	free(repo->path);
+	free(repo);
+}
+
+const char *
+got_repo_get_path(struct got_repository *repo)
+{
+	return repo->path;
+}
+
+const struct got_error *
+got_repo_get_reference(struct got_reference **ref,
+    struct got_repository *repo, const char *refname)
+{
+	const struct got_error *err = NULL;
+	char *path_refs;
+
+	/* Some refs live in the .git directory. */
+	if (strcmp(refname, GOT_REF_HEAD) == 0)
+		path_refs = get_path_git_dir(repo);
+	else
+		path_refs = get_path_refs(repo);
+
+	err = got_ref_open(ref, path_refs, refname);
+	free(path_refs);
+	return err;
+}
blob - /dev/null
blob + 141f5fdc96126c1f4195558560a3c915e3d9b4c3 (mode 644)
--- /dev/null
+++ regress/repository/Makefile
@@ -0,0 +1,11 @@
+.PATH:${.CURDIR}/../../lib
+
+PROG = repository_test
+SRCS = path.c repository.c error.c refs.c repository_test.c
+
+CPPFLAGS = -I${.CURDIR}/../../include
+LDADD = -lutil
+
+NOMAN = yes
+
+.include <bsd.regress.mk>
blob - /dev/null
blob + 9322b20c14d1a32e4f0d63294437ed4b37b677e7 (mode 644)
--- /dev/null
+++ regress/repository/repository_test.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sha1.h>
+
+#include "got_error.h"
+#include "got_path.h"
+#include "got_refs.h"
+#include "got_repository.h"
+
+#define RUN_TEST(expr, name) \
+	if (!(expr)) { printf("test %s failed", (name)); failure = 1; }
+
+#define GOT_REPO_PATH "../../../"
+
+static int
+repo_open_test(const char *repo_path)
+{
+	const struct got_error *err;
+	struct got_repository *repo;
+	const char *abspath;
+	int ret;
+
+	abspath = got_path_normalize(repo_path);
+	err = got_repo_open(&repo, abspath);
+	ret = (err == NULL && repo != NULL);
+	got_repo_close(repo);
+	return ret;
+}
+
+static int
+repo_get_head_ref(const char *repo_path)
+{
+	const struct got_error *err;
+	struct got_repository *repo;
+	struct got_reference *head_ref;
+	const char *abspath;
+	int ret;
+
+	abspath = got_path_normalize(repo_path);
+	err = got_repo_open(&repo, abspath);
+	if (err != NULL || repo == NULL)
+		return 0;
+	err = got_repo_get_reference(&head_ref, repo, GOT_REF_HEAD);
+	if (err != NULL || head_ref == NULL)
+		return 0;
+	got_ref_close(head_ref);
+	got_repo_close(repo);
+	return 1;
+}
+
+int
+main(int argc, const char *argv[])
+{
+	int failure = 0;
+	const char *repo_path;
+
+	if (argc == 1)
+		repo_path = GOT_REPO_PATH;
+	else if (argc == 2)
+		repo_path = argv[1];
+	else {
+		fprintf(stderr, "usage: repository_test [REPO_PATH]\n");
+		return 1;
+	}
+
+	RUN_TEST(repo_open_test(repo_path), "repo_open");
+	RUN_TEST(repo_get_head_ref(repo_path), "get_head_ref");
+
+	return failure ? 1 : 0;
+}