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:
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;