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