baseline

yet another open-source distributed versioning control system
Log | Files | Refs

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:
Mcmd-log.c | 21+++++++++++++++++++++
Mobjdb-fs.c | 222+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mobjdb.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 *);