commit 058694f8c9d5f6c9dc83956faad195fda3e9d44f
Author: Mohamed Aslan <maslan@sce.carleton.ca>
Date: Sun, 25 May 2014 05:01:41 -0400
initial import
Diffstat:
A | Makefile | | | 16 | ++++++++++++++++ |
A | baseline.c | | | 65 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cmd-add.c | | | 96 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cmd-branch.c | | | 115 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cmd-checkout.c | | | 85 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cmd-commit.c | | | 190 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cmd-help.c | | | 28 | ++++++++++++++++++++++++++++ |
A | cmd-init.c | | | 107 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cmd-log.c | | | 34 | ++++++++++++++++++++++++++++++++++ |
A | cmd-version.c | | | 28 | ++++++++++++++++++++++++++++ |
A | cmd.h | | | 29 | +++++++++++++++++++++++++++++ |
A | common.c | | | 108 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | common.h | | | 24 | ++++++++++++++++++++++++ |
A | config.c | | | 108 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | config.h | | | 24 | ++++++++++++++++++++++++ |
A | defaults.h | | | 24 | ++++++++++++++++++++++++ |
A | dircache-simple.c | | | 417 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | dircache.h | | | 41 | +++++++++++++++++++++++++++++++++++++++++ |
A | objdb-fs.c | | | 481 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | objdb.h | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
20 files changed, 2087 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,16 @@
+PROG= baseline
+BINDIR= /usr/bin
+
+SRCS= baseline.c config.c common.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+= objdb-fs.c
+SRCS+= dircache-simple.c
+
+MAN=
+
+#CFLAGS+= -DDEBUG -g
+CFLAGS+= -g
+COPTS+= -Wall
+
+.include <bsd.prog.mk>
+
diff --git a/baseline.c b/baseline.c
@@ -0,0 +1,65 @@
+/*
+ * 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>
+#include <stdlib.h> /* EXIT_* */
+#include <string.h> /* strcmp(1) */
+#include <err.h> /* err(3) */
+#include "cmd.h"
+
+void usage(void)
+{
+ fprintf(stderr, "usage: baseline <command> [args]\n");
+}
+
+int
+main(int argc, char **argv)
+{
+ /* TODO: use getopt(3) */
+ if (argc < 2) {
+ usage();
+ return EXIT_FAILURE;
+ }
+ if (!strcmp(argv[1], "add")) {
+ cmd_add(argc - 1, argv + 1);
+ }
+ else if (!strcmp(argv[1], "branch")) {
+ cmd_branch(argc - 1, argv + 1);
+ }
+ else if (!strcmp(argv[1], "checkout")) {
+ cmd_checkout(argc - 1, argv + 1);
+ }
+ else if (!strcmp(argv[1], "commit")) {
+ cmd_commit(argc - 1, argv + 1);
+ }
+ else if (!strcmp(argv[1], "init")) {
+ cmd_init(argc - 1, argv + 1);
+ }
+ else if (!strcmp(argv[1], "help")) {
+ cmd_help(argc - 1, argv + 1);
+ }
+ else if (!strcmp(argv[1], "log")) {
+ cmd_log(argc - 1, argv + 1);
+ }
+ else if (!strcmp(argv[1], "version")) {
+ cmd_version(argc - 1, argv + 1);
+ }
+ else {
+ errx(EXIT_FAILURE, "unkown commad: \'%s\'.\nFor list of available commands, type:\n\tbaseline help", argv[1]);
+ }
+ return EXIT_SUCCESS;
+}
+
diff --git a/cmd-add.c b/cmd-add.c
@@ -0,0 +1,96 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h> /* EXIT_FAILURE */
+#include <string.h> /* strstr(3) */
+#include <limits.h> /* PATH_MAX */
+#include <unistd.h> /* getcwd(3) */
+
+#include "objdb.h"
+#include "dircache.h"
+#include "cmd.h"
+
+extern int objdb_baseline_get_ops(struct objdb_ops **);
+extern int dircache_simple_get_ops(struct dircache_ops **);
+static int is_subdir_of(const char *, const char *);
+
+int
+cmd_add(int argc, char **argv)
+{
+ char cwd[PATH_MAX];
+ char *path = argv[1];
+ struct stat s;
+ struct dircache_ops *dircache;
+ struct dircache_ctx *dc_ctx;
+ struct objdb_ops *objdb;
+ struct objdb_ctx *db_ctx;
+ objdb_baseline_get_ops(&objdb);
+ dircache_simple_get_ops(&dircache);
+ if (path == NULL) {
+ fprintf(stderr, "baseline: error, no path specified.\n");
+ return EXIT_FAILURE;
+ }
+ if (objdb->open != NULL)
+ objdb->open(&db_ctx, "db", ".baseline");
+ if (objdb->init != NULL) /* TO BE REMOVED */
+ objdb->init(db_ctx);
+ if (getcwd(cwd, PATH_MAX) == NULL) {
+ fprintf(stderr, "baseline: error, failed to get the current working directory.\n");
+ return EXIT_FAILURE;
+ }
+ if (!is_subdir_of(path, cwd)) {
+ fprintf(stderr, "baseline: error, can not add files from outside the repository.\n");
+ return EXIT_FAILURE;
+ }
+ if (stat(path, &s) == -1) {
+ fprintf(stderr, "baseline: error, \'%s\' does not exist.\n", path);
+ return EXIT_FAILURE;
+ }
+ if (dircache->init(&dc_ctx, db_ctx, objdb) == EXIT_FAILURE) {
+ return EXIT_FAILURE;
+ }
+ if (dircache->insert(dc_ctx, path) == EXIT_FAILURE) {
+ fprintf(stderr, "baseline: error, failed to add \'%s\'.\n", path);
+ return EXIT_FAILURE;
+ }
+ if (objdb->close != NULL)
+ if (objdb->close(db_ctx) == EXIT_FAILURE)
+ return EXIT_FAILURE;
+ return EXIT_SUCCESS;
+}
+
+/*
+ * checks if first is subdir of second
+ */
+int
+is_subdir_of(const char *first, const char *second)
+{
+ char first_abspath[PATH_MAX];
+ if (first == NULL || second == NULL)
+ return 0;
+ if (realpath(first, first_abspath) == NULL)
+ return 0;
+#ifdef DEBUG
+ printf("DEBUG: realpath: %s\n", first_abspath);
+#endif
+ if (strstr(first_abspath, second) == first_abspath)
+ return 1;
+ return 0;
+}
+
diff --git a/cmd-branch.c b/cmd-branch.c
@@ -0,0 +1,115 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h> /* EXIT_FAILURE */
+#include <string.h> /* strstr(3) */
+#include <limits.h> /* PATH_MAX */
+#include <fcntl.h> /* open(2) */
+#include <unistd.h> /* getcwd(3) */
+#include <err.h> /* errx(3) */
+
+#include "defaults.h"
+#include "config.h"
+#include "objdb.h"
+#include "dircache.h"
+#include "cmd.h"
+
+extern int objdb_baseline_get_ops(struct objdb_ops **);
+extern int dircache_simple_get_ops(struct dircache_ops **);
+
+int cmd_branch(int, char **);
+
+/*
+ * creates a new branch.
+ */
+static int
+create(const char *branch_name) {
+ char *baseline_path;
+ int retval;
+ struct objdb_ops *objdb;
+ struct objdb_ctx *db_ctx;
+ objdb_baseline_get_ops(&objdb);
+ /* FIXME: get repository path correctly */
+ asprintf(&baseline_path, "%s", BASELINE_DIR);
+ /* FIXME: check if repository initialized */
+ if (objdb->open != NULL)
+ objdb->open(&db_ctx, "db", baseline_path);
+ if (objdb->init != NULL) /* TO BE REMOVED */
+ objdb->init(db_ctx);
+ retval = objdb->branch_create(db_ctx, branch_name);
+ if (objdb->close != NULL)
+ objdb->close(db_ctx);
+ free(baseline_path);
+ free(objdb);
+ return retval;
+}
+
+static int
+update(const char *branch_name, const char *head_objid)
+{
+
+ return EXIT_SUCCESS;
+}
+
+static int
+ls()
+{
+ char *baseline_path;
+ int retval;
+ struct objdb_ops *objdb;
+ struct objdb_ctx *db_ctx;
+ objdb_baseline_get_ops(&objdb);
+ /* FIXME: get repository path correctly */
+ asprintf(&baseline_path, "%s", BASELINE_DIR);
+ /* FIXME: check if repository initialized */
+ if (objdb->open != NULL)
+ objdb->open(&db_ctx, "db", baseline_path);
+ if (objdb->init != NULL) /* TO BE REMOVED */
+ objdb->init(db_ctx);
+ retval = objdb->branch_ls(db_ctx);
+ if (objdb->close != NULL)
+ objdb->close(db_ctx);
+ free(baseline_path);
+ free(objdb);
+ return retval;
+}
+
+int
+cmd_branch(int argc, char **argv)
+{
+ /*
+ * branch command can be used to:
+ * 1. list all branches
+ * 2. create a new branch
+ */
+
+ if (argc == 1) { /* no args */
+ /* list all branches */
+ ls();
+ }
+ else if (argc == 2) { /* 1 arg */
+ /* create a new branch */
+ if (create(argv[1]) == EXIT_FAILURE)
+ errx(EXIT_FAILURE, "failed to create branch \'%s\'", argv[1]);
+ }
+ else{
+ errx(EXIT_FAILURE, "wrong number of parameters for command \'branch\'");
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/cmd-checkout.c b/cmd-checkout.c
@@ -0,0 +1,85 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h> /* EXIT_FAILURE */
+#include <string.h> /* strstr(3) */
+#include <limits.h> /* PATH_MAX */
+#include <fcntl.h> /* open(2) */
+#include <unistd.h> /* getcwd(3) */
+#include <err.h> /* errx(3) */
+
+#include "defaults.h"
+#include "config.h"
+#include "objdb.h"
+#include "dircache.h"
+#include "cmd.h"
+
+extern int objdb_baseline_get_ops(struct objdb_ops **);
+extern int dircache_simple_get_ops(struct dircache_ops **);
+
+int cmd_branch(int, char **);
+
+/*
+ * creates a new branch.
+ */
+static int
+checkout(const char *branch_name) {
+ char *baseline_path;
+ int exist, retval = EXIT_SUCCESS;
+ struct objdb_ops *objdb;
+ struct objdb_ctx *db_ctx;
+ struct dircache_ops *dircache;
+ struct dircache_ctx *dc_ctx;
+ objdb_baseline_get_ops(&objdb);
+ dircache_simple_get_ops(&dircache);
+ /* FIXME: get repository path correctly */
+ asprintf(&baseline_path, "%s", BASELINE_DIR);
+ /* FIXME: check if repository initialized */
+ if (objdb->open != NULL)
+ objdb->open(&db_ctx, "db", baseline_path);
+ if (objdb->init != NULL) /* TO BE REMOVED */
+ objdb->init(db_ctx);
+ dircache->init(&dc_ctx, db_ctx, objdb);
+ if (objdb->branch_if_exists(db_ctx, branch_name, &exist) == EXIT_FAILURE) {
+ return EXIT_FAILURE;
+ }
+ if (exist)
+ dircache->checkout(dc_ctx, branch_name);
+ else
+ errx(EXIT_FAILURE, "branch \'%s\' does not exist.", branch_name);
+
+ if (objdb->close != NULL)
+ objdb->close(db_ctx);
+ free(baseline_path);
+ free(objdb);
+ return retval;
+}
+
+int
+cmd_checkout(int argc, char **argv)
+{
+ if (argc == 2) { /* 1 arg */
+ if (checkout(argv[1]) == EXIT_FAILURE)
+ errx(EXIT_FAILURE, "failed to checkout branch \'%s\'", argv[1]);
+ }
+ else{
+ errx(EXIT_FAILURE, "wrong number of parameters for command \'checkout\'");
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/cmd-commit.c b/cmd-commit.c
@@ -0,0 +1,190 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h> /* EXIT_FAILURE */
+#include <string.h> /* strstr(3) */
+#include <limits.h> /* PATH_MAX */
+#include <unistd.h> /* getcwd(3) */
+#include <err.h> /* err(3) */
+
+#include "defaults.h"
+#include "config.h"
+#include "objdb.h"
+#include "dircache.h"
+#include "cmd.h"
+
+extern int objdb_baseline_get_ops(struct objdb_ops **);
+extern int dircache_simple_get_ops(struct dircache_ops **);
+
+static const char msg_head[] = {
+ "\n"
+ "# Please enter your commit message.\n"
+ "# Lines starting with \'#\' will be ignored.\n"
+};
+
+int
+gen_temp_msgfile_from_msg(char **filename, const char *msg)
+{
+ char *msg_file = NULL;
+ int msgfd;
+
+ if (msg == NULL)
+ return EXIT_FAILURE;
+ /* FIXME: get baseline dir */
+ asprintf(&msg_file, "%s/tmp.XXXXXX", BASELINE_DIR);
+ if ((msgfd = mkstemp(msg_file)) == -1)
+ return EXIT_FAILURE;
+ if (write(msgfd, msg, strlen(msg)) == -1)
+ return EXIT_FAILURE;
+ if (write(msgfd, "\n", strlen("\n")) == -1)
+ return EXIT_FAILURE;
+ close(msgfd);
+ *filename = msg_file;
+ return EXIT_SUCCESS;
+}
+
+int
+gen_temp_msgfile(char **filename)
+{
+ char *msg_file = NULL;
+ int msgfd;
+ /* FIXME: get baseline dir */
+ asprintf(&msg_file, "%s/tmp.XXXXXX", BASELINE_DIR);
+ if ((msgfd = mkstemp(msg_file)) == -1)
+ return EXIT_FAILURE;
+ if (write(msgfd, msg_head, sizeof(msg_head) - 1) == -1)
+ return EXIT_FAILURE;
+ close(msgfd);
+ *filename = msg_file;
+ return EXIT_SUCCESS;
+}
+int
+process_msgfile(char **filename)
+{
+ char *proc_file = NULL;
+ char *line = NULL;
+ int pfd;
+ size_t size = 0;
+ FILE *fp;
+ /* FIXME: get baseline dir */
+ asprintf(&proc_file, "%s/tmp.XXXXXX", BASELINE_DIR);
+ if ((pfd = mkstemp(proc_file)) == -1)
+ return EXIT_FAILURE;
+ if((fp = fopen(*filename, "r")) == NULL) {
+ unlink(proc_file);
+ goto failure;
+ }
+ while (getline(&line, &size, fp) != -1) {
+ if(line[0] == '#')
+ continue;
+ if (write(pfd, line, strlen(line)) == -1)
+ goto failure;
+ }
+ close(pfd);
+ fclose(fp);
+ unlink(*filename);
+ free(*filename);
+ *filename = proc_file;
+ return EXIT_SUCCESS;
+failure:
+ free(proc_file);
+ *filename = NULL;
+ return EXIT_FAILURE;
+}
+
+int
+cmd_commit(int argc, char **argv)
+{
+ char *commit_msg, *msg_file;
+ char cwd[PATH_MAX];
+ char *exec;
+ const char *editor;
+ int ch;
+ int flag_msg = 0, flag_error = 0;
+ struct dircache_ops *dircache;
+ struct dircache_ctx *dc_ctx;
+ struct objdb_ops *objdb;
+ struct objdb_ctx *db_ctx;
+
+ /* load configurations */
+ if (baseline_config_load(BASELINE_DIR "/config") == EXIT_FAILURE)
+ errx(EXIT_FAILURE, "failed to load configurations");
+ /* parse command line options */
+ while ((ch = getopt(argc, argv, "m:")) != -1) {
+ switch (ch) {
+ case 'm':
+ flag_msg = 1;
+ asprintf(&commit_msg, "%s", optarg);
+ break;
+ default:
+ flag_error = 1;
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (flag_error) {
+ return EXIT_FAILURE;
+ }
+ if (flag_msg) {
+ /* TODO: should be per-stage file */
+ if (gen_temp_msgfile_from_msg(&msg_file, commit_msg) == EXIT_FAILURE)
+ errx(EXIT_FAILURE, "failed to generate commit message");
+ free(commit_msg);
+ }
+ else {
+ if (gen_temp_msgfile(&msg_file) == EXIT_FAILURE)
+ errx(EXIT_FAILURE, "failed to generate commit message");
+ if((editor = baseline_config_get_val("editor")) != NULL && strcmp(editor, "")) {
+ asprintf(&exec, "%s %s", editor, msg_file);
+ system(exec);
+ free(exec);
+ }
+ else if ((editor = getenv("EDITOR")) != NULL) {
+ asprintf(&exec, "%s %s", editor, msg_file);
+ system(exec);
+ free(exec);
+ }
+ else {
+ errx(EXIT_FAILURE, "error, commit message not specified.");
+ }
+ }
+ if (process_msgfile(&msg_file) == EXIT_FAILURE)
+ errx(EXIT_FAILURE, "failed to process commit message");
+
+ objdb_baseline_get_ops(&objdb);
+ dircache_simple_get_ops(&dircache);
+ if (objdb->open != NULL)
+ objdb->open(&db_ctx, "db", ".baseline");
+ if (objdb->init != NULL) /* TO BE REMOVED */
+ objdb->init(db_ctx);
+ if (getcwd(cwd, PATH_MAX) == NULL) {
+ fprintf(stderr, "baseline: error, failed to get the current working directory.\n");
+ return EXIT_FAILURE;
+ }
+ dircache->init(&dc_ctx, db_ctx, objdb);
+ if (dircache->commit(dc_ctx, msg_file) == EXIT_FAILURE)
+ fprintf(stderr, "baseline: error, failed to commit your changes.\n");
+ if (objdb->close != NULL) objdb->close(db_ctx);
+ unlink(msg_file);
+ free(msg_file);
+ return EXIT_SUCCESS;
+}
+
diff --git a/cmd-help.c b/cmd-help.c
@@ -0,0 +1,28 @@
+/*
+ * 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 "cmd.h"
+
+int
+cmd_help(int argc, char **argv)
+{
+ printf("help\n");
+ return EXIT_SUCCESS;
+}
+
diff --git a/cmd-init.c b/cmd-init.c
@@ -0,0 +1,107 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h> /* EXIT_FAILURE */
+#include <string.h> /* strstr(3) */
+#include <limits.h> /* PATH_MAX */
+#include <fcntl.h> /* open(2) */
+#include <unistd.h> /* getcwd(3) */
+#include <err.h> /* errx(3) */
+
+#include "defaults.h"
+#include "config.h"
+#include "objdb.h"
+#include "dircache.h"
+#include "common.h"
+#include "cmd.h"
+
+extern int objdb_baseline_get_ops(struct objdb_ops **);
+extern int dircache_simple_get_ops(struct dircache_ops **);
+
+int cmd_init(int, char **);
+
+/*
+ * creates a new repository in the given path.
+ */
+static int
+init(const char *path) {
+ char *baseline_path, *config_path;
+ /* int cfd; */
+ struct dircache_ops *dircache;
+ struct dircache_ctx *dc_ctx;
+ struct objdb_ops *objdb;
+ struct objdb_ctx *db_ctx;
+
+ objdb_baseline_get_ops(&objdb);
+ dircache_simple_get_ops(&dircache);
+ asprintf(&baseline_path, "%s/%s", path, BASELINE_DIR);
+ /* create '.baseline' dir */
+ if (mkdir(baseline_path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) {
+ return EXIT_FAILURE;
+ }
+ /* create '.baseline/config file */
+ asprintf(&config_path, "%s/config", baseline_path);
+/*
+ if ((cfd = open(config_path, O_CREAT | O_TRUNC | O_WRONLY)) == -1) {
+ return EXIT_FAILURE;
+ }
+ close(cfd);
+*/
+ if (baseline_config_create(config_path) == EXIT_FAILURE)
+ return EXIT_FAILURE;
+ /* FIXME: error checks */
+ /* let objdb initialize it's structures too */
+ if (objdb->open != NULL)
+ objdb->open(&db_ctx, "db", baseline_path);
+ if (objdb->init != NULL) /* TO BE REMOVED */
+ objdb->init(db_ctx);
+ /* as well as dircache */
+ if (dircache->init != NULL)
+ dircache->init(&dc_ctx, db_ctx, objdb);
+ if (objdb->close != NULL)
+ objdb->close(db_ctx);
+ free(config_path);
+ free(baseline_path);
+ free(objdb);
+ free(dircache);
+ return EXIT_SUCCESS;
+}
+
+int
+cmd_init(int argc, char **argv)
+{
+ char cwd[PATH_MAX+1];
+ char *baseline_dir = NULL;
+ const char *repo;
+
+ repo = baseline_repo_get_rootdir();
+ if (repo != NULL)
+ errx(EXIT_SUCCESS, "repository already initialized.");
+ if (getcwd(cwd, sizeof(cwd)) == NULL)
+ errx(EXIT_FAILURE, "error, can not read current directory.");
+ asprintf(&baseline_dir, "%s/%s", cwd, BASELINE_DIR);
+ if (exists(baseline_dir))
+ errx(EXIT_FAILURE, "error, corrupted repository already exists.");
+ free(baseline_dir);
+ if (init(cwd) == EXIT_FAILURE)
+ errx(EXIT_FAILURE, "error, could not initialize repository.");
+ printf("baseline: repository successfully initialized at \'%s\'.\n", cwd);
+ return EXIT_SUCCESS;
+}
+
diff --git a/cmd-log.c b/cmd-log.c
@@ -0,0 +1,34 @@
+/*
+ * 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 "cmd.h"
+#include "common.h"
+
+int
+cmd_log(int argc, char **argv)
+{
+ const char *ptr = baseline_repo_get_rootdir();
+ const char *ptr2 = baseline_repo_get_rootdir();
+ if (ptr != NULL)
+ printf("%s\n", ptr);
+ if (ptr2 != NULL)
+ printf("%s\n", ptr2);
+ return EXIT_SUCCESS;
+}
+
diff --git a/cmd-version.c b/cmd-version.c
@@ -0,0 +1,28 @@
+/*
+ * 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 "cmd.h"
+
+int
+cmd_version(int argc, char **argv)
+{
+ printf("baseline version 0.0.1\n");
+ return EXIT_SUCCESS;
+}
+
diff --git a/cmd.h b/cmd.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef _CMD_H_
+#define _CMD_H_
+
+int cmd_add(int, char **);
+int cmd_branch(int, char **);
+int cmd_checkout(int, char **);
+int cmd_commit(int, char **);
+int cmd_help(int, char **);
+int cmd_init(int, char **);
+int cmd_log(int, char **);
+int cmd_version(int, char **);
+
+#endif
diff --git a/common.c b/common.c
@@ -0,0 +1,108 @@
+/*
+ * 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>
+#include <stdlib.h>
+
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+
+#include "defaults.h"
+
+/* cache the repository root dir for future calls of baseline_repo_get_rootdir() */
+static char *repo_rootdir = NULL;
+
+int
+exists(const char *f)
+{
+ struct stat s;
+
+ if(stat(f, &s) == -1)
+ return 0;
+ return 1;
+}
+
+int
+dir_exists(const char *dir)
+{
+ struct stat s;
+
+ if (stat(dir, &s) == -1)
+ return 0;
+ if (!S_ISDIR(s.st_mode))
+ return 0;
+ return 1;
+}
+
+static int
+baseline_validate_repo(const char *path)
+{
+ char *ptr = NULL;
+ int valid;
+
+ asprintf(&ptr, "%s/%s", path, BASELINE_DIR);
+ if (ptr == NULL)
+ return 0;
+ valid = dir_exists(ptr);
+ /* TODO: use access & check for modes as well */
+ /* TODO: ask objdb, and may be dircache too */
+ free(ptr);
+ return valid;
+}
+
+/*
+ * may not be so safe
+ * do not free the returned pointer, it will be cached for later calls
+ * try not to use chdir() as possible
+ */
+const char *
+baseline_repo_get_rootdir()
+{
+ char cwd[PATH_MAX+1];
+ int i;
+ size_t len;
+
+ /* check if the repository root dir already cached */
+ if (repo_rootdir != NULL)
+ return repo_rootdir;
+ if (getcwd(cwd, sizeof(cwd)) == NULL)
+ return NULL;
+ len = strlen(cwd);
+ for (i=len-1 ; i>=0 ; i--) {
+ if (cwd[i] == '/') {
+ if (baseline_validate_repo(cwd))
+ break;
+ cwd[i] = '\0';
+ }
+ }
+ if (i == -1) {
+ if (baseline_validate_repo(cwd))
+ asprintf(&repo_rootdir, "/");
+ else
+ return NULL;
+ }
+ else {
+ asprintf(&repo_rootdir, "%s", cwd);
+ }
+#ifdef DEBUG
+ printf("repository root dir: %s\n", repo_rootdir);
+#endif
+ return repo_rootdir;
+}
+
diff --git a/common.h b/common.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+int exists(const char *);
+int dir_exists(const char *);
+const char* baseline_repo_get_rootdir();
+
+#endif
diff --git a/config.c b/config.c
@@ -0,0 +1,108 @@
+/*
+ * 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> /* strchr(3), strcmp(3) */
+
+#include "config.h"
+
+#define N_OPTIONS 3
+
+static const char config_sample[] =
+"#\n"
+"# baseline configuration file\n"
+"# edit as you wish, but be cautious!\n"
+"#\n"
+"\n"
+"username = Anonymous\n"
+"useremail = anon@localhost.localdomain\n"
+"\n";
+
+struct option {
+ char *key;
+ char val[32];
+};
+
+static struct option options[] = {
+ {.key = "username", .val = ""},
+ {.key = "useremail", .val = ""},
+ {.key = "editor", .val = ""}
+};
+
+int
+baseline_config_create(const char *path)
+{
+ FILE *fp;
+ if (path == NULL)
+ return EXIT_FAILURE;
+ if ((fp = fopen(path, "w")) == NULL)
+ return EXIT_FAILURE;
+ fwrite(config_sample, sizeof(char), sizeof(config_sample) - 1, fp);
+ fclose(fp);
+ return EXIT_SUCCESS;
+}
+
+int
+baseline_config_load(const char *path)
+{
+ char *line = NULL; /* must be initialized to NULL or getline()'s realloc() might complain */
+ char *ptr, *tmp;
+ char key[32], val[32];
+ int i, idx = 0;
+ size_t size = 0;
+ ssize_t len;
+ FILE *fp;
+ if (path == NULL)
+ return EXIT_FAILURE;
+ if ((fp = fopen(path, "r")) == NULL)
+ return EXIT_FAILURE;
+ while ((len = getline(&line, &size, fp)) != -1) {
+ /* skip blank lines */
+ if (line[0] == '\n')
+ continue;
+ i = 0;
+ /* skip white spaces */
+ while(line[i] == ' ' || line[i] == '\t')
+ i++;
+ ptr = &line[i];
+ /* skip comments */
+ if (ptr[0] == '#' || ptr[0] == ';')
+ continue;
+ if ((tmp = strchr(ptr, '=')) == NULL)
+ return EXIT_FAILURE;
+ *tmp = ' ';
+ sscanf(ptr, "%s%s", key, val);
+ if (!strcmp(options[idx].key, key)) {
+ strlcpy(options[idx].val, val, sizeof(options[idx].val));
+ idx++;
+ if (idx > N_OPTIONS)
+ break;
+ }
+ }
+ fclose(fp);
+ return EXIT_SUCCESS;
+}
+
+const char *
+baseline_config_get_val(const char *key) {
+ int i;
+ for (i=0 ; i<N_OPTIONS ; i++)
+ if(!strcmp(options[i].key, key))
+ return options[i].val;
+ return NULL;
+}
+
diff --git a/config.h b/config.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+#ifndef CONFIG_H_
+#define CONFIG_H_
+
+int baseline_config_create(const char *);
+int baseline_config_load(const char *);
+const char* baseline_config_get_val(const char *);
+
+#endif
diff --git a/defaults.h b/defaults.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+#ifndef DEFAULTS_H_
+#define DEFAULTS_H_
+
+#define BASELINE_DIR ".baseline"
+#define BASELINE_DB "db"
+#define DEFAULT_BRANCH "master"
+
+#endif
diff --git a/dircache-simple.c b/dircache-simple.c
@@ -0,0 +1,417 @@
+/*
+ * 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 <sys/stat.h> /* stat(3) */
+
+#include <stdio.h>
+#include <stdlib.h> /* malloc(2) */
+#include <string.h> /* str*(2), mem*(2) */
+#include <unistd.h> /* close(2), rmdir(2), unlink(2) */
+
+#include <fts.h> /* fts_*(3) */
+
+#include "defaults.h"
+#include "config.h"
+#include "objdb.h"
+#include "dircache.h"
+
+int dircache_simple_get_ops(struct dircache_ops **);
+static int simple_init(struct dircache_ctx **, struct objdb_ctx *, struct objdb_ops *);
+static int simple_insert(struct dircache_ctx *, const char *);
+static int simple_checkout(struct dircache_ctx *, const char *);
+static int simple_commit(struct dircache_ctx *, const char *);
+
+static struct dircache_ops simple_ops = {
+ .name = "simple",
+ .version = "1.0",
+ .init = simple_init,
+ .insert = simple_insert,
+ .remove = NULL,
+ .commit = simple_commit,
+ .checkout = simple_checkout,
+ .fsck = NULL,
+ .compress = NULL,
+ .dedup = NULL
+};
+
+int
+dircache_simple_get_ops(struct dircache_ops **ops)
+{
+ if (ops == NULL)
+ return EXIT_FAILURE;
+ *ops = (struct dircache_ops *)malloc(sizeof(struct dircache_ops));
+ memcpy(*ops, &simple_ops, sizeof(struct dircache_ops));
+ return EXIT_SUCCESS;
+}
+
+static int
+simple_init(struct dircache_ctx **dc_ctx, struct objdb_ctx *db_ctx, struct objdb_ops *db_ops)
+{
+ char *branch_name = NULL, *branch_path;
+ char *dc_path;
+ int exist, retval;
+ size_t size = 0;
+ struct stat s;
+ FILE *branch_fp;
+ if (dc_ctx == NULL || db_ctx == NULL)
+ return EXIT_FAILURE;
+ /* FIXME: use get baseline path 'common' function */
+ asprintf(&dc_path, "%s/%s", ".baseline", "dircache");
+ asprintf(&branch_path, "%s/branch", ".baseline");
+ /* check if '.baseline/dircache' dir exists */
+ if (stat(dc_path, &s) == -1) {
+ /* create '.baseline/dircache' dir */
+ if (mkdir(dc_path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) {
+ return EXIT_FAILURE;
+ }
+ }
+ /* check if '.baseline/branch' file exists */
+ if(stat(branch_path, &s) == 0) {
+ if ((branch_fp = fopen(branch_path, "r")) == NULL) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+ if (getline(&branch_name, &size, branch_fp) == -1) {
+ retval = EXIT_FAILURE;
+ free(branch_name);
+ goto ret;
+ }
+ fclose(branch_fp);
+ }
+ else {
+ asprintf(&branch_name, "%s", DEFAULT_BRANCH);
+ if (db_ops->branch_if_exists(db_ctx, branch_name, &exist) == EXIT_FAILURE) {
+ retval = EXIT_FAILURE;
+ free(branch_name);
+ goto ret;
+ }
+ if (!exist)
+ db_ops->branch_create(db_ctx, branch_name);
+ /* create '.baseline/branch' file */
+ if ((branch_fp = fopen(branch_path, "w")) == NULL) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+ fprintf(branch_fp, branch_name);
+ fclose(branch_fp);
+ }
+ *dc_ctx = (struct dircache_ctx *)malloc(sizeof(struct dircache_ctx));
+ (*dc_ctx)->db_ctx = db_ctx;
+ (*dc_ctx)->db_ops = db_ops;
+ret:
+ free(dc_path);
+ free(branch_path);
+ free(branch_name);
+ return EXIT_SUCCESS;
+}
+
+static int
+simple_insert(struct dircache_ctx *dc_ctx, const char *path)
+{
+ char *objid, *paths[2], *cache_path;
+ struct stat s, fs;
+ FILE *fp;
+ FTS *dir;
+ FTSENT *entry;
+ if (stat(path, &s) == -1)
+ return EXIT_FAILURE;
+ if (S_ISDIR(s.st_mode)) {
+ /* dirs are cached for now, and should be inserted at commit time */
+ paths[0] = (char *)path;
+ paths[1] = NULL;
+ if ((dir = fts_open(paths, FTS_NOCHDIR, 0)) == NULL)
+ return EXIT_FAILURE;
+ while ((entry = fts_read(dir)) != NULL) {
+ /* skip directories starting with '.', other than our top-level directory */
+ if (entry->fts_name[0] == '.' && entry->fts_level != FTS_ROOTLEVEL) {
+ fts_set(dir, entry, FTS_SKIP);
+ continue;
+ }
+ if (entry->fts_info & FTS_D) {
+#ifdef DEBUG
+ printf("DS: %s\n", entry->fts_path);
+#endif
+ /* FIXME: path check is required */
+ asprintf(&cache_path, ".baseline/dircache/%s", entry->fts_path);
+ if (mkdir(cache_path, S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
+ free(cache_path);
+ return EXIT_FAILURE;
+ }
+#ifdef DEBUG
+ printf("[DEBUG] dircache: created dir %s\n", cache_path);
+#endif
+ free(cache_path);
+ }
+ if(entry->fts_info & FTS_F) {
+#ifdef DEBUG
+ printf("F: %s\n", entry->fts_path);
+#endif
+ if (stat(entry->fts_path, &fs) == -1)
+ return EXIT_FAILURE;
+ if (dc_ctx->db_ops->insert_from_file(dc_ctx->db_ctx, T_FILE, entry->fts_path, &objid) == EXIT_FAILURE)
+ return EXIT_FAILURE;
+#ifdef DEBUG
+ printf("\t ID = %s\n", objid);
+#endif
+ /* FIXME: path check is required */
+ asprintf(&cache_path, ".baseline/dircache/%s", entry->fts_path);
+ if ((fp = fopen(cache_path, "w")) == NULL) {
+ free(cache_path);
+ return EXIT_FAILURE;
+ }
+#ifdef DEBUG
+ printf("[DEBUG] dircache: created file %s\n", cache_path);
+#endif
+ fprintf(fp, "F %s %o\n", objid, fs.st_mode);
+ fclose(fp);
+ free(cache_path);
+ }
+ }
+ fts_close(dir);
+ }
+ else {
+ /* it's a file, why the hell would we wait, insert it immediately */
+ return dc_ctx->db_ops->insert_from_file(dc_ctx->db_ctx, T_FILE, path, &objid);
+ }
+ return EXIT_SUCCESS;
+}
+
+static int
+simple_gen_dindex(char **didx)
+{
+ char *dircache_path, *didx_path = NULL;
+ char *paths[2];
+ int didx_fd;
+ struct stat s;
+ FILE *didx_fp;
+ FTS *ftsp;
+ FTSENT *entry;
+ /* FIXME: root dir */
+ asprintf(&dircache_path, ".baseline/dircache");
+ if (stat(dircache_path, &s) == -1)
+ return EXIT_FAILURE;
+ if (!S_ISDIR(s.st_mode))
+ return EXIT_FAILURE;
+ /* create dir-only index */
+ asprintf(&didx_path, ".baseline/dindex.XXXXXXX");
+ if ((didx_fd = mkstemp(didx_path)) == -1) {
+ return EXIT_FAILURE;
+ }
+ if ((didx_fp = fdopen(didx_fd, "w")) == NULL) {
+ unlink(didx_path);
+ close(didx_fd);
+ return EXIT_FAILURE;
+ }
+ paths[0] = (char *)dircache_path;
+ paths[1] = NULL;
+ if ((ftsp = fts_open(paths, FTS_NOCHDIR, 0)) == NULL)
+ return EXIT_FAILURE;
+ while ((entry = fts_read(ftsp)) != NULL) {
+ /* skip directories starting with '.', other than our top-level directory */
+ if (entry->fts_name[0] == '.' && entry->fts_level != FTS_ROOTLEVEL) {
+ fts_set(ftsp, entry, FTS_SKIP);
+ continue;
+ }
+ if (entry->fts_info & FTS_DP) {
+ /* FIXME: spaces! */
+ fprintf(didx_fp, "%s\n", entry->fts_path);
+ }
+ }
+ fts_close(ftsp);
+ fclose(didx_fp);
+ free(dircache_path);
+ *didx = didx_path;
+ return EXIT_SUCCESS;
+}
+
+static int
+simple_gen_commit(const char *dir_id, const char *msg_file, char **commit_file)
+{
+ char *dc_path = ".baseline/dircache"; /* FIXME */
+ char *cmt_path;
+ char *msg = NULL;
+ const char *user_name, *user_email;
+ int cfd;
+ size_t size = 0;
+ struct stat s;
+ FILE *cfp, *mfp;
+
+ user_name = baseline_config_get_val("username");
+ user_email = baseline_config_get_val("useremail");
+ if (user_name == NULL || user_email == NULL)
+ return EXIT_FAILURE;
+ if (msg_file == NULL)
+ return EXIT_FAILURE;
+ if (stat(dc_path, &s) == -1)
+ return EXIT_FAILURE;
+ if (!S_ISDIR(s.st_mode))
+ return EXIT_FAILURE;
+ /* commit message */
+ if ((mfp = fopen(msg_file, "r")) == NULL)
+ return EXIT_FAILURE;
+ /* create commit */
+ asprintf(&cmt_path, "%s/commit.XXXXXXX", dc_path);
+ if ((cfd = mkstemp(cmt_path)) == -1) {
+ return EXIT_FAILURE;
+ }
+ if ((cfp = fdopen(cfd, "w")) == NULL) {
+ unlink(cmt_path);
+ close(cfd);
+ return EXIT_FAILURE;
+ }
+ fprintf(cfp, "dir %s\n", dir_id);
+ fprintf(cfp, "author %s <%s>\n", user_name, user_email);
+ fprintf(cfp, "committer %s <%s>\n", user_name, user_email);
+ while (getline(&msg, &size, mfp) != -1) {
+ fprintf(cfp, "%s", msg);
+ }
+ fclose(cfp);
+ fclose(mfp);
+ *commit_file = cmt_path;
+ return EXIT_SUCCESS;
+}
+
+static int
+simple_get_current_branch(char **branch_name)
+{
+ size_t size = 0;
+ FILE *branch_fp;
+ /* FIXME: */
+ *branch_name = NULL;
+ if ((branch_fp = fopen(".baseline/branch", "r")) == NULL) {
+ return EXIT_FAILURE;
+ }
+ if (getline(branch_name, &size, branch_fp) == -1) {
+ free(*branch_name);
+ return EXIT_FAILURE;
+ }
+ fclose(branch_fp);
+ return EXIT_SUCCESS;
+}
+
+static int
+simple_commit(struct dircache_ctx *dc_ctx, const char *msgfile)
+{
+ char *branch;
+ char *objid, *path, *paths[2], tmp_objid[1024];
+ char *dircache_path, *didx_path;
+ struct objdb_dirhandle *handle;
+ struct stat s;
+ FILE *didx_fp, *fp;
+ FTS *dir;
+ FTSENT *entry;
+ unsigned int mode;
+ size_t size;
+ ssize_t len;
+ char type;
+
+ /* FIXME: root dir */
+ asprintf(&dircache_path, ".baseline/dircache");
+ if (stat(dircache_path, &s) == -1)
+ return EXIT_FAILURE;
+ if (!S_ISDIR(s.st_mode))
+ return EXIT_FAILURE;
+ /* FIXME: empty dirache directory */
+ simple_gen_dindex(&didx_path);
+ if ((didx_fp = fopen(didx_path, "r")) == NULL)
+ return EXIT_FAILURE;
+ size = 0;
+ path = NULL; /* if not set to NULL, realloc() will get pissed. And, it can waste your day! */
+ while ((len = getline(&path, &size, didx_fp)) != -1) {
+ if (path[len-1] == '\n') {
+ path[len-1] = 0;
+ --len;
+ }
+ printf("DIR: %s\n", path);
+ dc_ctx->db_ops->dir_begin(dc_ctx->db_ctx, &handle);
+ paths[0] = (char *)path;
+ paths[1] = NULL;
+ if ((dir = fts_open(paths, FTS_NOCHDIR, 0)) == NULL)
+ return EXIT_FAILURE;
+ while ((entry = fts_read(dir)) != NULL) {
+ if (entry->fts_name[0] == '.' && entry->fts_level != FTS_ROOTLEVEL) {
+ fts_set(dir, entry, FTS_SKIP);
+ continue;
+ }
+ if (entry->fts_info & FTS_F) {
+ printf("\t FILE: %s\n", entry->fts_path);
+ if ((fp = fopen(entry->fts_path, "r")) == NULL)
+ return EXIT_FAILURE;
+ fscanf(fp, "%c %s %o", &type, tmp_objid, &mode);
+ if (type == 'F')
+ dc_ctx->db_ops->dir_insert_object(dc_ctx->db_ctx, handle, tmp_objid, entry->fts_name, T_FILE, mode);
+ else if (type == 'D')
+ dc_ctx->db_ops->dir_insert_object(dc_ctx->db_ctx, handle, tmp_objid, entry->fts_name, T_DIR, mode);
+ fclose(fp);
+ unlink(entry->fts_path);
+ }
+ else {
+ if (entry->fts_level >= 1) {
+ fts_set(dir, entry, FTS_SKIP);
+ }
+ }
+ }
+ fts_close(dir);
+ dc_ctx->db_ops->dir_end(dc_ctx->db_ctx, handle, &objid);
+#ifdef DEBUG
+ printf("dir \'%s\' commited with ID = %s\n", path, objid);
+#endif
+ if (stat(path, &s) == -1)
+ return EXIT_FAILURE;
+ /* FIXME: do not remove '.baseline/dircache' dir */
+ if (rmdir(path) == -1)
+ return EXIT_FAILURE;
+ if ((fp = fopen(path, "w")) == NULL)
+ return EXIT_FAILURE;
+ fprintf(fp, "%c %s %o", 'D', objid, s.st_mode);
+ fclose(fp);
+ }
+ free(path);
+ fclose(didx_fp);
+ unlink(didx_path);
+ /* temp */
+ unlink(".baseline/dircache");
+ if (mkdir(".baseline/dircache", S_IRUSR | S_IWUSR | S_IXUSR) == -1) {
+ return EXIT_FAILURE;
+ }
+ /* create commit */
+ if (simple_gen_commit(objid, msgfile, &path) == EXIT_FAILURE)
+ return EXIT_FAILURE;
+ if (dc_ctx->db_ops->insert_from_file(dc_ctx->db_ctx, T_COMMIT, path, &objid) == EXIT_FAILURE)
+ return EXIT_FAILURE;
+ printf("COMMIT ID: %s\n", objid);
+ unlink(path);
+ simple_get_current_branch(&branch);
+ dc_ctx->db_ops->branch_set_head(dc_ctx->db_ctx, branch, objid);
+ free(path);
+ free(objid);
+ return EXIT_SUCCESS;
+}
+
+static int
+simple_checkout(struct dircache_ctx *dc_ctx, const char *branch_name)
+{
+ FILE *branch_fp;
+ /* FIXME: */
+ if ((branch_fp = fopen(".baseline/branch", "w")) == NULL) {
+ return EXIT_FAILURE;
+ }
+ fprintf(branch_fp, branch_name);
+ fclose(branch_fp);
+ /* TODO: copy head files into working dir */
+ return EXIT_SUCCESS;
+}
+
diff --git a/dircache.h b/dircache.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef _DIRCACHE_H_
+#define _DIRCAHCE_H_
+
+#include <sys/types.h>
+#include "objdb.h"
+
+struct dircache_ctx {
+ struct objdb_ctx *db_ctx;
+ struct objdb_ops *db_ops;
+};
+
+struct dircache_ops {
+ char *name;
+ char *version;
+ int (*init)(struct dircache_ctx **, struct objdb_ctx *, struct objdb_ops *);
+ int (*insert)(struct dircache_ctx *, const char *);
+ int (*remove)(struct dircache_ctx *, const char *);
+ int (*commit)(struct dircache_ctx *, const char *);
+ int (*checkout)(struct dircache_ctx *, const char *);
+ int (*fsck)(struct dircache_ctx *);
+ int (*compress)(struct dircache_ctx *);
+ int (*dedup)(struct dircache_ctx *);
+};
+
+#endif
diff --git a/objdb-fs.c b/objdb-fs.c
@@ -0,0 +1,481 @@
+/*
+ * 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 <sys/stat.h> /* stat(3) */
+
+#include <stdio.h> /* rename(2) */
+#include <stdlib.h> /* malloc(2) */
+#include <string.h> /* str*(2) , mem*(2) */
+#include <unistd.h> /* access(2) */
+
+#include <sha2.h> /* SHA256*() */
+
+#include <fts.h> /* fts_*(3) */
+
+#include "defaults.h"
+#include "objdb.h"
+
+int objdb_baseline_get_ops(struct objdb_ops **);
+static char * get_objdb_dir(struct objdb_ctx *);
+static int objdb_bl_open(struct objdb_ctx **, const char *, const char *);
+static int objdb_bl_close(struct objdb_ctx *);
+static int objdb_bl_init(struct objdb_ctx *);
+static int objdb_bl_insert(struct objdb_ctx *, enum objdb_type, const u_int8_t *, char **);
+static int objdb_bl_insert_from_file(struct objdb_ctx *, enum objdb_type, const char *, char **);
+static int objdb_bl_remove(struct objdb_ctx *, const char *, const char *);
+static int objdb_bl_dir_begin(struct objdb_ctx *, struct objdb_dirhandle **);
+static int objdb_bl_dir_insert_object(struct objdb_ctx *, struct objdb_dirhandle *, const char *, const char *, enum objdb_type, mode_t);
+static int objdb_bl_dir_end(struct objdb_ctx *, struct objdb_dirhandle *, char **);
+static int objdb_bl_branch_create(struct objdb_ctx *, const char *);
+static int objdb_bl_branch_if_exists(struct objdb_ctx *, const char *, int *);
+static int objdb_bl_branch_set_head(struct objdb_ctx *, const char *, const char *);
+static int objdb_bl_branch_get_head(struct objdb_ctx *, const char *, char **);
+static int objdb_bl_branch_ls(struct objdb_ctx *);
+
+
+static const struct objdb_ops baseline_objdb_ops = {
+ .name = "baseline",
+ .version = "1.0",
+ .init = objdb_bl_init,
+ .open = objdb_bl_open,
+ .close = objdb_bl_close,
+ .insert = objdb_bl_insert,
+ .insert_from_file = objdb_bl_insert_from_file,
+ .remove = objdb_bl_remove,
+ .dir_begin = objdb_bl_dir_begin,
+ .dir_insert_object = objdb_bl_dir_insert_object,
+ .dir_end = objdb_bl_dir_end,
+ .branch_create = objdb_bl_branch_create,
+ .branch_if_exists = objdb_bl_branch_if_exists,
+ .branch_set_head = objdb_bl_branch_set_head,
+ .branch_get_head = objdb_bl_branch_get_head,
+ .branch_ls = objdb_bl_branch_ls,
+ .fsck = NULL,
+ .compress = NULL,
+ .dedup = NULL
+};
+
+#define N_MAINDIRS 5
+static const char *main_dirs[] = {
+ BASELINE_DIR "/" BASELINE_DB "/files",
+ BASELINE_DIR "/" BASELINE_DB "/dirs",
+ BASELINE_DIR "/" BASELINE_DB "/commits",
+ BASELINE_DIR "/" BASELINE_DB "/branches",
+ BASELINE_DIR "/" BASELINE_DB "/tags"
+};
+
+int
+objdb_baseline_get_ops(struct objdb_ops **ops)
+{
+ if (ops == NULL)
+ return EXIT_FAILURE;
+ *ops = (struct objdb_ops *)malloc(sizeof(struct objdb_ops));
+ memcpy(*ops, &baseline_objdb_ops, sizeof(struct objdb_ops));
+ return EXIT_SUCCESS;
+}
+
+/* FIXME */
+static char *
+get_objdb_dir(struct objdb_ctx *ctx)
+{
+ char *db_dir_name = NULL;
+ int len;
+ if (ctx == NULL)
+ return NULL;
+ /* malloc() + strlcpy() + strlcat() = asprintf() */
+ len = asprintf(&db_dir_name, "%s/%s", ctx->db_path, ctx->db_name);
+ return db_dir_name;
+}
+
+static int
+mkdir_ifn_exists(const char *path)
+{
+ struct stat s;
+ /* TODO: check if path is writable */
+ if (stat(path, &s) == 0) {
+ if (S_ISDIR(s.st_mode))
+ return EXIT_SUCCESS;
+ return EXIT_FAILURE;
+ }
+ if (mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR) == 0)
+ return EXIT_SUCCESS;
+ return EXIT_FAILURE;
+}
+
+static int
+objdb_bl_open(struct objdb_ctx **ctx, const char *db_name, const char *db_path)
+{
+ if (ctx == NULL)
+ return EXIT_FAILURE;
+ *ctx = (struct objdb_ctx *)malloc(sizeof(struct objdb_ctx));
+ asprintf(&((*ctx)->db_name), "%s", db_name);
+ asprintf(&((*ctx)->db_path), "%s", db_path);
+ asprintf(&((*ctx)->db_version), "1.0");
+ return EXIT_SUCCESS;
+}
+
+static int
+objdb_bl_close(struct objdb_ctx *ctx)
+{
+ if (ctx == NULL)
+ return EXIT_FAILURE;
+ free(ctx->db_name);
+ free(ctx->db_path);
+ free(ctx->db_version);
+ free(ctx);
+ return EXIT_SUCCESS;
+}
+
+static int
+objdb_bl_init(struct objdb_ctx *ctx)
+{
+ char *db_dir_name;
+ int i, retval;
+ struct stat s;
+ retval = EXIT_FAILURE;
+ db_dir_name = NULL;
+ /* check if path exists */
+ if (stat(ctx->db_path, &s) == -1)
+ goto ret;
+ db_dir_name = get_objdb_dir(ctx);
+ if (mkdir_ifn_exists(db_dir_name) == EXIT_FAILURE)
+ goto ret;
+ /* try to create main sub-dirs as well */
+ for (i=0 ; i<N_MAINDIRS ; i++) {
+ if (mkdir_ifn_exists(main_dirs[i]) == EXIT_FAILURE)
+ goto ret;
+ }
+ret:
+ if(db_dir_name != NULL)
+ free(db_dir_name);
+ return retval;
+}
+
+static int
+objdb_bl_insert(struct objdb_ctx *ctx, enum objdb_type type, const u_int8_t *data, char **obj_id)
+{
+ return EXIT_SUCCESS;
+}
+
+static int
+objdb_bl_insert_from_file(struct objdb_ctx *ctx, enum objdb_type type, const char *file_name, char **obj_id)
+{
+ char *db_dir_name, *full_path, *obj_file_name, *obj_hash, *ptr, *tmp_file_name;
+ int count, i, len, retval, tmpfd;
+ FILE *fp, *tmpfp;
+ SHA2_CTX hash_ctx;
+ struct stat s;
+ u_int8_t buf[4096], digest[SHA256_DIGEST_LENGTH];
+
+ if (file_name == NULL) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+ if ((fp = fopen(file_name, "r")) == NULL) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+ db_dir_name = get_objdb_dir(ctx);
+ if (db_dir_name == NULL) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+ /* check object's type */
+ if (type == T_FILE) {
+ /* malloc() + strlcpy() + strlcat() = asprintf() */
+ len = asprintf(&full_path, "%s/%s", db_dir_name, "files");
+ }
+ else if (type == T_DIR) {
+ len = asprintf(&full_path, "%s/%s", db_dir_name, "dirs");
+ }
+ else if (type == T_COMMIT) {
+ len = asprintf(&full_path, "%s/%s", db_dir_name, "commits");
+ }
+ else {
+ len = asprintf(&full_path, "%s", db_dir_name);
+ }
+
+#ifdef DEBUG
+ printf("[DEBUG] DB: checking for path = %s\n", full_path);
+#endif
+ /* check if path exists */
+ if (stat(full_path, &s) != 0) {
+#ifdef DEBUG
+ printf("\t[DEBUG] DB: creating new dir %s\n", full_path);
+#endif
+ if (mkdir(full_path, S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+ }
+ else {
+ if (!S_ISDIR(s.st_mode)) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+ }
+ /* create a temp file */
+ /* malloc() + strlcpy() + strlcat() = asprintf() */
+ len = asprintf(&tmp_file_name, "%s/tmp.XXXXXX", full_path);
+ if ((tmpfd = mkstemp(tmp_file_name)) == -1) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+ if ((tmpfp = fdopen(tmpfd, "w")) == NULL) {
+ unlink(tmp_file_name);
+ close(tmpfd);
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+ SHA256Init(&hash_ctx);
+ do {
+ count = fread(buf, sizeof(u_int8_t), sizeof(buf), fp);
+ fwrite(buf, sizeof(u_int8_t), count, tmpfp); /* faster for new a file, but slower if the file already exist! */
+ SHA256Update(&hash_ctx, buf, count);
+ } while (!feof(fp));
+ SHA256Final(digest, &hash_ctx);
+ fclose(fp);
+ fclose(tmpfp);
+ /* TODO: think of something safer */
+ obj_hash = (char *)malloc(SHA256_DIGEST_LENGTH * 2 + 1);
+ for (i=0, ptr=obj_hash ; i<SHA256_DIGEST_LENGTH ; i++, ptr+=2) {
+ snprintf(ptr, 3, "%02x", digest[i]);
+ }
+ /* malloc() + strlcpy() + strlcat() = asprintf() */
+ asprintf(&obj_file_name, "%s/%s", full_path, obj_hash);
+#ifdef DEBUG
+ printf("[DEBUG] DB: object file name = %s\n", obj_file_name);
+#endif
+ /* check if file is already there */
+ if (access(obj_file_name, F_OK) != -1) {
+ unlink(tmp_file_name);
+ goto success;
+ }
+ if (rename(tmp_file_name, obj_file_name)) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+
+success:
+ *obj_id = obj_hash;
+ retval = EXIT_SUCCESS;
+ret:
+ /* assuming free(NULL) is safe */
+ free(db_dir_name);
+ free(full_path);
+ free(obj_file_name);
+ free(tmp_file_name);
+ return retval;
+}
+
+static int
+objdb_bl_remove(struct objdb_ctx *ctx, const char *group_name, const char *obj_name)
+{
+ return EXIT_SUCCESS;
+}
+
+static int
+objdb_bl_dir_begin(struct objdb_ctx *ctx, struct objdb_dirhandle **handle)
+{
+ char *db_dir_name;
+ char *tmp_file_name;
+ int len, tmpfd;
+ FILE *tmpfp;
+ db_dir_name = get_objdb_dir(ctx);
+ /* malloc() + strlcpy() + strlcat() = asprintf() */
+ len = asprintf(&tmp_file_name, "%s/tmp.XXXXXX", db_dir_name);
+ if ((tmpfd = mkstemp(tmp_file_name)) == -1) {
+ return EXIT_FAILURE;
+ }
+ if ((tmpfp = fdopen(tmpfd, "w")) == NULL) {
+ unlink(tmp_file_name);
+ close(tmpfd);
+ return EXIT_FAILURE;
+ }
+ *handle = (struct objdb_dirhandle *)malloc(sizeof(struct objdb_dirhandle));
+ (*handle)->id = tmpfd;
+ asprintf(&((*handle)->name), "%s", tmp_file_name);
+ free(db_dir_name);
+ free(tmp_file_name);
+ return EXIT_SUCCESS;
+}
+
+static int
+objdb_bl_dir_insert_object(struct objdb_ctx *ctx, struct objdb_dirhandle *handle, const char *objid, const char *name, enum objdb_type type, mode_t mode)
+{
+ char *entry;
+ int len;
+ /* TODO: check if obj exists! */
+ if (type == T_FILE) {
+ /* FIXME: non-portable code */
+ len = asprintf(&entry, "%c %s %o %s\n", 'F', objid, mode, name);
+ write(handle->id, entry, len);
+ }
+ else if (type == T_DIR) {
+ /* FIXME: non-portable code */
+ len = asprintf(&entry, "%c %s %o %s\n", 'D', objid, mode, name);
+ write(handle->id, entry, len);
+ }
+ return EXIT_SUCCESS;
+}
+
+static int
+objdb_bl_dir_end(struct objdb_ctx *ctx, struct objdb_dirhandle *handle, char **objid)
+{
+ enum objdb_type type = T_DIR;
+ int retval;
+ close(handle->id);
+ retval = objdb_bl_insert_from_file(ctx, type, handle->name, objid);
+ if (retval == EXIT_SUCCESS)
+ unlink(handle->name);
+ free(handle->name);
+ free(handle);
+ return retval;
+}
+
+static int
+objdb_bl_branch_create(struct objdb_ctx *ctx, const char *branch_name)
+{
+ char *db_dir_name = NULL;
+ char *branch_path = NULL;
+ int retval = EXIT_SUCCESS;
+
+ if (branch_name == NULL)
+ return EXIT_FAILURE;
+ /* FIXME: validate branch name & check if already exists */
+ db_dir_name = get_objdb_dir(ctx);
+ asprintf(&branch_path, "%s/branches/%s", db_dir_name, branch_name);
+#ifdef DEBUG
+ printf("creating branch %s\n", branch_path);
+#endif
+ if (mkdir(branch_path, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
+ retval = EXIT_FAILURE;
+ free(db_dir_name);
+ free(branch_path);
+ return retval;
+}
+
+static int
+objdb_bl_branch_if_exists(struct objdb_ctx *ctx, const char *branch_name, int *exist)
+{
+ char *db_dir_name = NULL;
+ char *branch_path = NULL;
+ int retval;
+ struct stat s;
+
+ if (branch_name == NULL)
+ return EXIT_FAILURE;
+ *exist = 0;
+ db_dir_name = get_objdb_dir(ctx);
+ asprintf(&branch_path, "%s/branches/%s", db_dir_name, branch_name);
+ /* TODO: check if path is writable */
+ if (stat(branch_path, &s) == 0) {
+ if (S_ISDIR(s.st_mode)) {
+ *exist = 1;
+ retval = EXIT_SUCCESS;
+ }
+ else {
+ *exist = 0;
+ retval = EXIT_FAILURE;
+ }
+ }
+ else {
+ *exist = 0;
+ retval = EXIT_SUCCESS;
+ }
+ free(db_dir_name);
+ free(branch_path);
+ return retval;
+}
+
+static int
+objdb_bl_branch_set_head(struct objdb_ctx *ctx, const char *branch_name, const char *head_objid)
+{
+ char *db_dir_name = NULL;
+ char *branch_head = NULL;
+ int retval = EXIT_SUCCESS;
+ FILE *head_fp;
+ db_dir_name = get_objdb_dir(ctx);
+ asprintf(&branch_head, "%s/branches/%s/head", db_dir_name, branch_name);
+ if ((head_fp = fopen(branch_head, "w")) == NULL) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+ fprintf(head_fp, "%s", head_objid);
+ fclose(head_fp);
+ret:
+ free(db_dir_name);
+ free(branch_head);
+ return retval;
+}
+
+static int
+objdb_bl_branch_get_head(struct objdb_ctx *ctx, const char *branch_name, char **head_objid)
+{
+ char *db_dir_name = NULL;
+ char *branch_head = NULL;
+ int retval = EXIT_SUCCESS;
+ size_t size = 0;
+ FILE *head_fp;
+ db_dir_name = get_objdb_dir(ctx);
+ asprintf(&branch_head, "%s/branches/%s/head", db_dir_name, branch_name);
+ if ((head_fp = fopen(branch_head, "r")) == NULL) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+ if (getline(head_objid, &size, head_fp) == -1) {
+ retval = EXIT_FAILURE;
+ goto ret;
+ }
+ fclose(head_fp);
+ret:
+ free(db_dir_name);
+ free(branch_head);
+ return retval;
+}
+
+static int
+objdb_bl_branch_ls(struct objdb_ctx *ctx)
+{
+ char *branches_path, *db_dir_name;
+ char *paths[2];
+ FTS *dir;
+ FTSENT *entry;
+
+ db_dir_name = get_objdb_dir(ctx);
+ asprintf(&branches_path, "%s/branches", db_dir_name);
+
+ paths[0] = (char *)branches_path;
+ paths[1] = NULL;
+ if ((dir = fts_open(paths, FTS_NOCHDIR, 0)) == NULL)
+ return EXIT_FAILURE;
+ while ((entry = fts_read(dir)) != NULL) {
+ if (entry->fts_level == FTS_ROOTLEVEL)
+ continue;
+ if (entry->fts_info & FTS_D) {
+ printf("%s\n", entry->fts_name);
+ }
+ else {
+ if (entry->fts_level >= 1)
+ fts_set(dir, entry, FTS_SKIP);
+ }
+ }
+ fts_close(dir);
+ free(db_dir_name);
+ free(branches_path);
+ return EXIT_SUCCESS;
+}
+
diff --git a/objdb.h b/objdb.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef _OBJDB_H_
+#define _OBJDB_H_
+
+#include <sys/types.h>
+
+enum objdb_type {
+ T_FILE,
+ T_DIR,
+ T_COMMIT,
+};
+
+struct objdb_dirhandle {
+ int id;
+ /* optional */
+ char *name;
+};
+
+struct objdb_ctx {
+ char *db_name;
+ char *db_path;
+ char *db_version;
+};
+
+struct objdb_ops {
+ char *name;
+ char *version;
+ /**/
+ int (*open)(struct objdb_ctx **, const char *, const char *);
+ int (*close)(struct objdb_ctx *);
+ int (*init)(struct objdb_ctx *);
+ /* file ops*/
+ int (*insert)(struct objdb_ctx *, enum objdb_type, const u_int8_t *, char **);
+ int (*insert_from_file)(struct objdb_ctx *, enum objdb_type, const char *, char **);
+ /* dir ops */
+ int (*dir_begin)(struct objdb_ctx *, struct objdb_dirhandle **);
+ int (*dir_insert_object)(struct objdb_ctx *, struct objdb_dirhandle *, const char *, const char *, enum objdb_type, mode_t);
+ int (*dir_end)(struct objdb_ctx *, struct objdb_dirhandle *, char **);
+ /* branch ops */
+ int (*branch_create)(struct objdb_ctx *, const char *);
+ int (*branch_if_exists)(struct objdb_ctx *, const char *, int *);
+ int (*branch_set_head)(struct objdb_ctx *, const char *, const char *);
+ int (*branch_get_head)(struct objdb_ctx *, const char *, char **);
+ int (*branch_ls)(struct objdb_ctx *);
+ /**/
+ int (*remove)(struct objdb_ctx *, const char *, const char *);
+ int (*fsck)(struct objdb_ctx *);
+ int (*compress)(struct objdb_ctx *);
+ int (*dedup)(struct objdb_ctx *);
+};
+
+#endif