commit 3f8e633759e57131b6727b253c944b8bfc36db54
parent 439cb506b82b3ff36a6855d45642051e7775e698
Author: Mohamed Aslan <maslan@sce.carleton.ca>
Date:   Fri, 27 Jun 2014 04:54:13 -0400
create new command log
currently it only supports a single parent
Diffstat:
| M | cmd-log.c |  |  | 21 | +++++++++++++++++++++ | 
| M | objdb-fs.c |  |  | 222 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| M | objdb.h |  |  | 1 | + | 
3 files changed, 244 insertions(+), 0 deletions(-)
diff --git a/cmd-log.c b/cmd-log.c
@@ -16,18 +16,39 @@
 
 #include <stdio.h> /* printf(3) */
 #include <stdlib.h> /* EXIT_SUCCESS */
+#include <string.h> /* strdup(3) */
+#include <time.h> /* ctime(3) */
 
 #include "cmd.h"
 #include "session.h"
 
 #include "objects.h"
 
+
 int
 cmd_log(int argc, char **argv)
 {
+	char *head;
+	int done = 0;
 	struct session s;
+	struct commit *comm;
 	baseline_session_begin(&s, 0);
 
+	s.db_ops->branch_get_head(s.db_ctx, s.branch, &head);
+
+	do {
+		comm = baseline_commit_new();
+		s.db_ops->select_commit(s.db_ctx, head, comm);
+		printf("commit ID: %s\n", comm->id);
+		printf("author:\n\tname: %s\n\temail: %s\n\ttime: %s", comm->author.name, comm->author.email, ctime(&(comm->author.timestamp)));
+		printf("committer:\n\tname: %s\n\temail: %s\n\ttime: %s", comm->committer.name, comm->committer.email, ctime(&(comm->committer.timestamp)));
+		printf("message:\n%s\n", comm->message);
+		if (comm->n_parents == 0)
+			done = 1;
+		else
+			head = strdup(comm->parents[0]);
+		baseline_commit_free(comm);
+	} while (!done);
 
 	baseline_session_end(&s);
 	return EXIT_SUCCESS;
diff --git a/objdb-fs.c b/objdb-fs.c
@@ -37,6 +37,7 @@ static int objdb_bl_init(struct objdb_ctx *);
 static int objdb_bl_insert_file(struct objdb_ctx *, struct file *);
 static int objdb_bl_insert_dir(struct objdb_ctx *, struct dir *);
 static int objdb_bl_insert_commit(struct objdb_ctx *, struct commit *);
+static int objdb_bl_select_commit(struct objdb_ctx *, const char *, struct commit *);
 static int objdb_bl_remove(struct objdb_ctx *, const char *, const char *);
 static int objdb_bl_branch_create(struct objdb_ctx *, const char *);
 static int objdb_bl_branch_create_from(struct objdb_ctx *, const char *, const char *);
@@ -55,6 +56,7 @@ static const struct objdb_ops baseline_objdb_ops = {
 	.insert_file = objdb_bl_insert_file,
 	.insert_dir = objdb_bl_insert_dir,
 	.insert_commit = objdb_bl_insert_commit,
+	.select_commit = objdb_bl_select_commit,
 	.remove = objdb_bl_remove,
 	.branch_create = objdb_bl_branch_create,
 	.branch_create_from = objdb_bl_branch_create_from,
@@ -99,6 +101,55 @@ get_objdb_dir(struct objdb_ctx *ctx)
 	return db_dir_name;
 }
 
+static int
+read_line(int fd, char *buf, size_t len)
+{
+	char ch, *ptr = buf;
+	int n, nbytes = 0;
+
+	while ((n = read(fd, &ch, 1)) > 0) {
+		if (nbytes++ == len - 1) {
+			printf("line too big\n");
+			return -1;
+		}
+		*ptr++ = (ch == '\n') ? '\0' : ch;
+		if (ch == '\n')
+			break;
+	}
+	return n;
+}
+
+static int
+is_hex(const char *str)
+{
+	if (str == NULL)
+		return 0;
+	do {
+		if (*str == '\0')
+			break;
+		if ((*str >= '0' && *str <= '9') || (*str >= 'a' && *str <= 'f'))
+			continue;
+		return 0;
+	} while (*str++);
+	return 1;
+}
+
+static int
+is_dec(const char *str)
+{
+	if (str == NULL)
+		return 0;
+	do {
+		if (*str == '\0')
+			break;
+		if (*str >= '0' && *str <= '9')
+			continue;
+		return 0;
+	} while (*str++);
+	return 1;
+}
+
+
 /*
  * converts struct commit to char*
  */
@@ -122,6 +173,140 @@ ret:
 	return raw;
 }
 
+static int
+commit_deserialize(int fd, struct commit *comm)
+{
+	char buf[128], *p1, *p2;
+	off_t end, pos;
+	size_t len;
+
+	/* 1. */
+	/* find the commit's dir */
+	if (read_line(fd, buf, sizeof(buf)) == -1)
+		return EXIT_FAILURE;
+	/* "dir " + obj id */
+	if (strlen(buf) != 4 + SHA256_DIGEST_LENGTH * 2)
+		goto parse_error;
+	/* check if line starts with "dir " */
+	if (strstr(buf, "dir ") != buf)
+		goto parse_error;
+	/* skip "dir " */
+	p1 = buf + 4;
+	if (!is_hex(p1))
+		goto parse_error;
+	comm->dir = strdup(p1);
+
+	/* 2. */
+	/* find the commit's parent */
+	/* TODO: support more than parent */
+	if (read_line(fd, buf, sizeof(buf)) == -1)
+		return EXIT_FAILURE;
+	/* check if line starts with "parent " */
+	if (strstr(buf, "parent ") == buf) {
+		p1 = buf + 7;
+		if (!is_hex(p1))
+			goto parse_error;
+		comm->n_parents = 1;
+		comm->parents[0] = strdup(p1);
+
+		/* read the next line */
+		if (read_line(fd, buf, sizeof(buf)) == -1)
+			return EXIT_FAILURE;
+	}
+	else {
+		comm->n_parents = 0;
+	}
+
+	/* 3. */
+	/* find the commit's author */
+	/* check if line starts with "author " */
+	if (strstr(buf, "author ") != buf)
+		goto parse_error;
+	/* skip "author " */
+	p1 = buf + 7;
+	/* find author's name */
+	if ((p2 = strchr(p1, ' ')) == NULL)
+		goto parse_error;
+	*p2 = '\0';
+	comm->author.name = strdup(p1);
+	/* find author's email */
+	p1 = ++p2;
+	if (*p1 != '<')
+		goto parse_error;
+	p1++;
+	if ((p2 = strchr(p1, '>')) == NULL)
+		goto parse_error;
+	*p2 = '\0';
+	comm->author.email = strdup(p1);
+	/* find author's timestamp */
+	p1 = ++p2;
+	if (!is_dec(++p1))
+		goto parse_error;
+	/* OpenBSD time_t is now 64-bit */
+	/* FIXME: other POSIX systems still use 32-bit time_t */
+	comm->author.timestamp = atoll(p1);
+
+	/* 4. */
+	/* find the commit's committer */
+	if (read_line(fd, buf, sizeof(buf)) == -1)
+		return EXIT_FAILURE;
+	/* check if line starts with "committer " */
+	if (strstr(buf, "committer ") != buf)
+		goto parse_error;
+	/* skip "committer " */
+	p1 = buf + 10;
+	/* find committer's name */
+	if ((p2 = strchr(p1, ' ')) == NULL)
+		goto parse_error;
+	*p2 = '\0';
+	comm->committer.name = strdup(p1);
+	/* find committer's email */
+	p1 = ++p2;
+	if (*p1 != '<')
+		goto parse_error;
+	p1++;
+	if ((p2 = strchr(p1, '>')) == NULL)
+		goto parse_error;
+	*p2 = '\0';
+	comm->committer.email = strdup(p1);
+	/* find committer's timestamp */
+	p1 = ++p2;
+	if (!is_dec(++p1))
+		goto parse_error;
+	/* OpenBSD time_t is now 64-bit */
+	/* FIXME: other POSIX systems still use 32-bit time_t */
+	comm->committer.timestamp = atoll(p1);
+
+	/* 5. */
+	/* find the commit's message */
+	pos = lseek(fd, 0, SEEK_CUR);
+	end = lseek(fd, 0, SEEK_END);
+	/* even if off_t is 64-bits, we would never be able to support messages of size more than 32-bits */
+	len = (size_t)(end - pos);
+	lseek(fd, pos, SEEK_SET);
+	/* for now the max. size of a message is 10 MBytes */
+	if (len < 1048576) {
+		comm->message = (char *)malloc(len);
+		if ((read(fd, comm->message, len)) == -1) {
+			fprintf(stderr, "error reading file.\n");
+			return EXIT_FAILURE;
+		}
+	}
+	else {
+		goto bigmsg_error;
+	}
+
+	return EXIT_SUCCESS;
+
+parse_error:
+	fprintf(stderr, "error parsing file, not a valid commit.\n");
+	return EXIT_FAILURE;
+bigmsg_error:
+	fprintf(stderr, "error parsing file, commit message too big.\n");
+	return EXIT_FAILURE;
+}
+
+
 /*
  * converts struct dir to char*
  */
@@ -653,3 +838,40 @@ objdb_bl_branch_ls(struct objdb_ctx *ctx)
 	return EXIT_SUCCESS;
 }
 
+static int
+objdb_bl_select_commit(struct objdb_ctx *ctx, const char *objid, struct commit *comm)
+{
+	char *db_dir_name, *full_path;
+	int fd, retval;
+
+	db_dir_name = get_objdb_dir(ctx);
+	if (db_dir_name == NULL) {
+		retval = EXIT_FAILURE;
+		goto ret;
+	}
+	asprintf(&full_path, "%s/%s/%s", db_dir_name, "commits", objid);
+	/* check if file is already there */
+	if (access(full_path, F_OK) == -1) {
+		retval = EXIT_FAILURE;
+		goto ret;
+	}
+
+	if ((fd = open(full_path, O_RDONLY)) == -1) {
+		retval = EXIT_FAILURE;
+		goto ret;
+	}
+
+	comm->id = strdup(objid);
+	commit_deserialize(fd, comm);
+
+	close(fd);
+
+success:
+	retval = EXIT_SUCCESS;
+ret:
+	/* assuming free(NULL) is safe */
+	free(db_dir_name);
+	free(full_path);
+	return retval;
+}
+
diff --git a/objdb.h b/objdb.h
@@ -38,6 +38,7 @@ struct objdb_ops {
 	int (*insert_file)(struct objdb_ctx *, struct file *);
 	int (*insert_dir)(struct objdb_ctx *, struct dir *);
 	int (*insert_commit)(struct objdb_ctx *, struct commit *);
+	int (*select_commit)(struct objdb_ctx *, const char *, struct commit *);
 	/* branch ops */
 	int (*branch_create)(struct objdb_ctx *, const char *);
 	int (*branch_create_from)(struct objdb_ctx *, const char *, const char *);