baseline

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

commit f7ccd9777e8a664d9f769772895ff9f202e311cd
parent 2cccbecd9549222161efe1cd1ad138b8e5a8a218
Author: Mohamed Aslan <maslan@sce.carleton.ca>
Date:   Sun, 29 Jun 2014 01:16:03 -0400

introduce command 'cat'
* command cat, can be used to write a specific file to the stdout, if the file was found.
* if no commit id was specified, command cat will look for the file in the current branch's head.
* command cat, only accepts full paths, relative paths are not supported.

Diffstat:
MMakefile | 2+-
Mbaseline.c | 3+++
Acmd-cat.c | 162+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcmd-log.c | 1+
Mcmd-ls.c | 1+
Mdircache-simple.c | 4++--
Mobjdb-fs.c | 45++++++++++++++++++++++++++++++++++++++++++---
Mobjdb.h | 1+
Mobjects.c | 2+-
Mobjects.h | 4++--
10 files changed, 216 insertions(+), 9 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-ls.c cmd-log.c cmd-version.c +SRCS+= cmd-add.c cmd-branch.c cmd-cat.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 @@ -39,6 +39,9 @@ main(int argc, char **argv) else if (!strcmp(argv[1], "branch")) { cmd_branch(argc - 1, argv + 1); } + else if (!strcmp(argv[1], "cat")) { + cmd_cat(argc - 1, argv + 1); + } else if (!strcmp(argv[1], "checkout")) { cmd_checkout(argc - 1, argv + 1); } diff --git a/cmd-cat.c b/cmd-cat.c @@ -0,0 +1,162 @@ +/* + * 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 <unistd.h> /* getopt(3) */ +#include <err.h> /* errx(3) */ + +#include "cmd.h" +#include "session.h" + +#include "objects.h" + + +static struct dirent* +lookup(struct dir *dir, const char *name) +{ + struct dirent *ent; + + ent = dir->children; + while (ent != NULL) { + if (!strcmp(ent->name, name)) + return ent; + ent = ent->next; + } + return NULL; +} + +int +cmd_cat(int argc, char **argv) +{ + char *comm_id = NULL, *path; + char *id = NULL, *p1, *p2; + char buf[1024]; + int ch, n; + struct session s; + struct commit *comm; + struct dir *dir; + struct dirent *ent; + struct file *file; + + baseline_session_begin(&s, 0); + + /* parse command line options */ + while ((ch = getopt(argc, argv, "c:")) != -1) { + switch(ch) { + case 'c': + comm_id = strdup(optarg); + break; + default: + return EXIT_FAILURE; + } + } + argc -= optind; + argv += optind; + + if (argc == 0) + errx(EXIT_FAILURE, "error, no file is specified."); + if (argc > 1) + errx(EXIT_FAILURE, "error, incorrect number of arguments specified."); + + /* no commit specified, use current branch head */ + if (comm_id == NULL) { + if (s.db_ops->branch_get_head(s.db_ctx, s.branch, &comm_id) == EXIT_FAILURE) + errx(EXIT_FAILURE, "error, branch \'%s\' was not found.", s.branch); + if (comm_id == NULL) + errx(EXIT_FAILURE, "error, branch \'%s\' has zero commits.", s.branch); + } + + path = strdup(argv[0]); + + comm = baseline_commit_new(); + if (s.db_ops->select_commit(s.db_ctx, comm_id, comm) == EXIT_FAILURE) + errx(EXIT_FAILURE, "error, commit \'%s\' was not found.", comm_id); + + dir = baseline_dir_new(); + s.db_ops->select_dir(s.db_ctx, comm->dir, dir); + + p1 = p2 = path; + while (1) { + /* skip leading '/' */ + if (*p1 == '/') { + p1++; + p2++; + continue; + } + if (*p2 == '/') { + /* should be a directory */ + *p2 = '\0'; +#ifdef DEBUG + printf("[DEBUG] looking up directory \'%s\'.\n", p1); +#endif + if ((ent = lookup(dir, p1)) == NULL) + errx(EXIT_FAILURE, "error, no such a file or directory \'%s\'.", argv[0]); + if (!S_ISDIR(ent->mode)) + errx(EXIT_FAILURE, "error, \'%s\' is not a directory.", p1); + free(id); + id = strdup(ent->id); + + baseline_dir_free(dir); + dir = baseline_dir_new(); + s.db_ops->select_dir(s.db_ctx, id, dir); + + p1 = ++p2; + p2 = p1; + } + else if (*p2 == '\0') { + /* found a directory (ending with '/', skipped by above code) */ + if (p1 == p2) + errx(EXIT_FAILURE, "error, to list directories use \'ls\' command instead."); + /* should be a file */ +#ifdef DEBUG + printf("[DEBUG] lookuping up file \'%s\'\n", p1); +#endif + if ((ent = lookup(dir, p1)) == NULL) + errx(EXIT_FAILURE, "error, no such a file or directory \'%s\'.", argv[0]); + if (S_ISDIR(ent->mode)) + errx(EXIT_FAILURE, "error, \'%s\' is a directory, to list directories use \'ls\' command instead.", p1); + + file = baseline_file_new(); + s.db_ops->select_file(s.db_ctx, ent->id, file); +#ifdef DEBUG + printf("[DEBUG] file found with id \'%s\'.\n", file->id); +#endif + break; + } + else { + p2++; + } + } + + /* output file to stdout */ + while ((n = read(file->fd, buf, sizeof(buf))) > 0) { + write(1, buf, n); + } + if (n == -1) + errx(EXIT_FAILURE, "error, failed to read file."); + close(file->fd); + + free(path); + baseline_dir_free(dir); + baseline_file_free(file); + + baseline_session_end(&s); + return EXIT_SUCCESS; +} + diff --git a/cmd-log.c b/cmd-log.c @@ -32,6 +32,7 @@ cmd_log(int argc, char **argv) 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); diff --git a/cmd-ls.c b/cmd-ls.c @@ -33,6 +33,7 @@ cmd_ls(int argc, char **argv) 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); diff --git a/dircache-simple.c b/dircache-simple.c @@ -291,7 +291,7 @@ simple_insert(struct dircache_ctx *dc_ctx, const char *path) return EXIT_FAILURE; /* TODO: baseline_file_new2() */ file = baseline_file_new(); - file->loc = ON_FS; + file->loc = LOC_FS; if ((file->fd = open(entry->fts_path, O_RDONLY, 0)) == -1) return EXIT_FAILURE; dc_ctx->db_ops->insert_file(dc_ctx->db_ctx, file); @@ -323,7 +323,7 @@ simple_insert(struct dircache_ctx *dc_ctx, const char *path) return EXIT_FAILURE; /* TODO: baseline_file_new2() */ file = baseline_file_new(); - file->loc = ON_FS; + file->loc = LOC_FS; if ((file->fd = open(path, O_RDONLY, 0)) == -1) return EXIT_FAILURE; dc_ctx->db_ops->insert_file(dc_ctx->db_ctx, file); 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_file(struct objdb_ctx *, const char *, struct file *); 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 *); @@ -57,6 +58,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_file = objdb_bl_select_file, .select_dir = objdb_bl_select_dir, .select_commit = objdb_bl_select_commit, .remove = objdb_bl_remove, @@ -444,7 +446,7 @@ file_gen_id(struct file *obj) SHA256Init(&hash_ctx); /* TODO: locking */ - if (((struct file *)obj)->loc == ON_FS) { + if (((struct file *)obj)->loc == LOC_FS) { /* save file offset */ offset = lseek(obj->fd, 0, SEEK_CUR); do { @@ -603,7 +605,7 @@ objdb_bl_insert_file(struct objdb_ctx *ctx, struct file *file) retval = EXIT_FAILURE; goto ret; } - if (file->loc == ON_FS) { + if (file->loc == LOC_FS) { /* save file offset */ offset = lseek(file->fd, 0, SEEK_CUR); /* copy file content */ @@ -612,7 +614,7 @@ objdb_bl_insert_file(struct objdb_ctx *ctx, struct file *file) /* restore file offset */ lseek(file->fd, offset, SEEK_SET); } - else if (file->loc == ON_MEM) { + else if (file->loc == LOC_MEM) { write(tmpfd, file->buffer, strlen(file->buffer)); } close(tmpfd); @@ -994,3 +996,40 @@ ret: return retval; } +static int +objdb_bl_select_file(struct objdb_ctx *ctx, const char *objid, struct file *f) +{ + 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, "files", 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; + } + + f->id = strdup(objid); + f->loc = LOC_FS; + f->fd = fd; + + /* the caller should close the file descriptor */ + +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_file)(struct objdb_ctx *, const char *, struct file *); int (*select_dir)(struct objdb_ctx *, const char *, struct dir *); int (*select_commit)(struct objdb_ctx *, const char *, struct commit *); /* branch ops */ diff --git a/objects.c b/objects.c @@ -33,7 +33,7 @@ baseline_file_free(struct file *file) if (file == NULL) return; free(file->id); - if (file->loc == ON_MEM) + if (file->loc == LOC_MEM) free(file->buffer); free(file); } diff --git a/objects.h b/objects.h @@ -66,8 +66,8 @@ struct dir { struct file { char *id; enum { - ON_FS, - ON_MEM + LOC_FS, + LOC_MEM } loc; union { int fd;