commit - /dev/null
commit + 4027f31acc6a39eaa8e8448ccfa73b47af4963cb
blob - /dev/null
blob + 7552f1fdd3a8be1fd325b7bb4c4bdce4f0cc187f (mode 644)
--- /dev/null
+++ include/got_error.h
+
+/* 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
+struct got_object {
+ u_int8_t sha1[SHA1_DIGEST_LENGTH];
+};
blob - /dev/null
blob + 76a4e277e96f9698f3c6e3b8a772e1e78efc1a1b (mode 644)
--- /dev/null
+++ include/got_path.h
+/* 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
+/* 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
+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
+#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
+/* #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
+#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
+#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
+.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
+#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;
+}