commit 2cccbecd9549222161efe1cd1ad138b8e5a8a218
parent 9ac00e7c623a97610e51860c5a441e2aa5299022
Author: Mohamed Aslan <maslan@sce.carleton.ca>
Date:   Sat, 28 Jun 2014 10:21:52 -0400
add support for deserializing directories
* objdb-fs can now deserialize directories into struct dir
* new baseline command 'ls'
Diffstat:
7 files changed, 197 insertions(+), 11 deletions(-)
diff --git a/Makefile b/Makefile
@@ -2,7 +2,7 @@ PROG=		baseline
 BINDIR=		/usr/bin
 
 SRCS=		baseline.c config.c common.c session.c objects.c helper.c
-SRCS+=		cmd-add.c cmd-branch.c cmd-checkout.c cmd-commit.c cmd-help.c cmd-init.c cmd-log.c cmd-version.c
+SRCS+=		cmd-add.c cmd-branch.c cmd-checkout.c cmd-commit.c cmd-help.c cmd-init.c cmd-ls.c cmd-log.c cmd-version.c
 SRCS+=		objdb-fs.c
 SRCS+=		dircache-simple.c
 
diff --git a/baseline.c b/baseline.c
@@ -51,6 +51,9 @@ main(int argc, char **argv)
 	else if (!strcmp(argv[1], "help")) {
 		cmd_help(argc - 1, argv + 1);
 	}
+	else if (!strcmp(argv[1], "ls")) {
+		cmd_ls(argc - 1, argv + 1);
+	}
 	else if (!strcmp(argv[1], "log")) {
 		cmd_log(argc - 1, argv + 1);
 	}
diff --git a/cmd-log.c b/cmd-log.c
@@ -41,7 +41,7 @@ cmd_log(int argc, char **argv)
 	do {
 		comm = baseline_commit_new();
 		s.db_ops->select_commit(s.db_ctx, head, comm);
-		printf("commit ID: %s\n", comm->id);
+		printf("commit: %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);
diff --git a/cmd-ls.c b/cmd-ls.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca>
+ *
+ * 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 <stdio.h> /* printf(3) */
+#include <stdlib.h> /* EXIT_SUCCESS */
+#include <string.h> /* strdup(3) */
+#include <sys/stat.h> /* S_ISDIR */
+
+#include "cmd.h"
+#include "session.h"
+
+#include "objects.h"
+
+
+int
+cmd_ls(int argc, char **argv)
+{
+	char *head;
+	struct session s;
+	struct commit *comm;
+	struct dir *dir;
+	struct dirent *ent;
+	baseline_session_begin(&s, 0);
+
+	s.db_ops->branch_get_head(s.db_ctx, s.branch, &head);
+	if (head == NULL)
+		goto ret;
+
+	comm = baseline_commit_new();
+	s.db_ops->select_commit(s.db_ctx, head, comm);
+
+	dir = baseline_dir_new();
+	s.db_ops->select_dir(s.db_ctx, comm->dir, dir);
+
+	printf("commit: %s\n", comm->id);
+	ent = dir->children;
+	while (ent != NULL) {
+		if (S_ISDIR(ent->mode))
+			printf("%s/\n", ent->name);
+		else
+			printf("%s\n", ent->name);
+		ent = ent->next;
+	}
+
+	baseline_dir_free(dir);
+
+ret:
+	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_dir(struct objdb_ctx *, const char *, struct dir *);
 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 *);
@@ -56,6 +57,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_dir = objdb_bl_select_dir,
 	.select_commit = objdb_bl_select_commit,
 	.remove = objdb_bl_remove,
 	.branch_create = objdb_bl_branch_create,
@@ -105,18 +107,19 @@ static int
 read_line(int fd, char *buf, size_t len)
 {
 	char ch, *ptr = buf;
-	int n, nbytes = 0;
+	int nbytes = 0;
 
-	while ((n = read(fd, &ch, 1)) > 0) {
+	while (read(fd, &ch, 1) > 0) {
 		if (nbytes++ == len - 1) {
-			printf("line too big\n");
+			/* TODO: use err() instead */
+			fprintf(stderr, "line too big\n");
 			return -1;
 		}
 		*ptr++ = (ch == '\n') ? '\0' : ch;
 		if (ch == '\n')
 			break;
 	}
-	return n;
+	return nbytes > 0 ? nbytes - 1 : 0;
 }
 
 static int
@@ -149,6 +152,20 @@ is_dec(const char *str)
 	return 1;
 }
 
+static int
+is_oct(const char *str)
+{
+	if (str == NULL)
+		return 0;
+	do {
+		if (*str == '\0')
+			break;
+		if (*str >= '0' && *str <= '7')
+			continue;
+		return 0;
+	} while (*str++);
+	return 1;
+}
 
 /*
  * converts struct commit to char*
@@ -193,7 +210,7 @@ commit_deserialize(int fd, struct commit *comm)
 		goto parse_error;
 	/* skip "dir " */
 	p1 = buf + 4;
-	if (!is_hex(p1))
+	if (!is_hex(p1) || strlen(p1) != SHA256_DIGEST_LENGTH * 2)
 		goto parse_error;
 	comm->dir = strdup(p1);
 
@@ -205,7 +222,7 @@ commit_deserialize(int fd, struct commit *comm)
 	/* check if line starts with "parent " */
 	if (strstr(buf, "parent ") == buf) {
 		p1 = buf + 7;
-		if (!is_hex(p1))
+		if (!is_hex(p1) || strlen(p1) != SHA256_DIGEST_LENGTH * 2)
 			goto parse_error;
 		comm->n_parents = 1;
 		comm->parents[0] = strdup(p1);
@@ -332,7 +349,7 @@ dir_serialize(struct dir *dir)
 			ch = 'D';
 		/* assuming asprintf() uses realloc() */
 		/* free(entry); */
-		asprintf(&entry, "%c %s %06o %s\n", ch, it->id, it->mode, it->name);
+		asprintf(&entry, "%06o %s %s\n", it->mode, it->id, it->name);
 		/* allocation failed, need to do something! */
 		if (entry == NULL)
 			return NULL;
@@ -350,6 +367,71 @@ ret:
 	return raw;
 }
 
+static int
+dir_deserialize(int fd, struct dir *d)
+{
+	char buf[512], *id, *name, *p1, *p2;
+	int n;
+	mode_t mode;
+	struct dirent *head = NULL, *tail = NULL, *q = NULL;
+
+	while (1) {
+		if ((n = read_line(fd, buf, sizeof(buf))) == -1)
+			printf("EXIT\n");
+		if (n == 0)
+			break;
+		/* mode + obj id + at least 1 char name */
+		if (n < 9 + SHA256_DIGEST_LENGTH * 2)
+			goto parse_error;
+
+		/* 1. mode */
+		p1 = buf;
+		if ((p2 = strchr(p1, ' ')) == NULL)
+			goto parse_error;
+		*p2 = '\0';
+		if (!is_oct(p1) || strlen(p1) != 6)
+			goto parse_error;
+		/* TODO: find a safer alternative */
+		sscanf(p1, "%6o", &mode);
+
+		/* 2. id */
+		p1 = ++p2;
+		if ((p2 = strchr(p1, ' ')) == NULL)
+			goto parse_error;
+		*p2 = '\0';
+		if (!is_hex(p1) || strlen(p1) != SHA256_DIGEST_LENGTH * 2)
+			goto parse_error;
+		id = p1;
+
+		/* 3. name */
+		p1 = ++p2;
+		if (strlen(p1) == 0)
+			goto parse_error;
+		name = p1;
+
+		q = (struct dirent *)malloc(sizeof(struct dirent));
+		q->mode = mode;
+		q->id = strdup(id);
+		q->name = strdup(name);
+		q->next = NULL;
+		if (head == NULL) {
+			head = q;
+			tail = q;
+		}
+		else {
+			tail->next = q;
+			tail = tail->next;
+		}
+	}
+
+	d->children = head;
+	return EXIT_SUCCESS;
+
+parse_error:
+	fprintf(stderr, "error parsing file, not a valid directory.\n");
+	return EXIT_FAILURE;
+}
+
 static char *
 file_gen_id(struct file *obj)
 {
@@ -825,7 +907,7 @@ objdb_bl_branch_ls(struct objdb_ctx *ctx)
 		return EXIT_FAILURE;
 	while ((entry = fts_read(dir)) != NULL) {
 		if (entry->fts_level == FTS_ROOTLEVEL)
-				continue;
+			continue;
 		if (entry->fts_info & FTS_D) {
 			printf("* %s\n", entry->fts_name);
 		}
@@ -871,7 +953,42 @@ objdb_bl_select_commit(struct objdb_ctx *ctx, const char *objid, struct commit *
 success:
 	retval = EXIT_SUCCESS;
 ret:
-	/* assuming free(NULL) is safe */
+	free(db_dir_name);
+	free(full_path);
+	return retval;
+}
+
+static int
+objdb_bl_select_dir(struct objdb_ctx *ctx, const char *objid, struct dir *d)
+{
+	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, "dirs", 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;
+	}
+
+	d->id = strdup(objid);
+	dir_deserialize(fd, d);
+
+	close(fd);
+
+success:
+	retval = EXIT_SUCCESS;
+ret:
 	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_dir)(struct objdb_ctx *, const char *, struct dir *);
 	int (*select_commit)(struct objdb_ctx *, const char *, struct commit *);
 	/* branch ops */
 	int (*branch_create)(struct objdb_ctx *, const char *);
diff --git a/objects.h b/objects.h
@@ -48,6 +48,7 @@ struct commit {
 struct dirent {
 	char *id;
 	char *name;
+	/* TODO: get rid of type */
 	enum {
 		T_FILE,
 		T_DIR