commit 4027f31acc6a39eaa8e8448ccfa73b47af4963cb from: Stefan Sperling date: Sat Nov 04 18:41:54 2017 UTC initial import; current implementation only parses the .git/HEAD ref 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 */ + +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#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 blob - /dev/null blob + 9322b20c14d1a32e4f0d63294437ed4b37b677e7 (mode 644) --- /dev/null +++ regress/repository/repository_test.c @@ -0,0 +1,70 @@ +#include +#include +#include + +#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; +}