baseline

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

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:
MMakefile | 2+-
Mbaseline.c | 3+++
Mcmd-log.c | 2+-
Acmd-ls.c | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mobjdb-fs.c | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mobjdb.h | 1+
Mobjects.h | 1+
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