commit - 6332351956c375744e908c1f895721ba3a5157d2
commit + d1cda8264f62723250746b16cb897832a99c537b
blob - 325073050fc88a4c277d9fab2d2a72433701503f
blob + ae0f38629814f9c9648b74a1a15ef0a6500e7e9e
--- include/got_error.h
+++ include/got_error.h
#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;
{ 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
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;
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
+/*
+ * 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
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <sys/queue.h>
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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))
#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)
{
}
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,
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);
*obj = calloc(1, sizeof(**obj));
(*obj)->type = type;
+ (*obj)->hdrlen = hdrlen;
(*obj)->size = size;
return NULL;
}
const struct got_error *err;
FILE *f;
struct got_zstream_buf zb;
- char *p;
size_t outlen;
int i, ret;
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;
}
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
*/
#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"
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 *
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
+/*
+ * 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
.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
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <sys/queue.h>
+
#include <stdio.h>
#include <stdlib.h>
#include <sha1.h>
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);